@cspeach/cli 1.1.17 → 1.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +3 -4
  2. package/dist/agent/loop.js +143 -25
  3. package/dist/agent/providers/local-provider.js +4 -1
  4. package/dist/agent/repair-partial.js +76 -19
  5. package/dist/agent/tool-dispatch.js +80 -22
  6. package/dist/agent/turn-error-ux.js +77 -26
  7. package/dist/agent/turn-stream.js +25 -3
  8. package/dist/approvals/adt-type.js +27 -0
  9. package/dist/approvals/advisory-prompt.js +48 -0
  10. package/dist/approvals/advisory-render.js +25 -11
  11. package/dist/approvals/approval-prompt.js +74 -24
  12. package/dist/approvals/jwt.js +2 -1
  13. package/dist/approvals/render.js +64 -36
  14. package/dist/approvals/risk-floor.js +53 -2
  15. package/dist/auth/api-key.js +30 -16
  16. package/dist/auth/keychain.js +0 -0
  17. package/dist/auth/me.js +25 -7
  18. package/dist/cli.js +25 -6
  19. package/dist/commands/compact.js +12 -9
  20. package/dist/commands/config-set.js +6 -2
  21. package/dist/commands/config-show.js +7 -5
  22. package/dist/commands/cost.js +41 -34
  23. package/dist/commands/help.js +88 -50
  24. package/dist/commands/login.js +28 -19
  25. package/dist/commands/logout.js +7 -3
  26. package/dist/commands/plan-chain.js +71 -77
  27. package/dist/commands/plan-continue.js +1 -0
  28. package/dist/commands/plan-resume.js +20 -12
  29. package/dist/commands/whoami.js +23 -9
  30. package/dist/config/loader.js +15 -3
  31. package/dist/cost/pricing.js +6 -5
  32. package/dist/doctor/checks/forge-rules.js +13 -3
  33. package/dist/doctor/checks/keychain.js +6 -4
  34. package/dist/doctor/run.js +101 -20
  35. package/dist/index.js +5 -2
  36. package/dist/lib/graceful-exit.js +56 -0
  37. package/dist/lib/piped-prompt.js +65 -0
  38. package/dist/lock-contention.js +2 -2
  39. package/dist/one-shot.js +135 -40
  40. package/dist/projects/answer-blockers.js +6 -1
  41. package/dist/projects/handover-md.js +15 -14
  42. package/dist/projects/plan-run.js +97 -54
  43. package/dist/projects/promote-command.js +11 -2
  44. package/dist/projects/save-command.js +3 -2
  45. package/dist/projects/status.js +2 -1
  46. package/dist/projects/yes-no.js +12 -0
  47. package/dist/renderer/abap-inline.js +18 -14
  48. package/dist/renderer/answer-trim.js +69 -0
  49. package/dist/renderer/banners.js +8 -8
  50. package/dist/renderer/brand-settled.js +19 -0
  51. package/dist/renderer/change-card.js +156 -0
  52. package/dist/renderer/checklist-format.js +130 -0
  53. package/dist/renderer/color-mode.js +45 -0
  54. package/dist/renderer/error-detail.js +104 -0
  55. package/dist/renderer/fence-state.js +46 -0
  56. package/dist/renderer/footer-line.js +96 -0
  57. package/dist/renderer/glyphs.js +25 -0
  58. package/dist/renderer/goodbye.js +38 -0
  59. package/dist/renderer/legacy-palette.js +41 -0
  60. package/dist/renderer/look.js +30 -0
  61. package/dist/renderer/markdown.js +299 -92
  62. package/dist/renderer/notice-log.js +72 -0
  63. package/dist/renderer/notice-shape.js +48 -0
  64. package/dist/renderer/notices.js +40 -8
  65. package/dist/renderer/panel-rows.js +16 -0
  66. package/dist/renderer/pipeline.js +66 -0
  67. package/dist/renderer/progress-chatter.js +23 -17
  68. package/dist/renderer/rendering-mode.js +34 -0
  69. package/dist/renderer/routing-line.js +14 -0
  70. package/dist/renderer/sanitize.js +12 -0
  71. package/dist/renderer/severity.js +2 -7
  72. package/dist/renderer/startup-lines.js +152 -0
  73. package/dist/renderer/status-footer.js +52 -34
  74. package/dist/renderer/steering-echo.js +41 -0
  75. package/dist/renderer/syntax.js +31 -12
  76. package/dist/renderer/tables.js +5 -1
  77. package/dist/renderer/theme.js +44 -0
  78. package/dist/renderer/thinking-heartbeat.js +33 -2
  79. package/dist/renderer/todo-block.js +10 -49
  80. package/dist/renderer/tool-labels.js +238 -0
  81. package/dist/renderer/tool-widget.js +245 -51
  82. package/dist/renderer/trace.js +42 -0
  83. package/dist/renderer/transcript-flow.js +132 -0
  84. package/dist/renderer/tty.js +34 -1
  85. package/dist/renderer/ui-width.js +42 -0
  86. package/dist/renderer/verify-chain.js +4 -2
  87. package/dist/renderer/widget-fallback.js +58 -65
  88. package/dist/repl/bracketed-paste.js +7 -1
  89. package/dist/repl/builtin-commands.js +19 -8
  90. package/dist/repl/credential-handover-gate.js +28 -0
  91. package/dist/repl/early-line-buffer.js +5 -2
  92. package/dist/repl/file-picker.js +54 -12
  93. package/dist/repl/ink-stdin-guard.js +66 -0
  94. package/dist/repl/inquirer-guard.js +59 -16
  95. package/dist/repl/inquirer-theme.js +27 -33
  96. package/dist/repl/paste-marker.js +19 -0
  97. package/dist/repl/plan-turn-end.js +20 -0
  98. package/dist/repl/post-turn-status.js +36 -11
  99. package/dist/repl/reroute-turn.js +21 -0
  100. package/dist/repl/reset-tty-stdin.js +44 -0
  101. package/dist/repl/restore-guard.js +22 -0
  102. package/dist/repl/resume-standalone.js +63 -0
  103. package/dist/repl/rule8-detector.js +11 -3
  104. package/dist/repl/safety-confirm.js +161 -93
  105. package/dist/repl/session-spend-line.js +8 -4
  106. package/dist/repl/themed-prompts.js +19 -0
  107. package/dist/repl/ui-look-command.js +75 -0
  108. package/dist/repl/update-method-preview-hook.js +48 -5
  109. package/dist/repl.js +795 -332
  110. package/dist/rewind/cli.js +3 -2
  111. package/dist/rewind/format.js +14 -9
  112. package/dist/rewind/restore.js +11 -0
  113. package/dist/router/classifier.js +47 -4
  114. package/dist/router/intent-extractor.js +24 -8
  115. package/dist/router/piped-routing.js +23 -0
  116. package/dist/router/resume-routing.js +16 -0
  117. package/dist/router/routing-failure.js +57 -0
  118. package/dist/sap/first-run-choice.js +1 -1
  119. package/dist/sap/onboarding.js +3 -2
  120. package/dist/sap/standalone-onboarding.js +1 -1
  121. package/dist/sap/system-info.js +4 -2
  122. package/dist/sap/unreachable.js +28 -0
  123. package/dist/sap-errors/clean-error-text.js +182 -0
  124. package/dist/session/interrupt-reason.js +39 -0
  125. package/dist/session/recap.js +36 -24
  126. package/dist/session/resume.js +104 -36
  127. package/dist/session/store.js +69 -27
  128. package/dist/session/user-prompt.js +21 -0
  129. package/dist/skill-catalog.js +7 -0
  130. package/dist/skills/bundled-skills.js +76 -69
  131. package/dist/skills/signing-public-key.js +1 -1
  132. package/dist/standards/standards-init.js +1 -1
  133. package/dist/test-helpers/answer-any.js +46 -0
  134. package/dist/tools/approval.js +27 -11
  135. package/dist/tools/ask-question.js +47 -5
  136. package/dist/tools/filesystem/file-write.js +19 -2
  137. package/dist/tools/fiori/fe-extend.js +16 -1
  138. package/dist/tools/fiori/fe-scaffold.js +105 -3
  139. package/dist/tools/fiori/html-escapes.js +28 -0
  140. package/dist/tools/fiori/metadata/audit.js +605 -0
  141. package/dist/tools/fiori/metadata/references.js +104 -0
  142. package/dist/tools/fiori/metadata/types.js +8 -0
  143. package/dist/tools/fiori/preview/app-guard.js +215 -0
  144. package/dist/tools/fiori/preview/env.js +193 -0
  145. package/dist/tools/fiori/preview/readiness.js +127 -0
  146. package/dist/tools/fiori/preview/registry.js +412 -0
  147. package/dist/tools/fiori/preview/start.js +469 -0
  148. package/dist/tools/fiori/samples/loader.js +20 -5
  149. package/dist/tools/fiori/scaffold.js +18 -6
  150. package/dist/tools/fiori/smoke/app-driver-page.js +372 -0
  151. package/dist/tools/fiori/smoke/app-driver.js +100 -0
  152. package/dist/tools/fiori/smoke/assertions.js +161 -24
  153. package/dist/tools/fiori/smoke/audit-columns.js +21 -0
  154. package/dist/tools/fiori/smoke/batch.js +273 -0
  155. package/dist/tools/fiori/smoke/draft-safety.js +241 -0
  156. package/dist/tools/fiori/smoke/draft-smoke.js +473 -0
  157. package/dist/tools/fiori/smoke/driver.js +171 -4
  158. package/dist/tools/fiori/smoke/evidence.js +35 -0
  159. package/dist/tools/fiori/smoke/labels-codes.js +207 -0
  160. package/dist/tools/fiori/smoke/nav-error.js +26 -0
  161. package/dist/tools/fiori/smoke/run-smoke.js +155 -12
  162. package/dist/tools/fiori/tools.js +651 -7
  163. package/dist/tools/fiori/ui5-version.js +16 -0
  164. package/dist/tools/index.js +8 -0
  165. package/dist/tools/local-build.js +6 -0
  166. package/dist/tools/sap-read.js +16 -1
  167. package/dist/tools/sap-write.js +122 -10
  168. package/dist/tools/shell/shell_exec.js +30 -12
  169. package/dist/tools/syntax-state.js +67 -0
  170. package/dist/tools/todo.js +23 -18
  171. package/dist/tools/transport-fit.js +462 -0
  172. package/dist/tools/transport-resolution.js +3 -1
  173. package/dist/tools/transport.js +33 -7
  174. package/dist/tools/verify.js +184 -8
  175. package/dist/ui/app.js +284 -146
  176. package/dist/ui/approval-modal.js +41 -15
  177. package/dist/ui/ask-answer-text.js +17 -0
  178. package/dist/ui/body.js +25 -6
  179. package/dist/ui/card-slot.js +98 -0
  180. package/dist/ui/checklist.js +9 -0
  181. package/dist/ui/coaching-picker-classic.js +8 -5
  182. package/dist/ui/command-palette.js +6 -4
  183. package/dist/ui/confirm-request.js +18 -0
  184. package/dist/ui/context-grid.js +2 -1
  185. package/dist/ui/file-palette.js +6 -4
  186. package/dist/ui/files-card-emitter.js +19 -0
  187. package/dist/ui/footer-line.js +16 -0
  188. package/dist/ui/footer.js +105 -104
  189. package/dist/ui/input-wrap.js +122 -0
  190. package/dist/ui/interrupt.js +103 -0
  191. package/dist/ui/key-burst.js +117 -0
  192. package/dist/ui/line-resolution.js +4 -3
  193. package/dist/ui/live-area.js +71 -0
  194. package/dist/ui/login-banner.js +54 -43
  195. package/dist/ui/rewind-panel.js +76 -24
  196. package/dist/ui/role-props.js +47 -0
  197. package/dist/ui/sap-state-store.js +1 -0
  198. package/dist/ui/session-timeline.js +49 -3
  199. package/dist/ui/skill-picker.js +5 -47
  200. package/dist/ui/text-input.js +128 -24
  201. package/dist/ui/todo-emitter.js +19 -0
  202. package/dist/ui/turn-status-emitter.js +28 -2
  203. package/dist/ui/turn-status.js +16 -19
  204. package/dist/ui/use-card-request.js +44 -0
  205. package/dist/ui/widgets/ask-card.js +521 -0
  206. package/dist/ui/widgets/bar-chart.js +11 -5
  207. package/dist/ui/widgets/coaching-picker.js +50 -55
  208. package/dist/ui/widgets/confirm-card.js +193 -0
  209. package/dist/ui/widgets/dep-graph.js +3 -2
  210. package/dist/ui/widgets/diff-viewer.js +6 -5
  211. package/dist/ui/widgets/files-card.js +97 -0
  212. package/dist/ui/widgets/question-card.js +2 -1
  213. package/dist/ui/widgets/reclaim-raw-stdin.js +24 -0
  214. package/dist/ui/widgets/shortcuts-sheet.js +25 -0
  215. package/dist/ui/widgets/stack-frames.js +2 -1
  216. package/dist/upgrade-check.js +39 -8
  217. package/package.json +11 -2
  218. package/test-harness/fe-draft/app.ts +188 -0
  219. package/test-harness/fe-draft/fe-draft.e2e.test.ts +461 -0
  220. package/test-harness/fe-draft/manifest-enhancer.unit.test.ts +51 -0
  221. package/test-harness/fe-draft/routes.ts +224 -0
  222. package/test-harness/fe-draft/routes.unit.test.ts +45 -0
  223. package/test-harness/fe-draft/server.ts +225 -0
  224. package/test-harness/fe-draft/variants.ts +196 -0
  225. package/vitest.smoke-e2e.config.ts +20 -0
@@ -1,6 +1,6 @@
1
1
  // GENERATED FILE — bundled skills for local mode.
2
2
  // Source: https://manifest.cspeach.dev/v1.json
3
- // Generated: 2026-09-24T16:31:52.103Z
3
+ // Generated: 2026-09-28T17:36:32.510Z
4
4
  // Manifest signature verified at build time.
5
5
  export const BUNDLED_SKILLS = {
6
6
  "abap-atc-fix": {
@@ -8,265 +8,272 @@ export const BUNDLED_SKILLS = {
8
8
  "body": "---\r\nname: abap-atc-fix\r\ndescription: >\r\n Run ABAP Test Cockpit (ATC) checks against a specified object and auto-fix\r\n findings where safe. Categorizes findings by severity and fix type, proposes\r\n fixes with explanation, applies fixes only on user confirmation, then re-runs\r\n ATC to verify remediation. Requires MCP connection for actual execution.\r\nphase: VERIFY\r\nrequires_mcp: required\r\nforge_rules: [1, 6, 7, 10]\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.3.0\"\r\nwidget: diff-viewer\r\n---\r\n\r\n# abap-atc-fix\r\n\r\n## Purpose\r\n\r\nRun ATC checks on an ABAP object, parse the findings, categorize them into\r\nauto-fixable versus manual-fix-required, propose solutions for each, apply\r\nfixes with explicit user confirmation, and re-run ATC to confirm resolution.\r\n\r\nThe skill never applies fixes silently. Every fix is shown to the user before\r\napplication. Fixes that could affect runtime behavior are labeled \"Review\r\nRequired\" and are not applied without separate confirmation.\r\n\r\n## When to Use\r\n\r\n- After generating or modifying code and before releasing to transport\r\n- As part of a pre-flight check (`/abap-preflight` calls this internally)\r\n- When ATC findings are blocking a transport release\r\n- When a code review (`/abap-review`) flagged ATC-detectable issues\r\n\r\nThis skill requires an active MCP connection to SAP (uses `sap_atc_run` and\r\n`sap_set_source`). Without MCP, use `/abap-review` for a manual code analysis.\r\n\r\nWhen NOT to use: manual quality review without auto-fix — `/abap-review`; worklist-driven upgrade findings — `/abap-upgrade-fix`.\r\n\r\n## Inputs Expected\r\n\r\nRequired (MCP mode):\r\n- Object name and type (e.g. class ZCL_ORDER_APPROVAL, program ZR_PO_REPORT)\r\n\r\nOptional:\r\n- ATC check variant (default: the system default variant)\r\n- Whether to automatically approve fixes for Low/Info severity findings\r\n- Whether to include SAP extended program check (SLIN) findings\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Run ATC\r\n\r\nCall `sap_atc_run` with the object name. Capture the raw findings.\r\nIf the tool is unavailable, report the error clearly and stop.\r\n\r\n### Step 2 — Parse and Display All Findings\r\n\r\nPresent every finding in a structured table. For each finding:\r\n- Finding ID (for tracking)\r\n- Check name (e.g. FUNCTIONAL_DB, PERF_DB_SEARCH, STABI_IMPLICIT_ENHMT)\r\n- Priority: 1 (error) / 2 (warning) / 3 (info)\r\n- Location: method / line\r\n- Message text\r\n- Fix type: Auto-Fix Safe / Auto-Fix Review Required / Manual Fix / Not Fixable\r\n\r\n**Fix type definitions:**\r\n- **Auto-Fix Safe:** Syntactic change only, no behavior change possible (e.g. removing\r\n obsolete INTO CORRESPONDING FIELDS, adding explicit field list to SELECT)\r\n- **Auto-Fix Review Required:** Fix is straightforward but could affect behavior\r\n (e.g. replacing FM with a class, changing exception handling)\r\n- **Manual Fix:** Requires developer decision and context (e.g. authorization checks,\r\n architectural changes)\r\n- **Not Fixable Automatically:** Requires re-design (e.g. \"SELECT inside LOOP\" where\r\n moving the SELECT out changes the query logic)\r\n\r\n### Step 3 — Propose Fixes\r\n\r\nFor Auto-Fix Safe and Auto-Fix Review Required findings, show:\r\n- Current code snippet\r\n- Proposed replacement code\r\n- Explanation of why this fix addresses the finding\r\n\r\nGroup by category. Ask for confirmation before applying any fix.\r\n\r\n### Step 4 — Apply Fixes\r\n\r\nFor approved fixes:\r\n1. Read current source: `sap_get_source`\r\n2. Apply the change in memory\r\n3. Write updated source: `sap_set_source`\r\n4. Run syntax check: `sap_syntax_check`\r\n5. If syntax check passes: confirm fix applied\r\n6. If syntax check fails: revert the change, report failure\r\n\r\nDo NOT apply multiple fixes simultaneously — apply one at a time and verify\r\nbetween each.\r\n\r\n### Step 5 — Re-Run ATC\r\n\r\nAfter all approved fixes are applied, re-run ATC on the same object.\r\nCompare new findings to original. Report:\r\n- Fixed: {count}\r\n- Remaining: {count} (with list)\r\n- New findings introduced: {count} (if any)\r\n\r\n### Step 6 — Summary\r\n\r\nState final ATC status. If critical/high findings remain, recommend whether\r\nit is safe to release the transport or not (but leave the decision to the user).\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## ATC Run — {object_name}\r\n\r\n**Object:** {object_name} ({object_type})\r\n**Check Variant:** {variant_name}\r\n**Run Time:** {timestamp}\r\n\r\n---\r\n\r\n## Findings\r\n\r\n| ID | Check | Priority | Location | Message | Fix Type |\r\n|----|-------|----------|----------|---------|----------|\r\n| 1 | {check_name} | {1/2/3} | {location} | {message} | Auto-Fix Safe |\r\n| 2 | {check_name} | {1/2/3} | {location} | {message} | Manual Fix |\r\n\r\n**Total:** {count} findings ({prio1} priority-1, {prio2} priority-2, {prio3} priority-3)\r\n\r\n---\r\n\r\n## Proposed Fixes\r\n\r\n### Finding 1 — {check_name} (Auto-Fix Safe)\r\n\r\n**Current:**\r\n```abap\r\n{current_code}\r\n```\r\n\r\n**Proposed Fix:**\r\n```abap\r\n{fixed_code}\r\n```\r\n\r\n**Explanation:** {why_this_fixes_the_finding}\r\n\r\nApply this fix? (yes / no / skip all auto-fix-safe)\r\n\r\n---\r\n\r\n## Fix Application Log\r\n\r\n| Finding | Fix Type | Status | Syntax Check |\r\n|---------|----------|--------|--------------|\r\n| 1 | Auto-Fix Safe | Applied | Passed |\r\n| 2 | Manual Fix | Skipped — user action required | N/A |\r\n\r\n---\r\n\r\n## ATC Re-Run Results\r\n\r\n**Before:** {before_count} findings\r\n**After:** {after_count} findings\r\n**Fixed:** {fixed_count}\r\n\r\n### Remaining Findings\r\n| ID | Check | Priority | Location | Message |\r\n|----|-------|----------|----------|---------|\r\n| {remaining} | ... | ... | ... | ... |\r\n\r\n---\r\n\r\n## Recommendation\r\n\r\n{transport_safe / hold_for_manual_fixes} — {brief_justification}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method logic — use `sap_update_method` per method instead (Rule 7a)\r\n- NEVER apply fixes without showing the before/after and receiving confirmation\r\n- NEVER apply multiple fixes in a single sap_set_source call without testing each\r\n- NEVER proceed to next fix if syntax check failed for previous fix\r\n- If ATC re-run introduces NEW findings not in the original run, flag them explicitly\r\n- Do not claim ATC is \"clean\" if any priority-1 or priority-2 findings remain\r\n- ATC findings are system-specific — do not claim a finding will appear if not\r\n confirmed by actual ATC run\r\n- Do not apply performance-related fixes (e.g. restructuring SELECTs) as\r\n \"Auto-Fix Safe\" — always mark as \"Review Required\"\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-atc-fix\r\n\r\nRun ATC on class ZCL_INVOICE_PROCESSOR and fix what you can.\r\nAuto-approve fixes for info-level findings.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## ATC Run — ZCL_INVOICE_PROCESSOR\r\n\r\n**Object:** ZCL_INVOICE_PROCESSOR (Global Class)\r\n**Check Variant:** DEFAULT\r\n**Run Time:** 2026-03-29 14:22:11\r\n\r\n---\r\n\r\n## Findings\r\n\r\n| ID | Check | Priority | Location | Message | Fix Type |\r\n|----|----------------------------|----------|---------------------|-------------------------------------------------------|-----------------------|\r\n| 1 | STABI_OBSOLETE_STATEMENT | 2 | METHOD process_line | MOVE statement is obsolete — use assignment operator | Auto-Fix Safe |\r\n| 2 | PERF_DB_SEARCH | 2 | METHOD fetch_data | SELECT in LOOP — consider FOR ALL ENTRIES or JOIN | Not Fixable Auto |\r\n| 3 | STABI_OBSOLETE_STATEMENT | 3 | METHOD build_output | CONCATENATE is obsolete — use string template | Auto-Fix Safe |\r\n| 4 | SEC_AUTH_CHECK | 1 | METHOD delete_inv | Missing AUTHORITY-CHECK before modifying table | Manual Fix |\r\n| 5 | STABI_OBSOLETE_STATEMENT | 3 | METHOD build_output | COMPUTE is obsolete — use direct assignment | Auto-Fix Safe |\r\n\r\n**Total:** 5 findings (1 priority-1, 2 priority-2, 2 priority-3)\r\n\r\n---\r\n\r\n## Proposed Fixes\r\n\r\n### Finding 1 — STABI_OBSOLETE_STATEMENT (Auto-Fix Safe)\r\n\r\n**Current:**\r\n```abap\r\nMOVE lv_amount TO lv_total.\r\n```\r\n\r\n**Proposed Fix:**\r\n```abap\r\nlv_total = lv_amount.\r\n```\r\n\r\n**Explanation:** MOVE statement is marked obsolete since ABAP 7.40. Direct\r\nassignment is equivalent and preferred by Clean ABAP guidelines.\r\n\r\n### Finding 3 — STABI_OBSOLETE_STATEMENT (Auto-Fix Safe)\r\n\r\n**Current:**\r\n```abap\r\nCONCATENATE lv_prefix lv_name INTO lv_full_name SEPARATED BY '-'.\r\n```\r\n\r\n**Proposed Fix:**\r\n```abap\r\nlv_full_name = |{ lv_prefix }-{ lv_name }|.\r\n```\r\n\r\n**Explanation:** String template is the modern equivalent. Result is identical.\r\n\r\n### Finding 5 — STABI_OBSOLETE_STATEMENT (Auto-Fix Safe)\r\n\r\n**Current:**\r\n```abap\r\nCOMPUTE lv_count = lv_count + 1.\r\n```\r\n\r\n**Proposed Fix:**\r\n```abap\r\nlv_count = lv_count + 1.\r\n```\r\n\r\nAuto-applying findings 1, 3, 5 (info/warning level, safe).\r\nFinding 4 (security) requires manual fix — see below.\r\nFinding 2 (performance SELECT in LOOP) requires architectural decision.\r\n\r\n---\r\n\r\n## Fix Application Log\r\n\r\n| Finding | Fix Type | Status | Syntax Check |\r\n|---------|---------------|---------|--------------|\r\n| 1 | Auto-Fix Safe | Applied | Passed |\r\n| 3 | Auto-Fix Safe | Applied | Passed |\r\n| 5 | Auto-Fix Safe | Applied | Passed |\r\n| 4 | Manual Fix | Skipped | N/A |\r\n| 2 | Not Fixable | Skipped | N/A |\r\n\r\n---\r\n\r\n## ATC Re-Run Results\r\n\r\n**Before:** 5 findings\r\n**After:** 2 findings\r\n**Fixed:** 3\r\n\r\n### Remaining Findings\r\n| ID | Check | Priority | Location | Message |\r\n|----|----------------|----------|-------------------|--------------------------------------------------|\r\n| 4 | SEC_AUTH_CHECK | 1 | METHOD delete_inv | Missing AUTHORITY-CHECK before modifying table |\r\n| 2 | PERF_DB_SEARCH | 2 | METHOD fetch_data | SELECT in LOOP — consider FOR ALL ENTRIES or JOIN|\r\n\r\n---\r\n\r\n## Recommendation\r\n\r\n**Hold for manual fixes.** Priority-1 security finding (SEC_AUTH_CHECK) in\r\ndelete_inv must be addressed before transport release. Add AUTHORITY-CHECK\r\nOBJECT 'F_BKPF_BES' (or the relevant authorization object for your module)\r\nbefore the DELETE statement. Run /abap-review for detailed guidance on that method.\r\n```\r\n",
9
9
  "sha256": "7158f8dc16760140a872639f620dd3e099ba4ca7e3aff3ec2c9160bbf404d129",
10
10
  "signature": "",
11
- "signedAt": "2026-09-17T20:19:03.815Z"
11
+ "signedAt": "2026-09-28T14:36:53.246Z"
12
+ },
13
+ "abap-cap": {
14
+ "name": "abap-cap",
15
+ "body": "---\nname: abap-cap\ndescription: >\n Build a CAP (SAP Cloud Application Programming Model, Node.js) app on the\n developer's laptop from a spec — CDS domain model, service, handlers, Fiori\n Elements annotations and app — run it locally against SQLite,\n and prove it with the same Fiori checks used for RAP (render smoke + draft\n smoke). Side-by-side extensions, the clean-core home for logic that does not\n belong in the ABAP core. Local files only; never deploys unless asked.\nphase: BUILD\nrequires_mcp: false\nforge_rules: [1, 2, 5, 8, 10]\nversion: \"1.0\"\n---\n\n# abap-cap\n\n## Purpose\n\nTurn a requirement into a **running CAP app on the laptop**: domain model, OData V4 service, business\nlogic, a Fiori Elements list report + object page with draft — and **prove it runs** with the render\nsmoke and the draft smoke against the live local server. Everything is local files in the developer's\nproject folder; nothing touches an SAP system and nothing is deployed.\n\nCAP is where clean-core side-by-side extensions live (SAP BTP). Use `/abap-clean-core` when the question\nis *whether* something belongs in CAP or in RAP; use this skill once the answer is CAP.\n\n## When to Use\n\n- A new app or extension that should live **side by side** (BTP / CAP), not in the ABAP core.\n- The developer is in a CAP project (`db/`, `srv/`, `.cds` files, `@sap/cds` in package.json) or wants\n a new one.\n\n**Not this skill:**\n- On-stack RAP in the ABAP system → `/abap-rap`.\n- A UI on an already published OData service → `/abap-fiori-build`.\n- Deploying to BTP (HANA Cloud, XSUAA, approuter, `cf deploy`) → not covered yet; say so plainly.\n- Consuming an S/4HANA service from CAP (`cds import`) → not covered yet; say so plainly.\n\n## Inputs Expected\n\nThe business requirement (a spec as Word/PDF/Excel, or a description). From it you need: the entities\nand their fields, which fields are coded (status, type, category), relations between entities, which\nentity is edited by users (draft root), who may do what, and what the list and the object page show.\n\n## Required Behavior\n\n### Step 0 — Check the tools and the project\n\n1. Run `cds --version` (shell). If `cds` is missing, stop and tell the developer to install the CAP dev\n kit once: `npm i -g @sap/cds-dk` — do not try to work around it.\n2. Read the project context. If this is a CAP project, read `package.json`, `db/*.cds`, `srv/*.cds` and\n any `app/*` before proposing anything. If it is an empty folder, the plan starts with\n `cds init <name> --nodejs` (shell) followed by `npm install`.\n\n### Step 1 — Ask what is missing (Forge Rule 1)\n\nNever generate blindly. Ask, in one short numbered list, only what the spec leaves open — typically:\nthe key of each entity (default: `cuid`), coded fields and their values, mandatory fields, relations and\ntheir cardinality, the draft root, roles (default in development: one `admin` role), and the list\ncolumns / filters. Offer sensible defaults so the developer can answer \"defaults are fine\".\n\n### Step 2 — Show the plan\n\nShow the files you will create or change, one line each, before writing any:\n\n```\ndb/schema.cds domain model (namespace, entities, code lists)\ndb/data/<ns>-<Entity>.csv a few rows of sample data per entity and code list\nsrv/<name>-service.cds the service: projections, @odata.draft.enabled, @requires\nsrv/<name>-service.js handlers: validations, determinations, actions\napp/<app>/annotations.cds UI: LineItem, SelectionFields, HeaderInfo, Facets, FieldGroups\napp/<app>/webapp/… the Fiori Elements app (fiori_scaffold_fe)\npackage.json devDependency cds-plugin-ui5 (serves the app); dev login refuses unknown users\n```\n\nLocal files are not SAP objects: they do not need the batch card, but the plan is still shown first so\nthe developer can correct it.\n\n### Step 3 — Build, with these rules (they are what makes the checks pass)\n\n**Domain model (`db/schema.cds`)**\n- `using { cuid, managed, sap.common.CodeList } from '@sap/cds/common';` — keys via `cuid`, audit fields\n via `managed`.\n- **Every exposed element has a label**: `@title: 'Supplier Name'`. An element without a label renders\n as a technical name (or an empty header on older UI5) and fails the `labels` check.\n- **Every coded field is an association to a code list**, never a bare code:\n `entity Statuses : CodeList { key code : String(1); }` and `status : Association to Statuses;`.\n Code lists carry translatable `name` / `descr` and get a value help automatically.\n- `@mandatory` on mandatory fields; `@assert.range` / `@assert.format` where the spec gives limits.\n\n**Service (`srv/<name>-service.cds`)**\n- Expose projections, not the db entities directly.\n- `@odata.draft.enabled` on the root entity users edit; compositions for its children.\n- `@requires: 'admin'` (or the roles from Step 1) on the service. In development CAP's mocked login\n knows `alice` (admin) — note that for the draft smoke.\n- **Refuse unknown logins in development.** CAP's mocked login accepts ANY other user name with no\n roles (`\"*\": true` by default): someone who types their real SAP user gets in, every request fails\n with \"lacking required roles\", and the app shows \"could not be loaded\". Add to `package.json`:\n `\"cds\": { \"requires\": { \"auth\": { \"users\": { \"*\": false } } } }` — an unknown name then gets the\n login prompt again, and `alice` keeps working. (Merge with an existing `cds` section; if you define\n your own users, keep `\"*\": false` next to them.)\n- **Text instead of codes** on every coded field:\n `status @Common.Text: status.name @Common.TextArrangement: #TextOnly;` — a list that shows `O`\n instead of `Open` fails the `no-raw-codes` check.\n\n**Handlers (`srv/<name>-service.js`)** — read `package.json` first. Projects from `cds init` (dev kit\n10+) have `\"type\": \"module\"`: the handler MUST be an ES module, or the server refuses to start\n(\"require is not defined in ES module scope\"):\n```js\nimport cds from '@sap/cds';\n\nexport default class <Name>Service extends cds.ApplicationService {\n init() {\n const { <Entity> } = this.entities;\n this.before(['CREATE', 'UPDATE'], <Entity>, (req) => {\n if (req.data.name !== undefined && !String(req.data.name).trim()) req.error(400, 'Enter a name', 'name');\n });\n return super.init();\n }\n}\n```\nOnly an older project without `\"type\": \"module\"` uses CommonJS\n(`const cds = require('@sap/cds'); module.exports = class … `). Match what the project already uses.\nValidation messages are sentences a user understands, targeted at the field.\n\n**UI (`app/<app>/annotations.cds`)** — `annotate <Name>Service.<Entity> with @(UI.LineItem: [...],\nUI.SelectionFields: [...], UI.HeaderInfo: {...}, UI.Facets: [...], UI.FieldGroup #General: {...});`\nEvery LineItem entry either points at a labelled element or carries its own `Label`.\n\n**App** — `fiori_scaffold_fe` with template `lrop`, OData **V4**, **`backend: \"cap\"`**, service URL\n`http://localhost:4004` and path **`/odata/v4/<service>/`** (absolute), `basePath: app/<app>`, the\npinned UI5 version (the tool's default). `backend: \"cap\"` is required: without it the app's dev-server\nconfig keeps a middleware that loads a second copy of CAP into the server, and every draft save\ncrashes it. Do not copy the launchpad shell from SAP's CAP samples: it uses a deprecated bootstrap that newer UI5\nmarks FUTURE FATAL.\n\n**Serving the app — `cds-plugin-ui5`** (SAP's standard way to serve a UI5 app from a CAP server):\n1. In the CAP project root: `npm install --save-dev cds-plugin-ui5`.\n2. In the app folder: `npm install` (the app's own UI5 tooling, from the scaffold's package.json).\n\nThe CAP server then mounts the app at **`/<appId>`** — the app ID given to the scaffold (e.g.\n`/zsupp.suppliers`), not the folder name — and serves `/resources` + `/test-resources` from the UI5\nCDN at the version the manifest pins. The launchpad entry is\n`http://localhost:4004/<appId>/test/flpSandbox.html#<tile>` where `<tile>` is the key under\n`applications` in `app/<app>/webapp/test/flpSandbox.html` (e.g. `zsuppsuppliers-tile`). Read both\nfrom the files; never guess them. Check the entry answers before running a smoke.\n\nSample data: a few realistic rows per entity and every code value, with texts, in `db/data/`.\n\n### Step 4 — Compile, run, prove (Forge Rules 5 and 10)\n\n1. `cds compile srv --to edmx` (shell). It must succeed; fix and re-run until it does.\n2. Start the server in the background: `background_run` with **`npm start`** in the project root (the\n project's own `cds-serve`) — the proven way. Never `npx -p @sap/cds-dk cds serve`: with\n `cds-plugin-ui5` it loads a second copy of CAP and draft requests crash (\"Maximum call stack size\n exceeded\"). Wait until it\n listens on `http://localhost:4004` (monitor its events).\n3. **Render smoke** — `fiori_render_smoke` on the launchpad entry (`http://localhost:4004/<appId>/test/flpSandbox.html#<tile>`)\n with `capUser: \"alice\"` (or the role user from Step 1) — a service with `@requires` answers the\n browser with a login challenge otherwise, and nothing loads. Boot, OData, rows, labels and\n no-raw-codes must be green.\n4. **Draft smoke** — `fiori_draft_smoke` with `backend: \"cap\"`, `capUser: \"alice\"` (or the role user\n from Step 1), `servicePath: \"/odata/v4/<service>/\"`, the same launchpad entry as the app URL, a writable\n text field as `markerField`, and an approval naming the service. It creates, edits, discards and\n deletes one marked row in the **local** SQLite database. The tool refuses unless the server proves it\n is the CAP development server — never point it at anything else.\n5. A red check is a defect in what you built: fix the source, re-run the check. Never report a red check\n as done, and never call something verified that you did not run.\n\n### Step 5 — Report\n\nTL;DR first (the house format), then: files created, how to run it (`npm start`, the app URL), the\ncheck results with the evidence paths, and what is **not** done yet (BTP deploy, S/4 integration,\nproduction auth) when relevant.\n\n**The login goes right next to the app URL, every time** — the browser asks for a user name and\npassword, and a developer's first guess is their SAP login:\n```\nhttp://localhost:4004/<appId>/test/flpSandbox.html#<tile>\nLog in as: alice (no password — a local test user, not your SAP login)\n```\n\n## Guardrails\n\n- **Never deploy.** No `cds deploy --to hana`, `cf deploy`, `cf push`, `mbt build` or `npm run deploy`\n unless the developer asks for it in this conversation — those leave the laptop (and a deploy command\n still raises the batch card).\n- Local SQLite only; no production credentials, no `.env` secrets written into files.\n- No invented CAP syntax: when unsure, compile (`cds compile`) and read the error rather than guess.\n- Do not edit files under `node_modules/` or generated `gen/` folders.\n- Keep the developer's existing model style (naming, namespace) when extending a project.\n\n## Example Prompt\n\n\"Build a CAP app to manage suppliers and their quality certificates. Certificates have a type (ISO, CE,\nother), a status (valid, expiring, expired) and an expiry date. Buyers edit suppliers with draft.\"\n\n## Example Output Outline\n\n```\n## TL;DR\nHeadline: Supplier certificates app runs locally — all checks green.\n…\n## Files\ndb/schema.cds · db/data/… · srv/supplier-service.cds/.js · app/suppliers/…\n## Run it\nnpm start → http://localhost:4004/zsupp.suppliers/test/flpSandbox.html#zsuppsuppliers-tile\nLog in as: alice (no password — a local test user, not your SAP login)\n## Checks (evidence in ~/.cspeach/smoke/…)\nrender smoke: boot · odata · rows · labels · no-raw-codes — green\ndraft smoke: setup · rows · value-help · draft-save · draft-discard · cleanup — green\n## Not done\nBTP deploy · S/4 integration · production login (XSUAA)\n```\n",
16
+ "sha256": "50f8c454df7be75bc7d9f961afd4b1862e3acdedad759f9d6c09e02f298e371a",
17
+ "signature": "",
18
+ "signedAt": "2026-09-28T14:37:04.123Z"
12
19
  },
13
20
  "abap-cca": {
14
21
  "name": "abap-cca",
15
- "body": "---\nname: abap-cca\ndescription: >\n Custom Code Analysis for S/4HANA migration and Clean Core initiatives.\n Discovers data sources, inventories all custom objects, analyzes usage\n and ATC findings, classifies with 20 heuristic rules, generates review\n worksheets for functional leads, and produces a consulting-grade\n assessment report. Chains to /abap-upgrade-fix for remediation.\nphase: ANALYZE\nrequires_mcp: required\nforge_rules: [6]\nversion: \"1.0\"\nmin_cli_version: \"0.3.0\"\nwidget: bar-chart\n---\n\n# abap-cca\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n\n## Purpose\n\nAnalyze custom ABAP code before an S/4HANA migration or Clean Core initiative.\nThe skill walks through eight phases: discover data sources, inventory all custom\nobjects, analyze usage and ATC and Clean Core findings, classify each object with\n20 heuristic rules, generate review worksheets for functional leads, import\nfunctional decisions, and produce a consulting-grade assessment report.\n\nThis is a read-only analysis -- it does not change any code (Forge Rule 6: read\nonly by default). Remediation chains to `/abap-upgrade-fix` for FIX-classified\nobjects, and hands off RETIRE lists for a future `/abap-retire` skill.\n\n## When to Use\n\n- Before S/4HANA migration -- assess the custom code landscape\n- Clean Core initiative -- identify unreleased API usage\n- Custom code right-sizing -- find objects to retire\n- Management reporting -- effort estimates and risk assessment\n\nDo NOT use for:\n- Greenfield development (use `/abap-generate` or `/abap-rap`)\n- Single-object fixes (use `/abap-incident` or `/abap-atc-fix`)\n- Transport management (use `/abap-transport`)\n\n## Required Tools\n\n| Tool | Phase | Purpose |\n|---|---|---|\n| `sap_sql_query` | DISCOVER, INVENTORY, ANALYZE | System probes, object queries, usage data |\n| `sap_atc_run` | ANALYZE | ATC findings per package |\n| `sap_api_state` | ANALYZE | Clean Core release state check |\n| `sap_usage_references` | ANALYZE | Cross-reference analysis |\n| `sap_search_object` | DISCOVER | Object search and validation |\n\n## SQL Rules (apply to ALL phases)\n\n**Prefer simple queries.** Aggregate SQL (`COUNT(*)`, `GROUP BY`, `SUM`,\n`DISTINCT`-with-aggregation) USUALLY works on ADT SQL endpoints — verified\non S/4HANA 2023 — but support varies by release and patch level. Default\nto the simplest query that answers the question:\n- `SELECT COUNT(*) FROM table WHERE single_predicate` is reliable\n everywhere — use it for existence + scale checks.\n- A single `GROUP BY DEVCLASS` over `TADIR` is fine when you need\n per-package counts in one round-trip; if the endpoint returns HTTP 400\n or empty-when-data-exists, fall back to per-package queries +\n skill-side counting.\n- AVOID `DISTINCT` combined with aggregation in the SAME query (e.g.\n `SELECT COUNT(DISTINCT x)`) — those reliably fail on ADT SQL. Run two\n queries instead.\n\n**No JOINs:** Use separate queries and merge results in the skill.\n\n**Keyset pagination:** ADT SQL has no OFFSET. Use `WHERE key > '{last_value}'\nORDER BY key` to paginate.\n\n**Result size limit:** MCP tool output has a character limit. Never SELECT all\nobjects across all packages in one query. Query per package or per type.\n\n**Existence check — the canonical pattern.** Before declaring a package\nempty / out-of-scope / not-worth-analyzing, run EXACTLY:\n\n```sql\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'\n```\n\nNO additional filter clauses. NO `AND OBJECT <> 'DEVC'`. NO\n`AND PGMID = 'R3TR'`. NO `AND DELFLAG = ''`. Each extra `AND` is an\nopportunity to nuke the result and falsely declare a populated package\nempty. Result interpretation:\n\n- `COUNT = 0` → no TADIR entry; verify package itself exists via TDEVC.\n- `COUNT = 1` → only the package's own DEVC entry exists; truly empty.\n- `COUNT >= 2` → at least one custom object; proceed.\n\n**Cross-validate before concluding a package is empty.** ALWAYS run\n`sap_search_object('{pkg}*')` (note trailing `*`) as a second probe. If\nthe search returns more matches than just the single package row, the\npackage has objects and the COUNT query was wrong — re-run with NO\nfilter and trust the new result. Two probes disagreeing is a strong\nsignal the model added a stray filter.\n\n**Do NOT conflate `isAddingObjectsAllowed=\"false\"` with \"package is\nempty\".** The `pak:isAddingObjectsAllowed=\"false\"` attribute (visible\nin `sap_object_structure` package XML) means SAP has frozen the package\nagainst further additions — not that it contains nothing. Frozen\nproduction packages routinely hold dozens of active objects. The only\nauthoritative emptiness test is a bare TADIR row count, cross-validated\nwith `sap_search_object`.\n\n## Phase Overview\n\n```\n/abap-cca --> INIT --> DISCOVER --> INVENTORY --> ANALYZE --> CLASSIFY\n --> REVIEW (worksheets) --> IMPORT --> REPORT\n --> chain to /abap-upgrade-fix (for FIX objects)\n --> hand off RETIRE list (for future /abap-retire)\n```\n\n## CLI Flags\n\nTwo flags shape multi-developer / multi-session CCA work. Both are\noptional — without them, the skill runs as before with interactive\nscope selection and the existing project.json-based resume.\n\n### `--scope` (slice the estate for parallel runs)\n\nFilter the inventory before classification so each consultant's\nslice is independent. Three syntaxes (priority order — first wins):\n\n```\n--scope @<file> # one obj/line OR comma-list, file in workspace\n--scope \"ZFI*,ZSD*\" # comma-separated patterns, * wildcard ok\n--scope \"ZFI*\" # single pattern\n```\n\nApply the filter AFTER INIT (namespace discovery) and DISCOVER\n(estate map) phases, BEFORE INVENTORY paginated read. Each\nconsultant's run only inventories + classifies their slice — no\nduplicate work. Record the scope expression in\n`project.json.scope` so the manifest envelope and merge step can\nreconcile slices later.\n\n### `--resume <project-name>` or `--resume @<envelope>`\n\nExplicit resume flag. Two resolutions:\n\n- `--resume <project-name>` → load\n `.cspeach/cca/projects/<project-name>/project.json`. Falls back\n to the most-recent project under `.cspeach/cca/projects/` if no\n name supplied (`--resume` with no arg). For older projects, if the\n `.cspeach/cca/projects/` path is absent, fall back to the legacy\n `.abapforge/cca/projects/` location.\n- `--resume @<envelope>` → resolve the workspace envelope (a\n saved `cca-assessment` `.cspeach.json` file), read its\n `content.projectId`, and load that project.\n\nResume logic in priority order:\n1. Find the highest `step-N-done.json` checkpoint under\n `.cspeach/cca/projects/<id>/checkpoints/` (older projects: the legacy\n `.abapforge/cca/projects/<id>/checkpoints/`).\n2. Resume the next phase (N+1) using the cached output of step N.\n3. If no checkpoints exist, fall back to the existing\n `project.json.currentPhase` field (legacy resume).\n4. If neither exists, start fresh from INIT.\n\n## Checkpoints\n\nAfter each phase finishes successfully, write a checkpoint to:\n\n```\n.cspeach/cca/projects/<id>/checkpoints/step-<N>-done.json\n```\n\nWhere `<N>` is the phase number (1=INIT, 2=DISCOVER, 3=INVENTORY,\n4=ANALYZE, 5=CLASSIFY, 6=REVIEW, 7=IMPORT, 8=REPORT).\n\nCheckpoint format:\n\n```json\n{\n \"phase\": 5,\n \"phaseName\": \"CLASSIFY\",\n \"completedAt\": \"2026-05-09T10:30:00Z\",\n \"projectId\": \"<slug>\",\n \"outputs\": [\n \"objects.json\",\n \"project.json\"\n ],\n \"checksum\": \"<sha256 of project.json bytes at end of this phase>\"\n}\n```\n\nOn `--resume`, scan the checkpoints directory, find the highest\n`step-N-done.json`, validate the checksum still matches\n`project.json` (warn if drift), and resume from step N+1. The\ncached `outputs[]` files are the input to the next phase.\n\nCrashes / Ctrl+C / VPN drops mid-phase are recoverable: the\nunfinished phase has no `step-N-done.json`, so resume picks up at\nphase N (re-running it). Idempotent phase logic is required —\neach phase must tolerate being re-run after a partial completion\n(use `existsSync` checks before recreating files, etc.).\n\nDo NOT write `step-N-wip.json` or `step-N-failed.json` — only\nwrite `step-N-done.json` on phase success. Intra-phase recovery\nis a future enhancement.\n\n## Session Resume\n\nAt startup, check `.cspeach/cca/projects/` for existing project files (for older\nprojects, also check the legacy `.abapforge/cca/projects/`). Use the\n`Read` tool to list the directory. If project JSON files are found:\n\n1. Read each `project.json` file.\n2. List projects with their name, current phase, and progress summary (e.g.,\n \"ACME_MIGRATION -- ANALYZE phase, 340/512 objects classified\").\n3. Ask: \"Resume an existing project, or start a new one?\"\n\nIf the directory does not exist or is empty, proceed directly to INIT.\n\nWhen the user invoked the skill with `--resume`, skip the interactive\nprompt above and resume the named project directly (see CLI Flags\nsection). When the user invoked with `--scope`, skip the interactive\nscope selection in INIT (see Scope Selection below).\n\n## INIT Phase\n\n### First-Time Onboarding\n\nDisplay this text once at the start of a new project:\n\n```\nCustom Code Analysis helps you understand your custom ABAP code\nbefore an S/4HANA migration or Clean Core initiative. It will:\n\n1. Find all your custom programs, classes, tables, and enhancements\n2. Check which ones are still being used\n3. Identify which ones have S/4 compatibility issues\n4. Produce a report with effort estimates\n\nThis is a read-only analysis -- it will not change any code.\n```\n\n### Scope Selection\n\nPresent three options:\n\n```\nHow would you like to define the scope?\n 1. I know my package names (type them)\n 2. Find all custom packages on this system (recommended)\n 3. I'm not sure -- help me\n```\n\n**Option 1 (expert):** Accept direct entry. Valid formats:\n- Single package: `ZCUSTOM_FI`\n- Comma-separated list: `ZCUSTOM_FI, ZCUSTOM_SD, ZCUSTOM_MM`\n- Wildcards: `Z*`, `ZCUSTOM_*`\n- Mixed: `ZCUSTOM_FI, /ACME/CORE, YLEGACY_*`\n- Namespace prefixes: `/ACME/*`\n\nStore the raw input. If wildcards are present, resolve them in the wildcard\nresolution step below before proceeding.\n\n**Option 2 (discovery-first):** Run separate queries per prefix via `sap_sql_query`.\nDo NOT combine prefixes with OR — the `/%` pattern can cause silent failures on\nsome ADT endpoints, and large result sets may timeout returning empty instead of\nan error.\n\n```sql\n-- Query 1: Z-packages\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE 'Z%' ORDER BY DEVCLASS\n\n-- Query 2: Y-packages\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE 'Y%' ORDER BY DEVCLASS\n```\n\n**Query 3: Namespace packages.** Customer namespace packages (e.g., `/ACME/CORE`)\nalso need discovery. Do NOT run a blanket `LIKE '/%'` — that returns hundreds of\nSAP-delivered namespace packages. Instead, first discover which customer namespaces\nexist:\n\n```sql\nSELECT NAMESPACE FROM TRNSPACE WHERE OWNER <> 'SAP' AND OWNER <> '' ORDER BY NAMESPACE\n```\n\nIf TRNSPACE is not accessible, ask the user: \"Does your system have custom namespace\npackages (e.g., /ACME/*, /MYCO/*)? If yes, type the namespace prefix.\"\n\nFor each customer namespace found, run:\n```sql\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE '{namespace}%' ORDER BY DEVCLASS\n```\n\nMerge all namespace packages into the results alongside Z/Y packages.\n\n**Empty result safety:** If a TDEVC query returns zero rows, retry once. If still\nempty, fall back to TADIR: `SELECT DEVCLASS FROM TADIR WHERE DEVCLASS LIKE 'Z%'\nORDER BY DEVCLASS`. TADIR is larger but confirms whether packages with objects\nexist.\n\nMerge results from all queries. Display grouped by prefix.\n\n**Object counts per package — for DISPLAY in the user-facing list.** Use\nthis filtered count when rendering \"package X has Y objects\" so the DEVC\nrow doesn't make empty packages look populated:\n\n```sql\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}' AND OBJECT <> 'DEVC'\n```\n\nThis is for DISPLAY only. It is NOT the empty-package test (see SQL\nRules above — the existence check uses the BARE `WHERE DEVCLASS = '{pkg}'`\nquery with no extra filters). Mixing the two has caused real\nhallucinations where the model added a filter, got 0 from the count,\nand declared a populated package empty.\n\n**Optimization:** Do NOT run one query per package — that causes 37+ sequential\ncalls. Two viable approaches, both faster:\n\n```sql\n-- Approach A: GROUP BY in one round-trip (works on S/4HANA 2023+;\n-- if it returns HTTP 400 on your release, fall back to Approach B).\nSELECT DEVCLASS, COUNT(*) AS N FROM TADIR\nWHERE DEVCLASS LIKE '{prefix}%' AND OBJECT <> 'DEVC'\nGROUP BY DEVCLASS\n\n-- Approach B: IN-batched per-row, count client-side.\nSELECT OBJ_NAME, DEVCLASS FROM TADIR\nWHERE DEVCLASS IN ('{pkg1}','{pkg2}','{pkg3}',...) AND OBJECT <> 'DEVC'\nORDER BY DEVCLASS\n```\n\nFor Approach B, batch packages into groups of 10-15 per query (to stay\nwithin SQL length limits). Count results per DEVCLASS in the skill.\nThis turns 37 queries into 3-4 queries.\n\nIf a single batch returns too many results (>5000 rows), fall back to per-package\nCOUNT(*) queries for that batch only.\n\nShow the count next to each package name. Hide packages with 0 objects (empty\npackages). Do NOT attempt to SELECT all packages in one query on systems with\n200+ packages.\n\nLet the user confirm all packages or select specific ones.\n\n**Option 3 (help me):** Run the same discovery query as option 2, but prefix the\nresults with this explanation:\n\n```\nPackages are containers for custom ABAP objects. Custom packages start\nwith Z, Y, or a company namespace like /ACME/. I found the following\ncustom packages on your system:\n```\n\nThen show the same grouped list and let the user select.\n\n### Namespace Extraction\n\nAfter resolving the package list, extract unique namespace prefixes. For each\npackage, determine the prefix: `Z` for Z-packages, `Y` for Y-packages, or the\nfull namespace (e.g., `/ACME/`) for namespace packages. Store in\n`scope.namespaces`. Example: if packages are `ZCUSTOM_FI, YCUSTOM_SD,\n/ACME/CORE`, then `scope.namespaces = [\"Z\", \"Y\", \"/ACME/\"]`.\n\nEvery query in DISCOVER, INVENTORY, and ANALYZE that uses `LIKE '{prefix}%'`\nmust run once PER namespace. A query with `LIKE 'Z%'` will miss `/ACME/` objects.\n\n### Wildcard Resolution\n\nIf `scope.packages` contains any wildcard entries (e.g., `Z*`, `ZCUSTOM_*`),\nresolve them before proceeding. Run via `sap_sql_query`:\n\n```sql\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE '{pattern}'\nORDER BY DEVCLASS\n```\n\nReplace the wildcard entry with the explicit list of matching packages. Show the\nresolved list to the user for confirmation. If no packages match a wildcard\npattern, warn the user and remove that entry.\n\n### Compliance Tagging\n\nAfter scope is confirmed, ask:\n\n```\nAny packages subject to regulatory or quality compliance?\nExamples:\n ZPHARMA_QM --> GXP (pharmaceutical/FDA validation)\n ZPHARMA_FI --> SOX (financial controls / Sarbanes-Oxley)\n ZMU_QM --> QUALITY (ISO 9001 / quality management)\n Others --> NONE\n\nEnter compliance tags as: PACKAGE_NAME=TAG (comma-separated)\nor press Enter for NONE on all packages.\n```\n\nCompliance levels and their effects on classification:\n\n| Level | Effect |\n|---|---|\n| **GXP** | Objects NEVER auto-classified as RETIRE. Formal change control required. |\n| **SOX** | Objects NEVER auto-classified as RETIRE. SOX control owner sign-off required. |\n| **QUALITY** | Extra scrutiny: DORMANT instead of RETIRE_CANDIDATE when zero usage. |\n| **NONE** | Normal classification rules apply. |\n\nStore compliance tags in `scope.compliance` as a map of package name to tag.\n\n**Object-level compliance overrides:** If the user needs finer granularity (e.g.,\n3 GxP objects in a 160-object package), accept overrides in this format:\n\n```\nZPACKAGE/OBJECT_NAME=TAG\n```\n\nStore these in `scope.complianceOverrides` as a map of `package/object` to tag.\nObject-level overrides take precedence over package-level tags.\n\n### Per-Package Phase Tracking\n\nInitialize `packagePhases` as a map of package name to current phase. Set all\npackages to `INIT`. The project-level `currentPhase` is always the minimum phase\nacross all packages (i.e., the least-advanced package determines the overall\nphase).\n\n### Delta Scope Changes\n\nThe user can add or remove packages later in any phase. When packages are added:\n- New packages start at INIT phase.\n- Existing results are preserved -- no re-analysis of already-processed packages.\n- `currentPhase` recalculates as the minimum across all `packagePhases`.\n\nWhen packages are removed:\n- Remove the package from `packagePhases` and `scope.packages`.\n- Preserve the data in the project file (mark as `excluded: true`) in case the\n user wants to re-add later.\n\n### Save Project State\n\nAt the end of INIT, save the project file. Create the directory if it does not\nexist:\n\n```\n.cspeach/cca/projects/{project_name}/project.json\n```\n\nThe `project_name` is derived from the user's description or a default like\n`cca_{date}`. If the user stated a project id anywhere in the prompt (e.g.\n\"project id: acme-s4-2026\"), use it VERBATIM as `project_name`/`projectId` —\nno renaming, no normalisation — parallel team slices must share one identity\nso `/abap-cca-merge` can reconcile them. The project.json structure:\n\n```json\n{\n \"name\": \"{project_name}\",\n \"created\": \"{ISO timestamp}\",\n \"currentPhase\": \"INIT\",\n \"scope\": {\n \"packages\": [\"ZCUSTOM_FI\", \"ZCUSTOM_SD\"],\n \"namespaces\": [\"Z\"],\n \"compliance\": {\n \"ZCUSTOM_FI\": \"SOX\",\n \"ZCUSTOM_SD\": \"NONE\"\n },\n \"complianceOverrides\": {}\n },\n \"packagePhases\": {\n \"ZCUSTOM_FI\": \"INIT\",\n \"ZCUSTOM_SD\": \"INIT\"\n }\n}\n```\n\n### INIT Phase Completion\n\nPrint confirmation:\n\n```\nProject: {project_name}\nPackages: {count} packages in scope\nCompliance: {summary of tagged packages, or \"none\"}\nSaved to: .cspeach/cca/projects/{project_name}/project.json\n\nScope set. Moving to DISCOVER phase.\n```\n\nPhase complete. Move to DISCOVER.\n\n---\n\n## Full project.json Schema\n\nThis is the canonical schema for the project state file. Create it during INIT and\nupdate it after every phase. All fields shown here are valid; omit fields that have\nnot been populated yet. Always preserve existing data when updating -- never\noverwrite the entire file; read first, merge changes, then write.\n\n```json\n{\n \"version\": \"3.0\",\n \"projectId\": \"S4H_Migration_ZCUSTOM\",\n \"system\": { \"sid\": \"S4H\", \"client\": \"100\", \"release\": \"758 SP02\" },\n \"scope\": {\n \"packages\": [\"ZCUSTOM_FI\", \"ZCUSTOM_SD\"],\n \"namespaces\": [\"Z\", \"Y\"],\n \"compliance\": {\n \"ZPHARMA_QM\": \"GXP\",\n \"ZPHARMA_FI\": \"SOX\",\n \"ZMU_QM\": \"QUALITY\",\n \"ZCUSTOM_SD\": \"NONE\"\n },\n \"complianceOverrides\": {\n \"ZINT_LIMS_BATCH_SEND\": \"GXP\",\n \"ZINT_LIMS_BATCH_RECV\": \"GXP\"\n }\n },\n \"currentPhase\": \"ANALYZE\",\n \"packagePhases\": {\n \"ZCUSTOM_FI\": \"CLASSIFY\",\n \"ZCUSTOM_SD\": \"ANALYZE\"\n },\n \"discovery\": {\n \"dataSources\": {\n \"TADIR\": { \"available\": true, \"rowCount\": 512 },\n \"TDEVC\": { \"available\": true },\n \"TRDIR\": { \"available\": true },\n \"UPL\": {\n \"available\": true,\n \"table\": \"/SDF/MON_RECS\",\n \"progField\": \"PROG_NAME\",\n \"countField\": \"COUNTER\"\n },\n \"SCMON\": { \"available\": false },\n \"TBTCP\": { \"available\": true },\n \"SXC_ATTR\": { \"available\": true, \"count\": 12 },\n \"MODATTR\": { \"available\": true, \"count\": 3 },\n \"ARS_W_API_STATE\": { \"available\": true },\n \"ARS_W_API_SCCSSR\": { \"available\": true },\n \"TFDIR\": { \"available\": true, \"rfcCount\": 8 },\n \"ATC_VARIANT\": { \"available\": true, \"variant\": \"S4HANA_READINESS\" }\n },\n \"confidence\": \"HIGH\"\n },\n \"progress\": {\n \"inventory\": { \"completedPackages\": [], \"pendingPackages\": [] },\n \"analysis\": {\n \"atcCompleted\": [],\n \"usageCollected\": false,\n \"crossRefCompleted\": 0\n },\n \"review\": {\n \"worksheetsGenerated\": false,\n \"responsesReceived\": [],\n \"responsesPending\": []\n }\n }\n}\n```\n\n### Schema Notes\n\n- `version`: Always `\"3.0\"` for this skill version.\n- `system`: Populated during INIT from the connected SAP system metadata.\n- `scope.namespaces`: Extracted from packages. Every `LIKE '{prefix}%'` query runs\n once per namespace entry.\n- `scope.compliance` and `scope.complianceOverrides`: Object-level overrides take\n precedence over package-level tags.\n- `currentPhase`: Always the minimum phase across all `packagePhases`.\n- `discovery.dataSources`: Each key maps to a probe result. Set `available: false`\n if the table does not exist or returns an authorization error.\n- `discovery.confidence`: Preliminary estimate after DISCOVER. Recalculated after\n CLASSIFY when actual UPL coverage is known.\n- `progress`: Tracks completion within each phase. Updated incrementally.\n\n---\n\n## DISCOVER Phase\n\n### Purpose\n\nProbe the SAP system to determine which data sources are available for analysis.\nNot every system has UPL enabled, SCMON configured, or Clean Core tables present.\nDISCOVER finds out what exists before INVENTORY and ANALYZE depend on it.\n\n### Session Resume\n\nIf `discovery.dataSources` already has results for all 12 probes in project.json,\nskip DISCOVER and move directly to INVENTORY. If only some probes have results,\nresume from the first missing probe.\n\n### Entry Criteria\n\n- INIT phase is complete.\n- `scope.packages` and `scope.namespaces` are populated in project.json.\n\n### Probe Execution\n\nRun all 12 probes via `sap_sql_query`. For each probe, record the result in\n`discovery.dataSources`. If a query fails with \"table not found\" or similar, set\n`available: false`. If it fails with an authorization error, set `available: false`\nwith `reason: \"authorization\"`. Log the gap but do NOT hard-stop.\n\n**Multi-namespace rule:** Every query that uses `LIKE '{prefix}%'` must run once\nPER namespace from `scope.namespaces`. A query with `LIKE 'Z%'` will MISS all\n`/OLDCO/` objects. Loop over `scope.namespaces` and run each query per namespace,\nmerging results.\n\n### The 12 Probes\n\n| # | Probe | What | Query/Method | Notes |\n|---|-------|------|-------------|-------|\n| 1 | TADIR | Object count | `SELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'` per package | Always exists |\n| 2 | TDEVC | Package hierarchy + wildcard resolution | Already done in INIT | Always exists |\n| 3 | TRDIR | Program metadata | `SELECT COUNT(*) FROM TRDIR WHERE NAME LIKE '{prefix}%'` | Always exists |\n| 4a-d | UPL variants | Usage logging | Try each: `/SDF/MON_RECS`, `/SDF/MON_HEADER`, `ZQLREC`, `/SDF/MON` with `SELECT * UP TO 1 ROWS FROM {table}` | First success wins |\n| 5 | SCMON | System call monitoring | `SELECT * UP TO 1 ROWS FROM /SDF/CUS_MEASURE` | S/4 only usually |\n| 6 | TBTCP | Batch job history | `SELECT COUNT(*) FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'` | Always exists |\n| 7 | SXC_ATTR | Active BAdI implementations | `SELECT IMP_NAME, EXIT_NAME FROM SXC_ATTR WHERE IMP_NAME LIKE '{prefix}%' AND ACTIVE = 'X'` | Run per namespace |\n| 8 | MODATTR + MODACT | Active CMOD user exit projects | See CMOD resolution below | Run per namespace |\n| 9 | ARS_W_API_STATE | Clean Core release states | `SELECT * UP TO 1 ROWS FROM ARS_W_API_STATE` | S/4 only |\n| 10 | ARS_W_API_SCCSSR | Successor API mapping | `SELECT * UP TO 1 ROWS FROM ARS_W_API_SCCSSR` | Probe separately |\n| 11 | TFDIR | RFC-enabled FMs | `SELECT FUNCNAME, FMODE FROM TFDIR WHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'` | Run per namespace |\n| 12 | ATC variant | S4HANA_READINESS check | Use `sap_atc_run` with variant parameter or query ATC config | Probe before ANALYZE |\n\n### Probe 1: TADIR (Object Count)\n\nRun per package in `scope.packages`:\n\n```sql\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'\n```\n\nSum the counts across all packages. Store in `discovery.dataSources.TADIR`:\n```json\n{ \"available\": true, \"rowCount\": 512 }\n```\n\n### Probe 2: TDEVC (Package Hierarchy)\n\nAlready completed during INIT wildcard resolution. Mark as available:\n```json\n{ \"available\": true }\n```\n\n### Probe 3: TRDIR (Program Metadata)\n\nRun per namespace:\n\n```sql\nSELECT COUNT(*) FROM TRDIR WHERE NAME LIKE '{prefix}%'\n```\n\nMerge counts across namespaces. Store in `discovery.dataSources.TRDIR`:\n```json\n{ \"available\": true }\n```\n\n### Probes 4a-d: UPL (Usage and Procedure Logging)\n\nTry each UPL table variant in order. Stop at the first one that succeeds:\n\n1. `/SDF/MON_RECS`\n2. `/SDF/MON_HEADER`\n3. `ZQLREC`\n4. `/SDF/MON`\n\nFor each, run:\n\n```sql\nSELECT * UP TO 1 ROWS FROM {table}\n```\n\nIf the query succeeds, parse the response to discover column names. Apply these\nheuristics to identify key columns:\n\n- **Program field:** Column name containing `PROG`, `PROGRAM`, or `OBJECT_NAME`.\n- **Count field:** Column name containing `COUNT`, `COUNTER`, or `EXECUTION`.\n\nIf heuristic matching fails, present the column names to the user and ask:\n\n```\nUPL table {table} found, but I cannot auto-detect the column mapping.\nColumns: {comma-separated column list}\n\nWhich column contains the program name?\nWhich column contains the execution counter?\n```\n\nStore the result in `discovery.dataSources.UPL`:\n```json\n{\n \"available\": true,\n \"table\": \"/SDF/MON_RECS\",\n \"progField\": \"PROG_NAME\",\n \"countField\": \"COUNTER\"\n}\n```\n\nIf none of the four tables exist, store:\n```json\n{ \"available\": false }\n```\n\n### Probe 5: SCMON (System Call Monitoring)\n\n```sql\nSELECT * UP TO 1 ROWS FROM /SDF/CUS_MEASURE\n```\n\nStore in `discovery.dataSources.SCMON`:\n```json\n{ \"available\": true }\n```\n\nOr `{ \"available\": false }` if the table does not exist. SCMON is typically only\navailable on S/4HANA systems.\n\n### Probe 6: TBTCP (Batch Job History)\n\nRun per namespace:\n\n```sql\nSELECT COUNT(*) FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'\n```\n\nMerge counts. Store in `discovery.dataSources.TBTCP`:\n```json\n{ \"available\": true }\n```\n\n### Probe 7: SXC_ATTR (Active BAdI Implementations)\n\nRun per namespace:\n\n```sql\nSELECT IMP_NAME, EXIT_NAME FROM SXC_ATTR\nWHERE IMP_NAME LIKE '{prefix}%' AND ACTIVE = 'X'\n```\n\nMerge results across namespaces. Store count in `discovery.dataSources.SXC_ATTR`:\n```json\n{ \"available\": true, \"count\": 12 }\n```\n\n### Probe 8: MODATTR + MODACT (CMOD User Exit Projects)\n\nFirst, try a JOIN query:\n\n```sql\nSELECT NAME, MEMBER, TYP FROM MODSAP\nINNER JOIN MODACT ON MODSAP~NAME = MODACT~NAME\nWHERE MODACT~STATUS = 'A'\n```\n\nIf the JOIN fails (common on some ADT SQL endpoints), fall back to two sequential\nqueries:\n\n1. Get active projects:\n```sql\nSELECT NAME FROM MODACT WHERE STATUS = 'A'\n```\n\n2. Get members of active projects (use the names from step 1):\n```sql\nSELECT NAME, MEMBER, TYP FROM MODSAP WHERE NAME IN ({active_names})\n```\n\nMatch exit includes (names starting with `EXIT_` or `ZX`) against the inventory.\nTag matched objects with `isCMODExit = true` in later phases.\n\nStore in `discovery.dataSources.MODATTR`:\n```json\n{ \"available\": true, \"count\": 3 }\n```\n\n### Probe 9: ARS_W_API_STATE (Clean Core Release States)\n\n```sql\nSELECT * UP TO 1 ROWS FROM ARS_W_API_STATE\n```\n\nThis table exists only on S/4HANA systems with Clean Core metadata. Store:\n```json\n{ \"available\": true }\n```\n\nOr `{ \"available\": false }` if the table does not exist.\n\n### Probe 10: ARS_W_API_SCCSSR (Successor API Mapping)\n\nProbe separately from probe 9 -- this table may exist independently:\n\n```sql\nSELECT * UP TO 1 ROWS FROM ARS_W_API_SCCSSR\n```\n\nStore in `discovery.dataSources.ARS_W_API_SCCSSR`:\n```json\n{ \"available\": true }\n```\n\nOr `{ \"available\": false }`.\n\n### Probe 11: TFDIR (RFC-Enabled Function Modules)\n\nRun per namespace:\n\n```sql\nSELECT FUNCNAME, FMODE FROM TFDIR\nWHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'\n```\n\nThis detects RFC-enabled function modules -- a critical blind spot because RFC\ninterfaces are often undocumented and may have external consumers that break during\nmigration.\n\nMerge results across namespaces. This count is SYSTEM-WIDE (all Z/Y/namespace\nFMs), not scoped to the selected packages. The per-package RFC count is\ndetermined in INVENTORY when individual FMs are mapped to packages via TFDIR.\n\nStore in `discovery.dataSources.TFDIR`:\n```json\n{ \"available\": true, \"rfcCountSystemWide\": 8 }\n```\n\nIn the DISCOVER summary, show: `\"RFC-enabled FMs: {n} system-wide (scoped count in INVENTORY)\"`\nDo NOT show the system-wide count as if it applies to the scoped packages.\n\n### Probe 12: ATC Variants\n\nDo NOT run `sap_atc_run` here — that executes the full analysis and belongs in\nANALYZE. This probe only discovers which check variants are available.\n\n**Step 1: Query all available ATC check variants.**\n\nATC check variants are stored as PROG objects in TADIR with a `VC` suffix\n(padded with `=` signs). `SATC_CI_CHKV_GL` stores check CLASSES, not variants.\n\n```sql\nSELECT OBJ_NAME FROM TADIR WHERE OBJECT = 'PROG' AND OBJ_NAME LIKE '%==============VC' ORDER BY OBJ_NAME\n```\n\nEach result looks like `S4HANA_READINESS==============VC`. Strip the `=` padding\nand `VC` suffix to get the variant name: `S4HANA_READINESS`.\n\nFor S/4-specific variants only (faster, smaller result):\n```sql\nSELECT OBJ_NAME FROM TADIR WHERE OBJECT = 'PROG' AND OBJ_NAME LIKE 'S4HANA%VC' ORDER BY OBJ_NAME\n```\n\n**Step 2: CRITICAL — Use ONLY the SQL results.**\n\n**DO NOT add variant names from your training knowledge.** If a variant name\ndoes not appear in the SQL query result rows, it DOES NOT EXIST on this system.\nDo not infer, guess, or supplement the list. The SQL result is the ONLY truth.\n\nFrom the SQL results ONLY, separate into:\n- **User-visible variants** (`HIDDEN = ''` or `HIDDEN <> 'X'`)\n- **Hidden/internal variants** (`HIDDEN = 'X'`)\n\n**Step 3: Categorize the variants found in the SQL results.**\n\nFor each variant name ACTUALLY RETURNED by the SQL query, check if it matches\nthese patterns to flag it as S/4-relevant:\n\n| Pattern in the ACTUAL name | Category |\n|---|---|\n| Contains `S4HANA` or `READINESS` | S/4HANA readiness |\n| Contains `ARS_COMPAT` | Clean Core / API compatibility |\n| Contains `/SDF/B2S` | SAP Business Transformation Suite |\n| Contains `SFIN` | S/4HANA Finance |\n| Contains `FUNCTIONAL_DB` | HANA database |\n| Contains `CLOUD` or `RESTRICTED_ABAP` | ABAP Cloud |\n| `DEFAULT` | General quality (always present) |\n\nIf a pattern matches ZERO variants from the SQL results, that category is\nempty — do NOT fabricate a variant name for it.\n\n**Step 4: Store results.**\n\nStore the raw variant names (after stripping `=` padding and `VC` suffix) in\nproject.json. Keep the full list internally but do NOT show it to the user.\n\n```json\n{\n \"available\": true,\n \"allVariants\": [\"DEFAULT\", \"S4HANA_READINESS\", \"S4HANA_READINESS_2023\", ...],\n \"hasS4Readiness\": true\n}\n```\n\nIf the TADIR query returned zero `VC` rows: `{ \"available\": false }`.\n\n**Step 5: DISCOVER summary — keep it simple.**\n\nIn the DISCOVER completion output, show ONE line for ATC:\n\n- If S4HANA_READINESS variants found: `\"ATC: available (S/4HANA readiness checks installed)\"`\n- If only DEFAULT/other: `\"ATC: available (general checks only — no S/4 readiness variant)\"`\n- If nothing: `\"ATC: not available\"`\n\nDo NOT list all 18 variant names in the DISCOVER summary. The user doesn't\nneed that detail yet. Variant selection happens in ANALYZE.\n\n**Step 6: Variant selection happens in ANALYZE, not DISCOVER.**\n\nDuring ANALYZE, ask the user's target release and recommend the right variant:\n\n```\nATC variant selection:\n\nWhat is your target S/4HANA release?\n 1. S/4HANA 2023 (latest)\n 2. S/4HANA 2022\n 3. S/4HANA 2021\n 4. S/4HANA 2020\n 5. Not sure / latest available\n 6. Type a custom variant name\n\n(Version-specific variants check for incompatibilities specific to that\ntarget release. Using the wrong version may miss or over-report findings.)\n```\n\nMap the user's answer to the matching variant from the discovered list:\n- S/4HANA 2023 → `S4HANA_READINESS_2023` (if available, else `S4HANA_READINESS`)\n- S/4HANA 2022 → `S4HANA_READINESS_2022`\n- Not sure → `S4HANA_READINESS` (generic, latest)\n\nIf the user's target release has no matching variant, use the closest available\nor the generic `S4HANA_READINESS`.\n\n### Authorization Errors\n\nIf any probe returns an authorization error (not \"table not found\" but \"no\nauthorization for table X\"), treat that data source as unavailable:\n\n```json\n{ \"available\": false, \"reason\": \"authorization\" }\n```\n\nLog the gap in the summary output. Do NOT hard-stop the entire DISCOVER phase for\na single authorization failure.\n\n### Data Availability Summary\n\nAfter all probes, summarize what data is available across TWO dimensions:\n\n**Technical data** (ATC + Clean Core) — always the primary deliverable:\n- ATC: available / not available (and which variant)\n- Clean Core: ARS tables available / not available\n\n**Usage data** (UPL + batch + cross-ref) — needed for keep/retire decisions:\n- UPL: available / not available\n- Batch: available / count\n- Cross-ref: always available (via sap_usage_references)\n\nStore `discovery.dataAvailability`:\n```json\n{\n \"technical\": { \"atc\": true, \"cleanCore\": true },\n \"usage\": { \"upl\": false, \"batch\": true, \"crossRef\": true },\n \"summary\": \"Technical analysis: FULL. Usage analysis: LIMITED (no UPL).\"\n}\n```\n\nDo NOT show a single \"confidence: LOW\" — that hides the reason. Show exactly\nwhat is available and what is missing. The user needs to know: \"Your technical\nreadiness assessment is complete. Usage data is missing — here's how to get it.\"\n\n### Save Results\n\nAfter all 12 probes complete, update project.json with the full `discovery` object.\nSet `currentPhase` to `\"DISCOVER\"` and update all `packagePhases` entries from\n`\"INIT\"` to `\"DISCOVER\"`.\n\n### DISCOVER Phase Completion\n\nPrint this summary:\n\n```\nDISCOVER complete. Data sources found:\n TADIR: {rowCount} objects across {pkg_count} packages\n UPL: {available/not} ({table name if found})\n Batch jobs: {count} programs with scheduled jobs\n BAdI implementations: {count} active\n CMOD exits: {count} active\n RFC-enabled FMs: {count}\n ARS_W_API_STATE: {available/not} (Clean Core check {enabled/disabled})\n ARS_W_API_SCCSSR: {available/not} (Successor API mapping {enabled/disabled})\n ATC variant: {variant name or \"not found\"}\n Confidence: {HIGH/MEDIUM/LOW}\n```\n\nIf any data sources are unavailable, append:\n\n```\nGaps:\n - {source}: {reason}\n```\n\n**If UPL is unavailable AND batch data is zero (confidence = LOW):**\n\nRuntime usage data is critical for accurate classification. Without it, most\nobjects will be classified as DORMANT or UNKNOWN. Before proceeding, offer the\nuser options to get production usage data:\n\n```\nNo runtime usage data found on this system. Without it, classification\nconfidence will be LOW — most objects will need manual review.\n\nOptions:\n 1. Continue anyway (static analysis only — fast but low confidence)\n 2. Import PRD usage data (if you have an SE16 export or SolMan extract)\n 3. I'll get usage data and come back (pause here, resume later)\n\nIf you have a CSV/spreadsheet with program names and execution counts from\nproduction, I can import it to dramatically improve classification accuracy.\n```\n\nIf user picks 1 → proceed to INVENTORY.\nIf user picks 2 → ask for the file path. Parse program name + count columns.\nStore as UPL-equivalent data in objects.json during ANALYZE. Set confidence\nto MEDIUM.\nIf user picks 3 → save checkpoint, print resume instructions, stop.\n\nPhase complete. Move to INVENTORY.\n\n---\n\n## objects.json Per-Object Schema\n\nThis file stores per-object metadata at\n`.cspeach/cca/projects/{id}/objects.json`. Each top-level key is the SAP object\nname. Create the file during INVENTORY, then enrich it during ANALYZE and CLASSIFY.\n\n```json\n{\n \"ZFI_AGING_REPORT\": {\n \"objectType\": \"PROG\",\n \"package\": \"ZCUSTOM_FI\",\n \"author\": \"JSMITH\",\n \"lastChangedDate\": \"20230412\",\n \"isTestObject\": false,\n \"isRFCEnabled\": false,\n \"isEnhancement\": false,\n \"isCMODExit\": false,\n \"compliance\": \"NONE\",\n \"usage\": {\n \"uplRuns\": 45230,\n \"batchRuns\": 0,\n \"lastRunDate\": \"20260401\",\n \"hasStaticCallers\": true,\n \"usageSource\": \"UPL\"\n },\n \"atcFindings\": [\n { \"priority\": \"1\", \"messageId\": \"...\", \"category\": \"tableReplacement\" }\n ],\n \"cleanCore\": {\n \"ready\": true,\n \"unreleased\": [],\n \"successors\": []\n },\n \"technicalReadiness\": {\n \"status\": \"NEEDS_FIX\",\n \"atcSummary\": \"1 P1 (BSEG table replacement)\",\n \"cleanCoreReady\": true,\n \"cloudReady\": false\n },\n \"usageStatus\": {\n \"status\": \"ACTIVE\",\n \"evidence\": \"UPL=45230 runs, last run 2026-04-01\",\n \"source\": \"UPL\"\n },\n \"classification\": {\n \"primary\": \"REFACTOR\",\n \"secondary\": [\"HAS_ATC_FINDINGS\"],\n \"evidence\": \"Active (UPL=45230) + needs fix (BSEG replacement)\",\n \"confidence\": \"HIGH\"\n },\n \"humanReview\": {\n \"used\": null,\n \"decision\": null,\n \"comments\": null,\n \"overrideReason\": null,\n \"reviewedBy\": null\n }\n }\n}\n```\n\n### Schema Notes\n\n- Each object is keyed by its SAP object name (e.g., `ZFI_AGING_REPORT`).\n- `objectType`: One of `PROG`, `CLAS`, `FUGR`, `FUNC`, `TABL`, `DTEL`, `DOMA`,\n `DDLS`, `SRVD`, `SRVB`, `INTF`, `MSAG`, `TTYP`, `VIEW`, or any other TADIR\n object type code.\n- `compliance`: Inherited from the package-level tag in `scope.compliance`, or\n overridden at object level via `scope.complianceOverrides`. Default is `\"NONE\"`.\n- `usage`: Populated during ANALYZE. All fields are `null` until then.\n- `technicalReadiness`: Populated at end of ANALYZE from ATC + Clean Core data.\n This is INDEPENDENT of usage data. Values: `CLEAN` (no issues), `MINOR_FIX`\n (P2/P3 only), `NEEDS_FIX` (P1 findings — table replacements etc.),\n `NEEDS_REDESIGN` (structural issues), `CLOUD_READY` (RAP framework object),\n `NOT_CHECKED` (ATC didn't run). This dimension is ALWAYS available — it does\n not require UPL.\n- `usageStatus`: Populated during CLASSIFY from UPL/batch/cross-ref. Values:\n `ACTIVE` (UPL confirms), `BATCH_ACTIVE` (batch jobs confirm), `DORMANT`\n (no recent usage), `UNVERIFIED` (no usage data available — cannot determine),\n `RETIRE_CANDIDATE` (confirmed unused >36 months). When UPL is unavailable,\n most objects will be `UNVERIFIED` — this is honest, not misleading.\n- `classification`: The combined recommendation derived from technicalReadiness\n + usageStatus. Populated during CLASSIFY. This is what appears on worksheets\n and reports.\n- `humanReview`: Populated during IMPORT when review worksheets are returned. All\n fields are `null` until the REVIEW phase generates worksheets and the user\n returns completed responses.\n- For FUGR objects: Store both the FUGR entry AND a separate FUNC entry for each\n child function module. The FUNC entry's `objectType` is `\"FUNC\"` and its key is\n the function module name (e.g., `Z_CALCULATE_TAX`). Cross-reference the parent\n FUGR by storing `\"parentFugr\": \"ZFUGR_NAME\"` on each FUNC entry.\n\n---\n\n## INVENTORY Phase\n\n### Purpose\n\nBuild the complete object inventory from TADIR and enrich it with metadata from\nTRDIR, SEOCLASS, TFDIR, and cross-reference tables. Produce objects.json with one\nentry per custom object.\n\n### Session Resume\n\nIf `progress.inventory.completedPackages` contains packages, skip those. Resume\nfrom the first package in `scope.packages` that is NOT in `completedPackages`.\n\n### Entry Criteria\n\n- DISCOVER phase complete.\n- `discovery.dataSources` populated in project.json.\n- `scope.packages` and `scope.namespaces` populated.\n\n### Pagination Strategy\n\nLarge systems can have thousands of objects per package. Use keyset pagination to\navoid timeouts and result truncation.\n\n**Step 1: Get the package list from `scope.packages`.**\n\n**Step 2: For each package, query TADIR:**\n\n```sql\nSELECT OBJ_NAME, OBJECT, DEVCLASS FROM TADIR\nWHERE DEVCLASS = '{package}'\nORDER BY OBJ_NAME\n```\n\nUse keyset pagination if results exceed maxRows. Take the last `OBJ_NAME` from\nthe current result set and issue the next query:\n\n```sql\nSELECT OBJ_NAME, OBJECT, DEVCLASS FROM TADIR\nWHERE DEVCLASS = '{package}' AND OBJ_NAME > '{last_name}'\nORDER BY OBJ_NAME\n```\n\nRepeat until the query returns fewer rows than maxRows.\n\n**Step 3: Sub-paginate by object type if needed.** If a single package has too\nmany objects for even the keyset approach, break the query down by object type:\n\n```sql\nSELECT OBJ_NAME, OBJECT FROM TADIR\nWHERE DEVCLASS = '{package}' AND OBJECT = 'PROG'\nORDER BY OBJ_NAME\n```\n\nThen CLAS, FUGR, TABL, DTEL, DOMA, DDLS, and so on. Apply keyset pagination\nwithin each object type if necessary.\n\n### TRDIR Enrichment (Programs)\n\nAfter collecting TADIR entries, enrich program metadata. Run per namespace:\n\n```sql\nSELECT NAME, SUBC, CNAM, CDAT, UNAM, UDAT FROM TRDIR\nWHERE NAME LIKE '{prefix}%'\n```\n\nMap the results onto the objects.json entries:\n- `author` = `CNAM`\n- `lastChangedDate` = `UDAT` (or `CDAT` if `UDAT` is empty)\n- `isTestObject` = `true` if `SUBC = 'T'`\n\n### SEOCLASS Enrichment (Classes)\n\nRun per namespace:\n\n```sql\nSELECT CLSNAME, AUTHOR, CREATEDON, CHANGEDBY, CHANGEDON FROM SEOCLASS\nWHERE CLSNAME LIKE '{prefix}%'\n```\n\nMap the results onto class entries in objects.json:\n- `author` = `AUTHOR`\n- `lastChangedDate` = `CHANGEDON` (or `CREATEDON` if `CHANGEDON` is empty)\n\n### FUGR to FM Resolution\n\nFor every FUGR found in TADIR, resolve individual function modules. Query TFDIR:\n\n```sql\nSELECT FUNCNAME, PNAME FROM TFDIR WHERE PNAME LIKE 'SAPL{fugr_name}'\n```\n\nNote: TFDIR stores the FM's program name as `SAPL{FUGR_NAME}` (uppercase, prefixed\nwith `SAPL`).\n\nFor each function module returned, create a separate FUNC entry in objects.json:\n\n```json\n{\n \"Z_CALCULATE_TAX\": {\n \"objectType\": \"FUNC\",\n \"package\": \"ZCUSTOM_FI\",\n \"parentFugr\": \"ZFUGR_TAX\",\n \"author\": null,\n \"lastChangedDate\": null,\n \"isTestObject\": false,\n \"isRFCEnabled\": false,\n \"isEnhancement\": false,\n \"isCMODExit\": false,\n \"compliance\": \"NONE\",\n \"usage\": null,\n \"atcFindings\": null,\n \"cleanCore\": null,\n \"classification\": null,\n \"humanReview\": null\n }\n}\n```\n\n### RFC Detection Per FM\n\nCross-reference with DISCOVER probe 11 results (TFDIR with `FMODE='R'`). For each\nfunction module in objects.json, check if it appears in the RFC-enabled FM list.\nSet `isRFCEnabled = true` on matching entries.\n\nIf probe 11 results were not cached, re-query per namespace:\n\n```sql\nSELECT FUNCNAME FROM TFDIR\nWHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'\n```\n\n### DDIC Dependency Mapping\n\n**Tables to consuming programs:** Run per namespace:\n\n```sql\nSELECT TABNAME, MASTER FROM D010TAB WHERE TABNAME LIKE '{prefix}%'\n```\n\nStore a `dependentPrograms` list on each table object in objects.json. This data\nfeeds the CLASSIFY phase (Rule 3: tables with many consumers are harder to retire).\n\n**Data elements to consuming tables:** Run per namespace:\n\n```sql\nSELECT TABNAME, FIELDNAME, ROLLNAME FROM DD03L\nWHERE ROLLNAME LIKE '{prefix}%' AND AS4LOCAL = 'A'\n```\n\nStore the consuming tables on each data element entry. This data feeds impact\nanalysis during CLASSIFY.\n\n### Transport Lock Check\n\nIdentify objects currently locked in active transports. Run per namespace:\n\n```sql\nSELECT OBJ_NAME, OBJECT FROM E071 WHERE OBJ_NAME LIKE '{prefix}%'\n```\n\nThen check the transport status for the returned transport requests:\n\n```sql\nSELECT TRKORR, TRSTATUS FROM E070 WHERE TRKORR IN ({transport_list})\n```\n\nObjects in active transports (`TRSTATUS = 'D'` modifiable or `TRSTATUS = 'L'`\nmodifiable protected) get `inActiveTransport = true` in objects.json. This is an\ninformational flag -- it does not block classification but is surfaced in the\nREPORT phase.\n\n### Test Object Detection\n\nApply these rules to flag test objects:\n\n- Programs with `SUBC = 'T'` from TRDIR enrichment: set `isTestObject = true`.\n- Objects with names ending in `_TEST`: set `isTestObject = true`.\n- Objects in packages whose name contains `_TEST` or `_UNIT`: set\n `isTestObject = true`.\n\nTest objects are auto-classified as `TEST` during CLASSIFY -- they skip all other\nclassification rules.\n\n### BAdI / Enhancement Tagging\n\nCross-reference with DISCOVER probe 7 (SXC_ATTR) results:\n- For each BAdI implementation class found in probe 7, locate the matching CLAS\n entry in objects.json and set `isEnhancement = true`.\n\nCross-reference with DISCOVER probe 8 (MODATTR/MODACT) results:\n- For each CMOD exit include found in probe 8, locate the matching PROG or\n include entry in objects.json and set `isCMODExit = true`.\n\n### Compliance Inheritance\n\nFor each object, set the `compliance` field:\n1. Check `scope.complianceOverrides` for an object-level override (key format:\n `PACKAGE/OBJECT_NAME`). If found, use that tag.\n2. Otherwise, inherit from `scope.compliance` using the object's package.\n3. Default to `\"NONE\"` if neither is set.\n\n### Checkpoint and Save\n\nSave objects.json every 5 packages. Update `progress.inventory.completedPackages`\nwith the packages just processed. On resume, skip completed packages.\n\nAfter each checkpoint, also update project.json:\n- Add completed packages to `progress.inventory.completedPackages`.\n- Move completed packages from `progress.inventory.pendingPackages`.\n- Update `packagePhases` for completed packages to `\"INVENTORY\"`.\n\n### SQL Gotchas\n\n- Underscore `_` is a single-character wildcard in ABAP SQL LIKE clauses. Use\n exact match (`DEVCLASS = '{package}'`) instead of LIKE with underscores wherever\n possible. If LIKE is unavoidable, escape underscores with `#_` (ABAP escape\n character).\n- Namespace slashes in table names (e.g., `/SDF/MON_RECS`) -- ADT handles this\n natively but verify that the slash encoding does not corrupt the query.\n- No JOINs in V1 of the SQL tool. Use separate queries and merge results in the\n skill logic.\n\n### INVENTORY Phase Completion\n\nPrint this summary:\n\n```\nINVENTORY complete.\n Total objects: {n} across {pkg_count} packages\n Programs: {n}\n Classes: {n}\n Function Groups: {n} ({fm_count} function modules)\n Tables: {n}\n Other: {n}\n Test objects: {n} (will be classified as TEST)\n RFC-enabled FMs: {n}\n Active BAdI implementations: {n}\n CMOD exits: {n}\n Objects in active transports: {n}\n\nPhase complete. Move to ANALYZE.\n```\n\nUpdate project.json: set `currentPhase` to `\"INVENTORY\"` and update all\n`packagePhases` entries from `\"DISCOVER\"` to `\"INVENTORY\"`.\n\nPhase complete. Move to ANALYZE.\n\n---\n\n## ANALYZE Phase\n\n### Purpose\n\nCollect three categories of data for every object in the inventory: usage data\n(UPL + batch + cross-references), ATC findings, and Clean Core readiness. Each\ncategory is independent and can be collected in any order.\n\n### Session Resume\n\nCheck `progress.analysis` in project.json:\n- If `usageCollected` is `true`, skip Part 1 (usage collection).\n- If `atcCompleted` contains package names, skip those packages in Part 3.\n- If `crossRefCompleted` is non-zero, resume cross-reference analysis from that\n offset.\n- If all three parts are complete, skip ANALYZE and move to CLASSIFY.\n\n### Entry Criteria\n\n- INVENTORY phase complete.\n- objects.json populated with all objects and metadata.\n- `discovery.dataSources` populated from DISCOVER phase.\n\n### Multi-Namespace Rule\n\nEvery query using `LIKE '{prefix}%'` runs once PER namespace from\n`scope.namespaces`. A query with `LIKE 'Z%'` will miss `/ACME/` objects. Loop\nover `scope.namespaces` and run each query per namespace, merging results.\n\n### Authorization Errors\n\nIf any query returns an authorization error, treat that data source as\nunavailable. Log the gap and reduce confidence. Do not hard-stop.\n\n---\n\n### Part 1: Usage Data Collection\n\n#### UPL Collection\n\nOnly run if `discovery.dataSources.UPL.available = true`. Use the field names\ndiscovered in DISCOVER: `discovery.dataSources.UPL.progField` and\n`discovery.dataSources.UPL.countField`. Use the table name from\n`discovery.dataSources.UPL.table`.\n\nUse keyset pagination. No aggregate SQL (SUM/GROUP BY not guaranteed on all ADT\nendpoints). No offset parameter in `sap_sql_query`.\n\n```sql\n-- Page 1 (run per namespace):\nSELECT {progField}, {countField} FROM {uplTable}\nWHERE {progField} LIKE '{prefix}%'\nORDER BY {progField}\n\n-- Page 2+ (using last program name from previous page):\nSELECT {progField}, {countField} FROM {uplTable}\nWHERE {progField} LIKE '{prefix}%' AND {progField} > '{last_name}'\nORDER BY {progField}\n```\n\nSet maxRows to 5000 (default). Keep paginating until returned rows < maxRows.\n\n**Skill-side aggregation:** Process each page immediately -- sum counts per\nprogram name, track max date. Do NOT accumulate all pages in memory.\nProcess-and-discard per page. Update objects.json incrementally.\n\n**Important: UPL tracks PROGRAMS, not classes.** Classes show zero UPL. This is\na known data gap handled in CLASSIFY via transitive classification.\n\nFor each matched program in objects.json, update:\n- `usage.uplRuns`: summed count value\n- `usage.lastRunDate`: most recent date if available\n- `usage.usageSource`: `\"UPL\"`\n\n#### Batch Job Collection\n\nAlways available. Two queries, no JOIN. Run per namespace:\n\n```sql\nSELECT PROGNAME, JOBNAME, JOBCOUNT FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'\n```\n\nThen for each distinct JOBNAME found:\n\n```sql\nSELECT JOBNAME, JOBCOUNT, STRTDATE FROM TBTCO WHERE STATUS = 'F' AND JOBNAME = '{job}'\n```\n\nFor each object matched, update:\n- `usage.batchRuns`: count of finished job executions\n- `usage.lastRunDate`: most recent STRTDATE\n- `usage.usageSource`: set to `\"BATCH\"` if no UPL data exists, or keep `\"UPL\"`\n if UPL data already present\n\n#### Active Enhancement Verification\n\nCross-reference SXC_ATTR BAdI implementations (from DISCOVER probe 7) and\nMODATTR/MODACT CMOD exits (from DISCOVER probe 8) with the objects in inventory.\nFor matched objects:\n- Set `isEnhancement = true` for BAdI implementation classes.\n- Set `isCMODExit = true` for CMOD exit includes.\n\nThese flags are already set during INVENTORY but verify and update here if new\ndata is available from re-probing.\n\n#### Usage Collection Checkpoint\n\nAfter all usage data is collected, set `progress.analysis.usageCollected = true`\nin project.json. Save objects.json.\n\n---\n\n### Part 2: Cross-Reference Analysis (Tiered)\n\nThe cross-reference budget is split into tiers. Tier 2 is SEPARATE from Tier 1,\nnot shared.\n\n| Tier | What | Budget | Priority |\n|---|---|---|---|\n| Tier 1 | Classes with zero UPL | Up to 500 calls | All classes |\n| Tier 2 | RFC-enabled FMs (from TFDIR probe) | Up to 50 calls (separate) | All RFC FMs |\n| Tier 3 | Programs with zero UPL, zero batch | Remaining from Tier 1 | Best effort |\n\n**Total cap: 550 calls** (500 Tier 1 + 50 Tier 2).\n\n#### Tier 1 Execution\n\nFor each class in objects.json with zero UPL data, call `sap_usage_references`\nto find callers. Store callers in the object's `usage` data for use in\ntransitive classification during CLASSIFY.\n\nIf there are more than 500 classes needing cross-references, ask the user which\npackages to prioritize:\n\n```\n{count} classes need cross-reference analysis. Budget is 500.\nWhich packages should I prioritize? (critical business packages first)\n```\n\nFor each call, update:\n- `usage.hasStaticCallers`: `true` if any callers found, `false` if none\n- Store caller list for CLASSIFY transitive analysis\n\n#### Tier 2 Execution\n\nFor each RFC-enabled FM (`isRFCEnabled = true` in objects.json), call\n`sap_usage_references` to find internal callers. This budget is separate from\nTier 1.\n\nFor each call, update:\n- `usage.hasStaticCallers`: `true` if any callers found, `false` if none\n\n#### Tier 3 Execution\n\nUse whatever remains from the 500 Tier 1 budget for programs with zero UPL AND\nzero batch jobs.\n\n#### Cross-Reference Progress\n\nReport every 50 calls: `\"Cross-reference: {n}/{total} completed.\"`\n\nCheckpoint: Save objects.json and update `progress.analysis.crossRefCompleted`\nevery 100 calls.\n\nObjects beyond the budget: set `usage.usageSource = \"NONE\"` with note\n`\"Cross-reference skipped due to volume.\"`.\n\n---\n\n### Part 3: ATC Analysis\n\n#### Honest Timing\n\nFor 35+ packages, ATC takes 3-6 hours. Tell the user upfront:\n\n```\nATC analysis for {count} packages will take approximately {estimate}.\nProgress saves after each package. Sessions will likely need to resume.\nProceed? [y/n]\n```\n\nIf the user declines, skip ATC and note the gap. All other phases continue\nwithout ATC data.\n\n#### ATC Variant Selection\n\nUse the variants discovered in Probe 12 (`discovery.dataSources.ATC_VARIANT`).\n\n**If S/4-relevant variants were found** (e.g., `ARS_COMPATIBILIY_CHECK`,\n`S4HANA_READINESS`, `/SDF/B2S_SFIN`), recommend the best one:\n\n```\nATC check variants available on this system:\n\n S/4-relevant:\n {list s4Relevant variants}\n\n General:\n DEFAULT — standard code quality\n\n Recommended: {recommended variant}\n\n Which variant? (type name, or press Enter for recommended)\n You can also type a custom variant name if you have one.\n```\n\n**If no S/4-relevant variants found** but DEFAULT exists:\n\n```\nNo S/4HANA-specific ATC variant found on this system.\nAvailable: DEFAULT (general code quality only — will NOT detect\ntable replacements like BSEG/KONV/VBUK or obsolete APIs).\n\nOptions:\n 1. Use DEFAULT anyway (better than nothing)\n 2. Type a custom variant name (if your team created one)\n 3. Ask Basis to install S/4 readiness checks (pause and resume later)\n 4. Skip ATC entirely (not recommended)\n\nWhich option?\n```\n\n**If no variants found at all:** skip ATC, note the gap.\n\n**Custom variant:** The user may have a project-specific variant created by\ntheir Basis team or a consulting partner. Accept any variant name the user\ntypes — `sap_atc_run` will validate it. If it fails, report the error and\nask for another name.\n\nAfter variant selection, proceed with ATC execution per package.\n\n#### ATC Execution\n\nRun `sap_atc_run` per package with the selected variant:\n\n```\nsap_atc_run (scope: \"{package}\", variant: \"{variant}\")\n```\n\nParse results. For each finding, update the corresponding object in objects.json:\n\n```json\n{\n \"priority\": \"1\",\n \"messageId\": \"CHECK_BSEG_ACCESS\",\n \"category\": \"tableReplacement\"\n}\n```\n\nAppend each finding to the object's `atcFindings` array.\n\n#### ATC Checkpoint\n\nSave objects.json after each package completes. Update\n`progress.analysis.atcCompleted` with the package name. On resume, skip\ncompleted packages.\n\n#### System Load Management\n\nATC runs consume work processes. On shared development systems, add a\nconfigurable delay (default 30 seconds) between package ATC runs:\n\n```\nNote: Adding 30-second delay between ATC runs to reduce system load.\nTo change: \"Set delay to 0 seconds\" or \"Set delay to 60 seconds\"\n```\n\nIf the user requests a different delay, apply it for all subsequent ATC runs in\nthe session.\n\n#### Timeout Handling\n\nIf `sap_atc_run` takes longer than 5 minutes for one package, save the status as\n`\"pending\"` for that package in `progress.analysis.atcCompleted`. On the next\nsession, check if results are available before re-running.\n\n---\n\n### Part 4: Clean Core Analysis\n\nOnly run if `discovery.dataSources.ARS_W_API_STATE.available = true`. On ECC\nsystems without this table, skip entirely and set all `cleanCore` fields to\n`null`.\n\n#### Step 1: Extract SAP API Dependencies Per Custom Object\n\nQuery D010TAB for which SAP standard tables each custom program uses. Run per\nnamespace:\n\n```sql\nSELECT MASTER, TABNAME FROM D010TAB\nWHERE MASTER LIKE '{prefix}%'\nAND TABNAME NOT LIKE 'Z%' AND TABNAME NOT LIKE 'Y%'\nAND TABNAME NOT LIKE '/%'\n```\n\nThis gives: custom program -> SAP standard tables it references.\n\n#### Step 2: Check Each SAP Dependency Against ARS_W_API_STATE\n\nCollect all unique SAP table/view names from Step 1. Check in bulk using\n`sap_api_state`:\n\n```\nsap_api_state (objects: \"TABL:VBAK,TABL:BSEG,TABL:KONV,...\")\n```\n\nBatch in groups of 20-30 per call to avoid request size limits.\n\n#### Step 3: Tag Each Custom Object\n\nFor each custom object, based on its SAP dependencies:\n- ALL dependencies released -> `cleanCore.ready = true`\n- ANY dependency not released -> `cleanCore.ready = false`, record unreleased\n APIs in `cleanCore.unreleased`\n- No dependencies extracted -> `cleanCore.ready = \"unknown\"`\n\n#### Step 4: Successor Lookup\n\nFor unreleased APIs, `sap_api_state` already returns successor information from\nARS_W_API_SCCSSR. Store successors in `cleanCore.successors` on the affected\nobject:\n\n```json\n{\n \"cleanCore\": {\n \"ready\": false,\n \"unreleased\": [\"BSEG\", \"KONV\"],\n \"successors\": [\n { \"old\": \"BSEG\", \"new\": \"I_JournalEntryItem\", \"type\": \"CDS\" },\n { \"old\": \"KONV\", \"new\": \"I_PricingConditionRecord\", \"type\": \"CDS\" }\n ]\n }\n}\n```\n\n#### Clean Core Limitation\n\nD010TAB captures TABLE references only. FM/class API dependencies require\ncross-reference tables (WBCROSSGT) which may be too large to query. For V1,\nClean Core check covers TABLE dependencies only. Document this limitation in the\nREPORT phase.\n\n---\n\n### ANALYZE Phase Completion\n\nPrint this summary:\n\n```\nANALYZE complete.\n Objects with UPL data: {n} ({pct}%)\n Objects with batch data: {n}\n Cross-references completed: {n}\n ATC findings: {n} total ({p1} priority 1, {p2} priority 2)\n Clean Core checked: {n} ({ready_count} cloud-ready, {not_ready} needs migration)\n Objects with no usage data: {n} ({pct}%)\n\nPhase complete. Move to CLASSIFY.\n```\n\nUpdate project.json: set `currentPhase` to `\"ANALYZE\"` and update all\n`packagePhases` entries from `\"INVENTORY\"` to `\"ANALYZE\"`.\n\nSave objects.json with all collected analysis data.\n\n### Compute Technical Readiness (end of ANALYZE)\n\nBefore moving to CLASSIFY, compute `technicalReadiness` for every object. This\nuses ATC + Clean Core data ONLY — no usage data needed. This dimension is always\navailable and always meaningful, even on DEV systems with zero UPL.\n\nFor each object in objects.json:\n\n| Condition | technicalReadiness.status |\n|---|---|\n| RAP framework object (BDEF, DDLS, DDLX, DCLS, SRVD, SRVB, STOB, G4BA) | `CLOUD_READY` |\n| Zero ATC findings AND cleanCore.ready = true | `CLEAN` |\n| Zero ATC findings AND cleanCore.ready = false | `NEEDS_FIX` (unreleased APIs) |\n| Zero ATC findings AND cleanCore.ready = unknown | `CLEAN` (no known issues) |\n| Only P2/P3 ATC findings, no P1 | `MINOR_FIX` |\n| Any P1 ATC findings (table replacements, structural) | `NEEDS_FIX` |\n| P1 ATC + cleanCore.ready = false (double impact) | `NEEDS_REDESIGN` |\n| ATC not run (skipped or unavailable) | `NOT_CHECKED` |\n\nPrint technical readiness summary:\n\n```\nTechnical Readiness (independent of usage data):\n CLOUD_READY: {n} — RAP/ABAP Cloud objects, no changes needed\n CLEAN: {n} — no S/4 compatibility issues found\n MINOR_FIX: {n} — minor issues (P2/P3), quick fixes\n NEEDS_FIX: {n} — S/4 incompatibilities (table replacements, unreleased APIs)\n NEEDS_REDESIGN: {n} — significant rework needed\n NOT_CHECKED: {n} — ATC didn't run\n\n Clean Core coverage: {n} of {total} programs checked\n {If n < total: \"{gap} programs had no SAP table dependencies in D010TAB — Clean Core status unknown for those.\"}\n```\n\nThis is the answer to \"which objects will BREAK on S/4HANA\" — regardless of\nwhether anyone runs them.\n\nPhase complete. Move to CLASSIFY.\n\n---\n\n## CLASSIFY Phase\n\nAssign a usage-based classification and combine it with technical readiness to\nproduce the final recommendation. The CLASSIFY phase adds the usage dimension\n(UPL, batch, cross-references) on top of the technical readiness computed in\nANALYZE.\n\n### CRITICAL: No-UPL System Behavior\n\nWhen `discovery.dataSources.UPL.available = false`, the usage-based rules\nchange fundamentally:\n\n**Rules 1-4 (active usage) CANNOT fire** — they require runtime data.\n**Rules 10-15 (dormant/retire based on zero usage) CANNOT fire** — \"zero\nruntime\" means \"no data,\" not \"not used.\" Classifying as DORMANT when we\nsimply have no measurement is dishonest.\n\nOn no-UPL systems, usage status for programs/classes is:\n- `UNVERIFIED` — default when no UPL data. Honest: \"we don't know.\"\n- `BATCH_ACTIVE` — if batch job data confirms execution (batch IS available)\n- `HAS_CALLERS` — if cross-reference found callers (static evidence)\n- `ACTIVE` — only if user imports PRD usage data confirming execution\n\nDo NOT classify any program as `DORMANT` or `RETIRE_CANDIDATE` solely because\nUPL shows zero. UPL doesn't exist on this system — zero is not a measurement,\nit's an absence of data.\n\n**Rules 5-7 (enhancement/CMOD) still fire** — structural, not runtime.\n**Rule 0b (RFC) still fires** — structural.\n**Rule 17b (RAP objects) still fires** — architectural.\n**Rules 8-9 (transitive) still fire** but callers may be UNVERIFIED too.\n\n**RAP behavior pool classes** (classes with `RAP_STACK` tag or whose name\nmatches a known behavior pool pattern like `ZBP_*`) are RAP framework objects.\nClassify them as `CLEAN` with `CLOUD_READY` via Rule 17b, NOT as `UNKNOWN`\nvia Rule 8.\n\n### Session Resume\n\nIf some objects already have classifications in objects.json, skip those. Resume\nfrom the first unclassified object. Check each object for a `classification`\nfield — if present and non-null, treat it as already classified.\n\n### Entry Criteria\n\nBefore starting CLASSIFY, verify:\n- ANALYZE phase is complete (`currentPhase` is `\"ANALYZE\"` in project.json)\n- Usage data populated: `usage.uplRuns` exists on program-type objects\n- ATC findings populated: `atc` field exists on objects that were scanned\n- Clean Core data populated: `cleanCore` field exists on objects with\n dependency data\n\nIf any of these are missing, stop and print:\n```\nCLASSIFY cannot start — ANALYZE phase incomplete.\nMissing: {list of missing data categories}\nRun ANALYZE first.\n```\n\n### Classification Output Format\n\nFor each object, write these fields into objects.json:\n\n```json\n{\n \"classification\": {\n \"primary\": \"QUICK_FIX\",\n \"secondary\": [\"STALE_USAGE\", \"HAS_DATA(45000)\"],\n \"rule\": \"Rule 3\",\n \"evidence\": \"UPL=12000, 2 P2 ATC findings (quick-win fixable)\",\n \"classifiedAt\": \"2026-04-09T14:30:00Z\"\n }\n}\n```\n\nFields:\n- `primary` — one of: `CLEAN`, `QUICK_FIX`, `REFACTOR`, `REDESIGN`,\n `RETIRE_CANDIDATE`, `DORMANT`, `BATCH_ACTIVE`, `INTERFACE_VERIFY`,\n `UNKNOWN`\n- `secondary` — array of zero or more tags: `REGULATED`, `QUALITY_REVIEW`,\n `STALE_USAGE`, `STALE_TRANSITIVE`, `HAS_DATA({n})`, `RFC_EXTERNAL`\n- `rule` — which rule determined the primary classification\n- `evidence` — human-readable string explaining WHY this classification was\n chosen, including key data points (UPL count, ATC counts, caller names)\n- `classifiedAt` — ISO timestamp of when classification was applied\n\n### Rule Evaluation Order\n\nEvaluate rules in strict priority order. Once a primary classification is\nassigned, stop evaluating further rules for that object (secondary tags may\nstill be added by earlier rules).\n\nPriority order:\n1. Rule 0a — GXP/SOX compliance gate (can override RETIRE to DORMANT)\n2. Rule 0a2 — QUALITY compliance gate (can override RETIRE to DORMANT)\n3. Rule 0b — RFC-enabled FM with zero internal callers\n4. Rule 0c — Stale UPL (skips Rules 1-4 but NOT 5-7)\n5. Rules 1-7 — Active usage and enhancement rules (detailed in Part 2)\n6. Rule 8 — Transitive classification for classes\n7. Rule 9 — DDIC classification by consumers\n8. Rules 10-15 — Batch, dormant, retirement rules (detailed in Part 2)\n\n---\n\n### Rule 0a: GXP/SOX Compliance Gate — Highest Priority\n\nCheck the object's `compliance` field. This field is inherited from the\nobject's package (set during INVENTORY) or from an object-level override.\n\nIf compliance is `GXP` or `SOX`:\n- The object is NEVER classified as `RETIRE_CANDIDATE` regardless of what\n other rules determine.\n- If technical rules (Rules 10-15) would yield `RETIRE_CANDIDATE`, override\n primary to `DORMANT`.\n- Add secondary tag: `REGULATED`\n- Set evidence: `\"Package {pkg} is tagged {GXP/SOX}. Retirement requires formal change control. Cannot auto-classify as retirement candidate.\"`\n\nApply Rule 0a FIRST, before all other rules. After all other rules have run,\ncheck the result: if primary is `RETIRE_CANDIDATE` and the object has GXP/SOX\ncompliance, force primary to `DORMANT` and add the `REGULATED` tag.\n\nImplementation approach: run all rules normally, then apply Rule 0a as a\npost-filter. This is simpler than checking compliance inside every rule.\n\n```\npseudocode:\n classify(object)\n if object.compliance in ['GXP', 'SOX'] AND object.classification.primary == 'RETIRE_CANDIDATE':\n object.classification.primary = 'DORMANT'\n object.classification.secondary.append('REGULATED')\n object.classification.evidence += ' [Rule 0a override: regulated package]'\n```\n\n---\n\n### Rule 0a2: QUALITY Compliance Gate\n\nIf compliance is `QUALITY`:\n- Object gets extra scrutiny but is NOT as strictly protected as GXP/SOX.\n- If technical rules would yield `RETIRE_CANDIDATE`, override primary to\n `DORMANT`.\n- Add secondary tag: `QUALITY_REVIEW`\n- Normal classifications (`CLEAN`, `REFACTOR`, `QUICK_FIX`, `REDESIGN`,\n `BATCH_ACTIVE`, etc.) are NOT affected — only `RETIRE_CANDIDATE` is blocked.\n\nApply as a post-filter alongside Rule 0a:\n\n```\npseudocode:\n if object.compliance == 'QUALITY' AND object.classification.primary == 'RETIRE_CANDIDATE':\n object.classification.primary = 'DORMANT'\n object.classification.secondary.append('QUALITY_REVIEW')\n object.classification.evidence += ' [Rule 0a2 override: quality-controlled package]'\n```\n\n---\n\n### Rule 0b: RFC-Enabled FM with Zero Internal Callers — Second Highest Priority\n\nCheck: `isRFCEnabled == true` AND cross-reference data shows zero callers\nwithin the SAP system.\n\nIf true:\n- Primary: `INTERFACE_VERIFY`\n- Add secondary tag: `RFC_EXTERNAL`\n- NEVER auto-RETIRE — external systems (middleware, third-party apps, other\n SAP systems) may call this FM and those calls do not appear in UPL or\n cross-reference data.\n- Evidence: `\"RFC-enabled, zero SAP callers — may be called by external systems. Confirm with integration team.\"`\n\nIf the RFC-enabled FM HAS internal callers (cross-reference shows > 0 callers),\nskip this rule and proceed to normal classification via remaining rules. The\nRFC nature is not special when internal callers exist.\n\nEvaluate Rule 0b BEFORE Rules 1-7. If Rule 0b assigns `INTERFACE_VERIFY`,\nstop — do not evaluate further rules for this object.\n\n---\n\n### Rule 0c: Stale UPL Data\n\nCheck: `usage.uplRuns > 0` AND `usage.lastRunDate` is more than 12 months\nbefore today's date.\n\nIf true:\n- The UPL data is historical, not reflecting current runtime behavior.\n- Add secondary tag: `STALE_USAGE`\n- **Skip Rules 1-4** — these are active-usage rules that require recent\n runtime data. Applying them to stale UPL would produce misleading\n classifications.\n- **Do NOT skip Rules 5-7** — enhancement rules (BAdI active, CMOD exit\n active, enhancement spot implementations). Enhancement status is\n STRUCTURAL, registered in SXC_ATTR/MODATTR, and does not depend on runtime\n execution. An active BAdI with stale UPL is still an active BAdI. Skipping\n Rules 5-7 would misclassify enhancement objects as DORMANT or\n RETIRE_CANDIDATE.\n- After Rules 5-7, fall through to Rules 10-15 (batch/dormant/retire rules).\n\nIf `usage.uplRuns == 0` (no UPL data at all), Rule 0c does not apply — that\nsituation is handled by Rule 14 (no usage data → UNKNOWN).\n\n```\npseudocode:\n if object.usage.uplRuns > 0 AND monthsSince(object.usage.lastRunDate) > 12:\n object.classification.secondary.append('STALE_USAGE')\n skip_rules = [1, 2, 3, 4] # skip active-usage rules\n # Rules 5-7 still apply\n # Then fall through to Rules 10-15\n```\n\n---\n\n### Rule 8: Transitive Classification for Classes\n\nClasses do not have direct UPL data. Their CALLERS do. Determine class usage\ntransitively from caller classifications.\n\n**Prerequisite:** All non-class objects must be classified first. Process\nclasses AFTER all programs, function modules, and other directly-classifiable\nobjects have been classified. This ensures caller classifications are available\nfor transitive lookup.\n\n**Procedure:**\n\n1. For each class WITHOUT an existing classification AND that was not already\n classified by Rules 5-7 (enhancement classes):\n\n2. Retrieve the class's callers from cross-reference data collected in\n ANALYZE Tier 1.\n\n3. Check each caller's CLASSIFICATION (not raw UPL data):\n\n | Caller mix | Transitive result |\n |---|---|\n | ANY caller is `CLEAN`, `QUICK_FIX`, `REFACTOR`, `REDESIGN`, or `BATCH_ACTIVE` | Class is transitively USED |\n | ALL callers are `RETIRE_CANDIDATE` | Class is transitively RETIRE-able |\n | ALL callers are `DORMANT` (including those with `STALE_USAGE`) | Class inherits `DORMANT` with secondary tag `STALE_TRANSITIVE` |\n | No callers found in cross-reference | Class is `UNKNOWN` |\n\n4. After determining transitive usage status, apply the class's own ATC\n findings to select the specific primary classification:\n\n - Transitively USED + 0 ATC findings → `CLEAN`\n - Transitively USED + only P2 quick-win findings → `QUICK_FIX`\n - Transitively USED + P1 findings or many P2 → `REFACTOR`\n - Transitively USED + structural issues → `REDESIGN`\n - Transitively RETIRE-able → `RETIRE_CANDIDATE`\n - Transitively DORMANT → `DORMANT`\n - No callers → `UNKNOWN`\n\n**Why use caller CLASSIFICATION, not raw UPL:** Rule 0c marks programs with\nstale UPL as `DORMANT`. If Rule 8 checked raw UPL (> 0), it would see \"caller\nhas usage\" and classify the class as actively USED — even though the caller\nitself is `DORMANT` due to stale data. Using the caller's classification\nensures consistency: a class is never rated higher than its callers.\n\n**Evidence format:**\n\n```\n\"Transitive — called by {classification} program {name} (UPL={n}). Own ATC: {n} P1, {n} P2.\"\n```\n\nExample:\n```\nZCL_ORDER_PROCESSOR:\n Callers: ZSD_ORDER_ENTRY (CLEAN, UPL=89000), ZSD_BATCH_ORDER (BATCH_ACTIVE)\n → Transitive: USED (caller ZSD_ORDER_ENTRY is CLEAN)\n → Own ATC: 2 P2 findings (quick-win)\n → Primary: QUICK_FIX\n → Rule: Rule 8\n → Evidence: \"Transitive — called by CLEAN program ZSD_ORDER_ENTRY (UPL=89000). Own ATC: 0 P1, 2 P2.\"\n```\n\n**Circular references:** If class A calls class B and class B calls class A,\nand neither has any non-class callers, classify both as `UNKNOWN` with\nevidence noting the circular dependency. Do not recurse indefinitely.\n\n---\n\n### Rule 9: DDIC Classification by Consumers\n\nTables, data elements, and domains are classified by their consuming programs.\nConsumer lists were collected via D010TAB and DD03L during INVENTORY.\n\n**Simple rule: If ANY consumer is NOT `RETIRE_CANDIDATE`, the DDIC object is\nCLEAN.** Period. No exceptions.\n\n`UNKNOWN` is NOT `RETIRE_CANDIDATE`. `DORMANT` is NOT `RETIRE_CANDIDATE`.\nOnly the literal classification `RETIRE_CANDIDATE` counts. Everything else —\n`CLEAN`, `DORMANT`, `UNKNOWN`, `INTERFACE_VERIFY`, `BATCH_ACTIVE`, `REFACTOR`,\n`REDESIGN`, `QUICK_FIX`, `TEST` — means the DDIC object is needed and stays\n`CLEAN`.\n\nDDIC objects are infrastructure; removing them while any program still\nreferences them causes syntax errors.\n\n| Consumer mix | DDIC classification |\n|---|---|\n| ALL consumers are literally `RETIRE_CANDIDATE` | `RETIRE_CANDIDATE` |\n| ANY consumer has ANY other classification (including `UNKNOWN` or `DORMANT`) | `CLEAN` |\n| No consumers found in D010TAB/DD03L | `UNKNOWN` |\n\n**Common mistake:** Do NOT classify a table as `UNKNOWN` just because its\nconsumers are `UNKNOWN`. A consumer exists — that means the table is referenced\nby code. `UNKNOWN` consumers are still consumers. Only classify as `UNKNOWN`\nwhen the consumer list is EMPTY (zero rows from D010TAB/DD03L).\n\nEvidence format:\n```\n\"Used by {n} programs. {n} RETIRE_CANDIDATE, {n} active. Needed by active code.\"\n```\nor:\n```\n\"All {n} consumers are RETIRE_CANDIDATE. Safe to retire with consumers.\"\n```\n\n**Table data volume check — execute during CLASSIFY, not deferred:**\n\nFor every custom table (object type `TABL`) classified as `RETIRE_CANDIDATE`,\nimmediately run a row count query:\n\n```sql\nSELECT COUNT(*) FROM {table_name}\n```\n\nUse `sap_sql_query` to execute this. If the query fails (table does not exist\nin DB, authorization error), note the failure in evidence and add secondary\ntag `DATA_CHECK_FAILED`.\n\nIf rows > 0:\n- Add secondary tag: `HAS_DATA({row_count})`\n- Update evidence: append `\"Table has {n} rows — archive decision needed before retirement.\"`\n\nIf rows == 0:\n- No additional tag needed. Evidence: append `\"Table is empty — no archive needed.\"`\n\nThis surfaces the data issue in the assessment report immediately, rather than\ndeferring it to a RETIRE phase that may never execute. Functional leads need\nto see data volume to make informed archive-or-delete decisions.\n\n**Performance note:** Only query tables classified as `RETIRE_CANDIDATE`. Do\nnot query tables classified as `CLEAN` or `UNKNOWN` — their row counts are\nirrelevant to the assessment.\n\n---\n\n### CLASSIFY Progress Tracking\n\nAfter processing each batch of objects (or every 50 objects, whichever is\nsmaller), print a progress line:\n\n```\nCLASSIFY: {n}/{total} objects classified. {clean} CLEAN, {qf} QUICK_FIX, {ref} REFACTOR, {red} REDESIGN, {ret} RETIRE, {dor} DORMANT, {bat} BATCH, {ifc} INTERFACE_VERIFY, {unk} UNKNOWN.\n```\n\nSave objects.json after each batch to enable session resume.\n\n---\n\n### Full Classification Rules Table\n\nApply these 20 rules in the execution order specified in the next section. Each\nrule has a condition, a primary classification, optional secondary tags, and\nnotes.\n\n| # | Rule | Condition | Primary | Secondary | Notes |\n|---|------|-----------|---------|-----------|-------|\n| 0a | GXP/SOX gate | compliance = GXP or SOX | (keep technical primary) | REGULATED | NEVER RETIRE_CANDIDATE. If other rules yield RETIRE_CANDIDATE, override to DORMANT. |\n| 0a2 | QUALITY gate | compliance = QUALITY | (keep technical primary) | QUALITY_REVIEW | DORMANT instead of RETIRE_CANDIDATE. Other primaries unchanged. |\n| 0b | RFC external | isRFCEnabled = true AND zero internal callers | INTERFACE_VERIFY | — | Never auto-RETIRE. |\n| 0c | Stale UPL | uplRuns > 0 AND lastRunDate > 12 months ago | (skip to Rules 5-7, then 10-15) | STALE_USAGE | Skip Rules 1-4 but NOT Rules 5-7. |\n| 1 | Active, clean | Runtime > 0 AND lastRun within 12 months, zero ATC findings | CLEAN | — | Best case. |\n| 2 | Active, quick-win | Runtime > 0 AND lastRun within 12 months, ATC findings are all quick-win (pragma suppression, minor fixes) | QUICK_FIX | — | Estimated 5 min per finding. |\n| 3 | Active, table replacement | Runtime > 0 AND lastRun within 12 months, ATC findings include table replacement (BSEG, KONV, VBUK etc.) | REFACTOR | HAS_ATC_FINDINGS | Estimated 2-4 hours per object. |\n| 4 | Active, functional redesign | Runtime > 0 AND lastRun within 12 months, ATC findings require functional redesign | REDESIGN | HAS_ATC_FINDINGS | Estimated 1-3 days per object. |\n| 5 | Active BAdI with ATC | isEnhancement = true AND has ATC findings | REFACTOR | WIRED_IN | Enhancement is structurally active. |\n| 6 | Active BAdI clean | isEnhancement = true AND no ATC findings | CLEAN | WIRED_IN | Enhancement is active and compatible. |\n| 7 | CMOD exit | isCMODExit = true | CLEAN | WIRED_IN, CMOD | CMOD exits are structural. |\n| 8 | Class transitive | Class with zero UPL, classified via callers | (transitive — see Rule 8 logic) | — | Use caller's classification status. |\n| 9 | DDIC transitive | Table/DTEL/DOMA classified via consumers | (transitive — see Rule 9 logic) | — | Use consumer's classification status. |\n| 10 | Batch active | Zero runtime, batch jobs in last 12 months | BATCH_ACTIVE | — | Job scheduler confirms usage. |\n| 11 | Stale batch (12-15mo) | Zero runtime, batch jobs older than 12 months but within 15 months | DORMANT | STALE_BATCH, CHECK_ANNUAL | May be quarterly/annual job. |\n| 11b | Stale batch (>15mo) | Zero runtime, batch jobs older than 15 months | DORMANT | STALE_BATCH | Likely genuinely stale. |\n| 12 | Has callers | Zero runtime, has static callers (from cross-ref), no batch | DORMANT | HAS_CALLERS | Code references exist but no execution. |\n| 13 | Recently changed | Zero runtime, zero callers, changed <= 12 months ago | DORMANT | RECENTLY_CHANGED | Developer recently touched it. If object has P1 ATC findings, add secondary tag HAS_ATC_P1 — surfaces in report as \"needs fix IF confirmed used.\" |\n| 14 | Old and unused | Zero runtime, zero callers, changed > 36 months ago | RETIRE_CANDIDATE | — | Candidate only — human confirms. |\n| 15 | Medium-age unused | Zero runtime, zero callers, changed 13-36 months ago | DORMANT | VERIFY | Gray zone — needs human input. |\n| 16 | In development | Object in active transport (from E071/E070 check) | (see note) | IN_DEVELOPMENT | Applied AFTER all other rules. If primary is RETIRE_CANDIDATE, override to DORMANT. All other primaries unchanged. |\n| 17 | Test object | SUBC = 'T' or name ends with *_TEST | TEST | SKIP_MIGRATION | Auto-detected in INVENTORY. |\n| 17b | RAP framework object | Object type is BDEF, DDLS, DDLX, DCLS, SRVD, SRVB, STOB, G4BA, OR class is a RAP behavior pool (ZBP_* with RAP_STACK tag) | CLEAN | CLOUD_READY | RAP objects ARE the ABAP Cloud target architecture. Cloud-ready by construction. Never classify as UNKNOWN. Includes behavior pool classes. |\n| 18 | No data | No usage, no callers, no ATC, no batch, not a RAP object — nothing | UNKNOWN | NEEDS_REVIEW | Truly unanalyzable. Rule 17b must be checked first. |\n\n---\n\n### Classification Execution Order\n\nThe order in which objects are classified matters because transitive rules (8,\n9) depend on their inputs being classified first. Follow this order exactly.\n\n**Step 1: Classify PROGRAMS, FUNCTION MODULES, and ENHANCEMENT CLASSES first.**\n\nApply Rules 0a-7 and 10-18 to:\n- All PROG objects.\n- All FUNC objects.\n- All CLAS objects where `isEnhancement = true` or `isCMODExit = true`.\n\nEnhancement classes (BAdI implementations, CMOD exit classes) are structurally\nwired into SAP standard processing. Evaluate them via Rules 5-7, NOT via\ntransitive Rule 8.\n\n**Step 2: Classify REMAINING CLASSES second (Rule 8 — transitive from callers).**\n\nOnly classes NOT already classified in Step 1. These are regular classes without\nenhancement flags.\n\nWithin Step 2, classify in dependency order (topological sort on the\nclass-calls-class graph):\n- Leaf classes (only called by programs/FMs from Step 1) first.\n- Classes that call other classes: classify AFTER their callees.\n- If circular dependencies exist (A calls B calls A), treat the cycle as a\n group:\n - If ANY class in the cycle has a non-class caller that is actively used,\n the entire cycle is USED.\n - If no external callers, the entire cycle is UNKNOWN.\n\n**Step 3: Classify DDIC objects last (Rule 9 — transitive from consumers).**\n\nWithin Step 3, classify in this order:\n1. TABLES first (their consumers are programs/classes from Steps 1-2).\n2. DATA ELEMENTS second (their consumers include tables, now classified).\n3. DOMAINS last (their consumers include data elements, now classified).\n\n**Step 4: Apply Rule 16 (IN_DEVELOPMENT) as a final pass across ALL objects.**\n\nCheck every object against the transport lock data. If an object is in an\nactive transport AND its primary classification is `RETIRE_CANDIDATE`, override\nto `DORMANT` with `IN_DEVELOPMENT` secondary tag.\n\n**Step 5: Apply Rule 17 (TEST) as a final pass.**\n\nObjects flagged as `isTestObject = true` during INVENTORY get `TEST` primary\nand `SKIP_MIGRATION` secondary, regardless of other rules.\n\n**Step 6: Apply compliance gates (Rules 0a, 0a2) as a final post-filter.**\n\nScan all classified objects. Any `RETIRE_CANDIDATE` in a GXP/SOX package:\noverride to `DORMANT` + `REGULATED`. Any `RETIRE_CANDIDATE` in a QUALITY\npackage: override to `DORMANT` + `QUALITY_REVIEW`.\n\n**Step 7: Tag DORMANT objects with P1 ATC findings.**\n\nScan all objects classified as `DORMANT` (any secondary tag). If the object has\nany ATC finding with priority = \"1\", add secondary tag `HAS_ATC_P1`. This\nensures the report surfaces: \"If confirmed used, these objects need code changes.\"\nThe management bucket \"Needs code changes\" should include a footnote:\n`\"Plus {n} DORMANT objects with P1 findings that need fixes if confirmed used.\"`\n\n**Step 8: Recognize RAP stacks as units.**\n\nAfter all objects are classified, detect RAP stacks: groups of objects sharing\nthe same root entity name across types (TABL, DDLS, BDEF, CLAS, SRVD, SRVB,\nDDLX, DCLS, STOB). If a group of 3+ objects shares a common root name (e.g.,\n`TCRSCOURSE` appears in `ZTCRS_COURSE`, `ZI_TCRSCOURSE`, `ZC_TCRSCOURSE`,\n`ZBP_TCRSCOURSE`, etc.), add secondary tag `RAP_STACK:{root_name}` to each.\nIn the report, list RAP stacks as units with one classification per stack\n(use the most significant classification in the group).\n\n---\n\n### Vocabulary Translation Table\n\nMap internal classification codes to business-friendly language for worksheets.\nWorksheets NEVER show raw internal codes.\n\n| Internal Code | Worksheet Shows |\n|---|---|\n| CLEAN | No changes needed |\n| QUICK_FIX | Minor technical changes needed |\n| REFACTOR | Code changes required for S/4HANA |\n| REDESIGN | Significant rework — business decision required |\n| RETIRE_CANDIDATE | Candidate for removal — please confirm |\n| BATCH_ACTIVE | Runs in background — keep |\n| INTERFACE_VERIFY | Verify with integration team — may be called externally |\n| DORMANT | Not recently used — please verify |\n| UNKNOWN | Needs your input |\n| TEST | Test object — skip |\n\nStore internal codes in objects.json. When generating any worksheet or report\noutput, translate using this table. Never expose raw codes like\n`INTERFACE_VERIFY` or `RETIRE_CANDIDATE` to business users.\n\n---\n\n### Per-Object Confidence\n\nAssign a confidence level to each object based on the quality of available data.\nStore in `classification.confidence` for each object in objects.json.\n\n| Data Available | Object Confidence |\n|---|---|\n| UPL data for this object | HIGH |\n| No UPL but transitive (class with classified callers) | MEDIUM |\n| Batch job data only | MEDIUM |\n| Cross-reference only (static callers, no runtime) | LOW |\n| RAP framework object (cloud-ready by construction) | MEDIUM |\n| ATC findings available (even without UPL) | LOW |\n| No data at all | NONE |\n\n**Project confidence display:**\n\nIf UPL is available: show percentage `\"{pct}% (HIGH+MEDIUM)\"`\n\nIf UPL is NOT available (discovery.dataSources.UPL.available = false):\nDo NOT show a percentage — it will always be near 0% and misleads the user.\nInstead show:\n\n```\nClassification basis: Static analysis only (no runtime usage data)\n - ATC findings: {n} objects checked\n - Clean Core: {n} objects checked\n - Cross-references: {n} objects checked\n - RAP objects: {n} (cloud-ready by construction)\n```\n\nThis tells the user WHAT the classification is based on, not a meaningless\npercentage.\n\n---\n\n### CLASSIFY Phase Completion\n\nWhen all objects are classified, print a TWO-PART summary showing both\ndimensions separately. This is the key insight: technical readiness and\nusage status are independent questions.\n\n```\nCLASSIFY complete.\n\nTECHNICAL READINESS (will this break on S/4HANA?):\n Cloud-ready: {n} ({pct}%) — RAP/ABAP Cloud, no changes needed\n Clean: {n} ({pct}%) — no compatibility issues\n Minor fix: {n} ({pct}%) — small changes (P2/P3 findings)\n Needs fix: {n} ({pct}%) — table replacements, unreleased APIs\n Needs redesign: {n} ({pct}%) — significant rework\n Not checked: {n} ({pct}%) — ATC didn't run\n\nUSAGE STATUS (should we keep or retire?):\n {If UPL available:}\n Active: {n} ({pct}%) — confirmed by UPL/batch\n Dormant: {n} ({pct}%) — evidence suggests not used\n Retire candidate:{n} ({pct}%) — confirmed unused >36 months\n Interface: {n} ({pct}%) — RFC, verify with integration team\n {If UPL NOT available:}\n Unverified: {n} (100%) — no runtime data on this system\n\n Usage cannot be determined without production runtime data.\n Technical readiness above is fully assessed regardless.\n\nCOMBINED RECOMMENDATION:\n No work needed: {n} ({pct}%) — technically clean (cloud-ready + clean)\n Needs code changes: {n} ({pct}%) — S/4 incompatibilities found\n Can be retired: {n} ({pct}%) — confirmed unused (requires UPL)\n Usage unverified: {n} ({pct}%) — technically clean, usage unknown\n\n**COMBINED bucket mapping:**\n- \"No work needed\" = technicalReadiness in (CLOUD_READY, CLEAN) AND usageStatus != RETIRE_CANDIDATE\n- \"Needs code changes\" = technicalReadiness in (MINOR_FIX, NEEDS_FIX, NEEDS_REDESIGN). Count these\n even if usageStatus is UNVERIFIED — they need fixing IF the system migrates to S/4.\n- \"Can be retired\" = usageStatus = RETIRE_CANDIDATE (only possible with UPL data)\n- \"Usage unverified\" = usageStatus = UNVERIFIED AND technicalReadiness in (CLEAN, CLOUD_READY)\n These are technically fine but we don't know if they're used.\n```\n\nPhase complete.\n\n### What Happens Next — Clear Direction\n\n### Report Generation (AUTOMATIC — not optional)\n\nThe report is the primary deliverable of CCA. It is NOT one of several options.\nAfter CLASSIFY completes, ALWAYS generate the report immediately. Move to the\nREPORT phase, produce the assessment report, save it, then present next steps.\n\nDo NOT ask \"do you want a report?\" — just generate it.\n\n### After Report — Next Steps\n\nAfter the report is generated and saved, present tailored next steps based on\nfindings. Lead with what the data tells you.\n\n```\nOutputs:\n - Open in the viewer -> <{id}-cca-assessment-{date}.cspeach.json> (the saved envelope)\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\n\n{Show the key findings summary from the report}\n\n---\nWhat's next?\n\n{If objects with NEEDS_FIX or NEEDS_REDESIGN exist:}\n >> {n} objects need code changes for S/4HANA:\n {list object names with specific issues — BSEG, KONV, etc.}\n\n{If UPL not available:}\n >> Usage is unknown for {n} objects — cannot determine keep vs retire\n without production runtime data.\n\n{If RAP stack detected:}\n >> RAP stack {name} ({n} objects) — clean, cloud-ready, no action needed.\n\nYour options:\n 1. Send worksheets to functional leads for usage verification\n 2. Start fixing the {n} objects with S/4 issues (/abap-upgrade-fix)\n 3. Done for now — resume later with /abap-cca\n\n (You can also ask me to export raw data as CSV at any time)\n```\n\nIf user picks 1 → move to REVIEW phase (worksheets).\nIf user picks 2 → generate fix-baseline.json, chain to /abap-upgrade-fix.\nIf user picks 3 → save state, print resume instructions, done.\n\n**Flow after worksheets come back:**\n```\nUser returns → /abap-cca → resume project → IMPORT phase\n → Import worksheet responses → Reclassify with usage decisions\n → Updated report (automatic) → Offer /abap-upgrade-fix for confirmed FIX objects\n```\n\n**Upload/Download summary — present to user if they ask:**\n\n| Direction | What | When | Format |\n|---|---|---|---|\n| Out | Assessment report | After CLASSIFY (automatic) | Markdown |\n| Out | Review worksheets | When user requests | CSV |\n| Out | Raw data export | Anytime on request | CSV/JSON |\n| In | PRD usage data | After DISCOVER if no UPL | CSV |\n| In | Worksheet responses | After leads fill worksheets | CSV |\n\n---\n\n## REVIEW Phase — Worksheets for Functional Leads\n\n### Purpose\n\nGenerate review worksheets as CSV files for functional leads to review and make decisions on classified objects. Worksheets use business-friendly vocabulary, not internal codes.\n\n### Session Resume\n\nIf `progress.review.worksheetsGenerated = true`, skip generation. Offer to regenerate or proceed to IMPORT.\n\n### Entry Criteria\n\nCLASSIFY phase complete. All objects have classifications in objects.json.\n\n---\n\n### Step 1: Module Assignment (Interactive)\n\nBefore generating worksheets, ask the user to assign packages to functional modules. This determines how worksheets are split.\n\nPresent each package found in objects.json and suggest a module based on the package name:\n\n```\nAssign packages to modules for worksheet generation:\n ZCUSTOM_FI → FI (your suggestion / type your own)\n ZCUSTOM_SD → SD\n ZPROJECT_ALPHA → ? (please assign)\n```\n\nWait for user response. The user assigns each package to a module name. Store the mapping in project.json under `review.moduleAssignment`:\n\n```json\n{\n \"review\": {\n \"moduleAssignment\": {\n \"ZCUSTOM_FI\": \"FI\",\n \"ZCUSTOM_SD\": \"SD\",\n \"ZPROJECT_ALPHA\": \"PP\"\n }\n }\n}\n```\n\nGenerate one worksheet per module. Objects from packages assigned to the same module appear in the same worksheet.\n\n---\n\n### Step 2: Transaction Code Enrichment\n\nBefore generating worksheets, query TSTC to map programs to transaction codes. Run per namespace prefix:\n\n```sql\nSELECT TCODE, PGMNA FROM TSTC WHERE PGMNA LIKE '{prefix}%'\n```\n\nThis adds a \"Transaction\" column so functional leads see `ZFD01 — Food Inventory Aging Report` instead of just `ZFI_AGING_REPORT (PROG)`.\n\nAlso query program titles for description enrichment:\n\n```sql\nSELECT NAME, DESCRIPT FROM TRDIR WHERE NAME IN (...)\n```\n\nUse the program names from the current module's objects. Batch in groups of 50 to stay under query limits. Cache results across modules to avoid duplicate queries.\n\n---\n\n### Step 3: Generate Worksheet CSV Files\n\nGenerate CSV files with comma delimiter. 13 columns per row. Quote all values.\n\n#### Header Row\n\n```csv\n\"Object Name\",\"Transaction\",\"Description\",\"Object Type\",\"Package\",\"Last Changed\",\"Technical Issues\",\"Recommended Action\",\"Regulatory Status\",\"RFC/External?\",\"Still Used? (Y/N)\",\"Your Decision (Keep/Retire/Fix/Redesign)\",\"Comments\"\n```\n\n#### Column Specifications\n\n| Column | Source | Notes |\n|---|---|---|\n| Object Name | object key from objects.json | For function modules show: `\"ZINT_MES_BATCH_SEND (FUGR: ZINT_MES)\"` |\n| Transaction | TSTC lookup | Blank if no t-code mapped |\n| Description | TRDIR program title or class description | Best effort; blank if unavailable |\n| Object Type | objectType | PROG, CLAS, FUNC, TABL, etc. |\n| Package | package | From objects.json |\n| Last Changed | lastChangedDate | Format: YYYY-MM-DD |\n| Technical Issues | ATC finding count + summary | E.g., `\"1 issue (table replacement)\"` or `\"0\"` |\n| Recommended Action | VOCABULARY TRANSLATED primary classification | See translation table below |\n| Regulatory Status | compliance tag | GXP, SOX, QUALITY, or NONE |\n| RFC/External? | isRFCEnabled | `\"YES - RFC enabled\"` with warning, or `\"No\"` |\n| Still Used? (Y/N) | BLANK — for lead to fill | Pre-populate only if evidence is overwhelming |\n| Your Decision | BLANK — for lead to fill | Options: Keep / Retire / Fix / Redesign |\n| Comments | Pre-filled warnings | See pre-fill rules below |\n\n#### Classification Vocabulary Translation\n\nTranslate internal classification codes to business-friendly language in the \"Recommended Action\" column:\n\n| Internal Classification | Worksheet Text |\n|---|---|\n| CLEAN | No changes needed |\n| QUICK_FIX | Minor technical changes needed |\n| REFACTOR | Code changes required for S/4HANA |\n| REDESIGN | Significant rework — business decision required |\n| RETIRE_CANDIDATE | Candidate for removal — please confirm |\n| BATCH_ACTIVE | Runs in background — keep |\n| INTERFACE_VERIFY | Verify with integration team — may be called externally |\n| DORMANT | Not recently used — please verify |\n| TEST | Test object — skip |\n| UNKNOWN | Needs your input |\n\n#### Pre-filled Comments Rules\n\nApply these rules in order. Concatenate multiple applicable comments with ` | ` separator:\n\n- For GXP objects: `\"GxP validated - formal change control required\"`\n- For SOX objects: `\"SOX controlled - sign-off required\"`\n- For RFC-enabled function modules: `\"WARNING: External systems may call this\"`\n- For RETIRE_CANDIDATE tables with data: `\"Table has {n} rows — archive decision needed\"`\n- For INTERFACE_VERIFY objects: `\"May have external consumers — confirm with integration team\"`\n- For STALE_USAGE objects: `\"Last executed {date} — usage may be historical\"`\n- Otherwise: blank\n\n#### Sample Rows\n\n```csv\n\"Object Name\",\"Transaction\",\"Description\",\"Object Type\",\"Package\",\"Last Changed\",\"Technical Issues\",\"Recommended Action\",\"Regulatory Status\",\"RFC/External?\",\"Still Used? (Y/N)\",\"Your Decision (Keep/Retire/Fix/Redesign)\",\"Comments\"\n\"ZFI_AGING_REPORT\",\"ZFIA1\",\"FI Aging Report\",\"PROG\",\"ZCUSTOM_FI\",\"2023-04-12\",\"1 issue (table replacement)\",\"Code changes required for S/4HANA\",\"NONE\",\"No\",\"\",\"\",\"\"\n\"ZINT_LIMS_BATCH_SEND (FUGR: ZINT_RFC)\",\"\",\"LIMS Batch Result Send\",\"FUNC\",\"ZINT_RFC\",\"2024-01-15\",\"0\",\"Verify with integration team — may be called externally\",\"NONE\",\"YES - RFC enabled\",\"\",\"\",\"WARNING: External systems may call this\"\n\"ZQM_BATCH_RELEASE\",\"ZQM01\",\"QM Batch Release\",\"PROG\",\"ZPHARMA_QM\",\"2022-08-10\",\"0\",\"No changes needed\",\"GXP\",\"No\",\"\",\"\",\"GxP validated - formal change control required\"\n```\n\n---\n\n### Step 4: Write Worksheet Files\n\nWrite CSV files to: `.cspeach/cca/projects/{id}/worksheets/`\n\nCreate the directory if it does not exist. Name files by module: `worksheet_{module}.csv` (e.g., `worksheet_FI.csv`, `worksheet_SD.csv`). Use lowercase module name in the filename.\n\nAfter writing all files, report the absolute file paths:\n\n```\nWorksheets generated:\n .cspeach/cca/projects/{id}/worksheets/worksheet_fi.csv ({n} objects)\n .cspeach/cca/projects/{id}/worksheets/worksheet_sd.csv ({n} objects)\n\nDistribute these to functional leads. They should fill in:\n - \"Still Used?\" column (Y or N)\n - \"Your Decision\" column (Keep, Retire, Fix, or Redesign)\n - \"Comments\" for any context\n\nWhen responses come back, use /abap-cca to import them.\n```\n\nUpdate project.json:\n- Set `progress.review.worksheetsGenerated = true`\n- List modules in `progress.review.responsesPending` (array of module names)\n- Record `progress.review.generatedAt` with current timestamp\n\n---\n\n### \"Generate Report NOW\" Escape\n\nAt any phase — DISCOVER, INVENTORY, ANALYZE, CLASSIFY, or REVIEW — the user can say \"give me the report now\" or \"generate report NOW.\" When this happens, generate a report with whatever data is available. Jump directly to REPORT phase logic.\n\nPrint a confidence disclaimer:\n\n```\nReport generated with current data.\nConfidence: {LOW/MEDIUM} (analysis incomplete — {n}/{total} objects analyzed)\nCaveat: {X} objects not yet classified. Run /abap-cca to resume full analysis.\n\nOutputs:\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\n```\n\nThis serves users under deadline pressure who need something to show management today, even if incomplete. Do not block or warn excessively — generate the report immediately.\n\n---\n\n### REVIEW Phase Completion\n\nWhen all worksheets are generated, print this summary:\n\n```\nREVIEW complete.\n Worksheets: {n} files generated for {n} modules\n Objects included: {n}\n Awaiting responses from: {module list}\n\nNext steps:\n 1. Distribute worksheets to functional leads\n 2. When responses come back, run /abap-cca to import them\n 3. Or say \"generate report now\" for a report without functional input\n\nPhase complete. Move to IMPORT (when responses are available).\n```\n\n---\n\n## Phase: IMPORT\n\n### Purpose\n\nImport functional lead responses from returned worksheet CSVs. Validate the data, detect conflicts, apply decisions, and update objects.json with human review input.\n\n### Session Resume\n\nIf `progress.review.responsesReceived` already contains modules, those are already imported. Only process new/pending modules.\n\n### Entry Criteria\n\nREVIEW worksheets have been generated and at least one response file is available. The user provides the file path.\n\n---\n\n### 11-Point Validation\n\nWhen the user provides a returned worksheet file, run these 11 validation steps in order.\n\n#### 1. BOM Strip\n\nIf the file starts with a UTF-8 BOM (bytes EF BB BF), strip it before parsing.\n\n#### 2. XLSX Detection\n\nIf the file is binary (starts with PK signature — ZIP/XLSX), error immediately:\n\n```\nThis is an Excel file (.xlsx). Please re-save as CSV:\n File → Save As → CSV (Comma delimited) (*.csv)\n```\n\nDo not attempt to parse XLSX. Stop and ask the user to re-export.\n\n#### 3. Delimiter Detection\n\nAuto-detect the delimiter by checking the header row:\n- Count commas, semicolons, and tabs in the first line\n- Use the most frequent as the delimiter\n- Default to comma if ambiguous\n\n#### 4. Header Matching\n\nMatch columns by header NAME, not position. The lead may have reordered columns. Required headers (case-insensitive, fuzzy match):\n\n| Header | Required? | Purpose |\n|---|---|---|\n| \"Object Name\" | Required — matching key | Links row to objects.json entry |\n| \"Still Used?\" | Optional | Usage confirmation from lead |\n| \"Your Decision\" or \"Decision\" | Optional | Keep/Retire/Fix/Redesign |\n| \"Comments\" | Optional | Free text from lead |\n\nIf \"Object Name\" column is missing, error immediately:\n\n```\nCannot find 'Object Name' column. Available columns: {list}\n```\n\nDo not guess. Stop and report.\n\n#### 5. Value Normalization\n\nNormalize common response values before processing:\n\n**Used column:**\n- Y / Yes / Ja / Oui / 1 / TRUE → `Y`\n- N / No / Nein / 0 / FALSE → `N`\n- blank → `null`\n\n**Decision column:**\n- Keep / keep / KEEP → `KEEP`\n- Retire / retire / RETIRE / Remove / Delete → `RETIRE`\n- Fix / fix / FIX / Change / Modify → `FIX`\n- Redesign / redesign / REDESIGN / Rewrite → `REDESIGN`\n- blank → `null`\n\n#### 6. Conflict Detection\n\nCheck each row for conflicts BEFORE applying decisions. Present conflicts to the user and wait for resolution.\n\n**BAdI conflict — lead retires a BAdI implementation:**\n\nIf `isEnhancement = true` in objects.json and the lead marked the object as \"Retire\":\n\n```\nCONFLICT: {object_name} is an active BAdI implementation.\nThe lead marked it for retirement, but it is structurally wired into SAP.\n\nOptions:\n a) Override lead's decision → keep as {current_classification}/WIRED_IN (recommended)\n b) Accept with warning → mark RETIRE but flag \"Active BAdI — deactivation required first\"\n c) Ask lead to clarify → generate follow-up for this specific object\n\nWhich option? [a/b/c]\n```\n\nWait for the user's choice. Apply the selected option.\n\n**GXP/SOX conflict — lead retires a regulated object:**\n\nIf the object is in a GXP or SOX package and the lead marked it as \"Retire\":\n\n```\nCONFLICT: {object_name} is in a {GXP/SOX} package.\nRetirement requires formal change control. Cannot auto-approve.\n\nThe object will be marked as RETIRE with REGULATED flag.\nA formal change control process must be followed.\n```\n\nApply automatically — no user choice needed. Set `humanReview.regulatedRetire = true`.\n\n**RFC conflict — lead says RFC FM is not used:**\n\nIf the object is an RFC-enabled function module (TFDIR.FMODE = 'R') and the lead says \"Not used\":\n\n```\nWARNING: {object_name} is RFC-enabled (TFDIR.FMODE = 'R').\nThe lead says it's not used, but external systems may call it.\nConfirm with the integration/middleware team before retiring.\n```\n\nSet `humanReview.rfcWarning = true`. Do not auto-retire.\n\n#### 7. Partial Decisions\n\nIf the lead filled \"Still Used?\" but left \"Decision\" blank:\n- Set `humanReview.used` to the normalized value\n- Set status: `USAGE_CONFIRMED_DECISION_PENDING`\n- The object keeps its technical classification — do not override\n\n#### 8. Default for Used-but-Undecided\n\nIf \"Still Used?\" = Y but \"Decision\" is blank:\n- Treat as CLEAN (conservative — the lead confirmed usage but did not decide)\n- Set `humanReview.decision = null` (not overridden, just confirmed used)\n- Do not change the primary classification\n\n#### 8b. Decision Mapping — How Lead Decisions Change Classification\n\nApply these rules when the lead provides an explicit decision:\n\n| Lead writes | Effect on primary classification |\n|---|---|\n| Keep | Primary stays unchanged. Set `humanReview.decision = \"KEEP\"`. |\n| Fix | Primary becomes REFACTOR if it was CLEAN/DORMANT/UNKNOWN. If already REFACTOR or QUICK_FIX, keep current. Set `humanReview.decision = \"FIX\"`. Chains to /abap-upgrade-fix. |\n| Retire | Primary becomes RETIRE_CANDIDATE if it was DORMANT/UNKNOWN. If it was CLEAN or REFACTOR (actively used), this is a conflict — warn the user before applying. Set `humanReview.decision = \"RETIRE\"`. |\n| Redesign | Primary becomes REDESIGN. Set `humanReview.decision = \"REDESIGN\"`. Escalated to steering committee. |\n\n#### 9. No-Response vs Blank-Response Distinction\n\nDistinguish between two different \"no answer\" states:\n\n- Row EXISTS in CSV but Used + Decision are both blank → `REVIEWED_NO_ANSWER` (lead saw it, skipped it)\n- Row is MISSING from CSV (lead deleted the row) → `NOT_REVIEWED` (lead may not have seen it)\n\nBoth stay at their technical classification — no override applied. Track counts of each for the project manager.\n\n#### 10. Unmatched Object Names\n\nIf a row in the CSV has an object name not found in objects.json, report as warning:\n\n```\nWARNING: Object '{name}' from worksheet not found in inventory. Skipped.\n```\n\nDo not create new entries. Do not error out — continue processing remaining rows.\n\n#### 11. Follow-up Worksheet\n\nAfter import, if any objects still have `DECISION_PENDING` status, generate a targeted follow-up CSV with only those objects:\n\n```\n{n} objects still need decisions. Generated follow-up worksheet:\n .cspeach/cca/projects/{id}/worksheets/followup_{module}_{date}.csv\n```\n\nThe follow-up worksheet uses the same format as the original worksheet but contains only the pending objects. Include the original technical classification and any partial responses already received.\n\n---\n\n### Import Summary\n\nAfter validation and import, print this summary:\n\n```\nIMPORT complete for module {module}.\n Rows processed: {n}\n Decisions applied: {n} (Keep: {n}, Fix: {n}, Retire: {n}, Redesign: {n})\n Conflicts resolved: {n}\n No response (reviewed, skipped): {n}\n Not reviewed (rows missing): {n}\n Unmatched names: {n}\n Follow-up needed: {n} objects\n\n{remaining_modules} modules still pending import.\n```\n\nUpdate `progress.review.responsesReceived` with the module name. Remove the module from `responsesPending`.\n\n---\n\n### Reclassification After Import\n\nAfter all responses are imported (or the user says \"done importing\"), reclassify objects that changed:\n\n1. Objects with decision = \"Fix\" → ensure primary = REFACTOR\n2. Objects with decision = \"Retire\" → ensure primary = RETIRE_CANDIDATE (with conflict checks from step 6)\n3. Objects with decision = \"Redesign\" → ensure primary = REDESIGN\n4. Rerun compliance gates (Rules 0a, 0a2) on any changed objects\n5. Write updated objects.json\n\n---\n\n### IMPORT Phase Completion\n\nWhen all modules are imported and reclassification is done, print:\n\n```\nIMPORT complete.\n All modules imported: {module list}\n Total decisions: {n}\n Reclassified objects: {n}\n Objects awaiting follow-up: {n}\n\nPhase complete. Move to REPORT.\n```\n\n---\n\n## REPORT Phase\n\n### Purpose\n\nGenerate the consulting-grade assessment report. The report is the primary deliverable of the CCA process.\n\n### Entry Criteria\n\nCLASSIFY phase must be complete (minimum). IMPORT phase improves the report but is optional.\n\n### Output Location\n\nSave reports to: `.cspeach/cca/projects/{id}/reports/report_{date}.md`\n\n### Report Generation\n\nGenerate a Markdown report with the following sections. Fill every placeholder with actual data from objects.json and project.json. Do not leave any placeholder unfilled — if data is missing, write \"N/A\" or \"not available\" with a reason.\n\n#### Header\n\n```markdown\n## Custom Code Analysis Report\n### System: {SID} / Client {client} / Release {release}\n### Scope: {packages}\n### Date: {date}\n### Session: {projectId}\n### Prepared by: {developer} / {company} — prepared with CSPeach\n\n### Data Sources Used\n{Table showing each discovery probe result and confidence level}\n```\n\n#### Executive Summary\n\nGenerate the management-facing 4-bucket summary:\n\n```markdown\n### Executive Summary (management view — 4 buckets)\nTotal: {N} custom objects\n\n| Category | Count | % | What it means |\n|---|---|---|---|\n| No work needed | {n} | {pct}% | Clean, batch-active, test — migrate as-is |\n| Needs code changes | {n} | {pct}% | Quick fixes + refactoring + redesign |\n| Can be retired | {n} | {pct}% | Not used — candidates for deletion (saves effort) |\n| Needs investigation | {n} | {pct}% | Unknown, dormant, interfaces — verify before deciding |\n\nEstimated remediation effort: **{d} person-days** for code changes.\nPotential savings from retirement: **{d} person-days** avoided.\n```\n\nMap classifications to buckets as follows:\n- \"No work needed\" = CLEAN + BATCH_ACTIVE + TEST\n- \"Needs code changes\" = QUICK_FIX + REFACTOR + REDESIGN\n- \"Can be retired\" = RETIRE_CANDIDATE\n- \"Needs investigation\" = UNKNOWN + DORMANT + INTERFACE_VERIFY\n\n#### Detailed Classification\n\n```markdown\n### Detailed Classification (technical view)\n\n| Classification | Count | % | Confidence | Risk | Est. Effort |\n|---|---|---|---|---|---|\n| CLEAN | {n} | {pct}% | HIGH | Low | test only |\n| QUICK_FIX | {n} | {pct}% | HIGH | Low | {h} hours |\n| REFACTOR | {n} | {pct}% | HIGH | Medium | {d} days |\n| REDESIGN | {n} | {pct}% | HIGH | High | {d} days |\n| RETIRE_CANDIDATE | {n} | {pct}% | varies | None | 0 (pending confirmation) |\n| BATCH_ACTIVE | {n} | {pct}% | MEDIUM | Low | verify schedules |\n| INTERFACE_VERIFY | {n} | {pct}% | LOW | High | confirm with integration team |\n| DORMANT | {n} | {pct}% | LOW | Low | verify with business |\n| TEST | {n} | {pct}% | N/A | None | skip |\n| UNKNOWN | {n} | {pct}% | NONE | Unknown | needs review |\n\nNote: REGULATED and QUALITY_REVIEW are secondary tags, not primary\nclassifications. Count them separately:\n \"Of the {n} CLEAN objects, {m} are in regulated packages (GXP/SOX).\"\n \"Of the {n} DORMANT objects, {m} are in QUALITY packages.\"\n```\n\n#### Coverage and Breakdown Sections\n\n```markdown\n### Coverage\nObjects with HIGH confidence: {n} ({pct}%)\nObjects with MEDIUM confidence: {n} ({pct}%)\nObjects with LOW/NONE confidence: {n} ({pct}%)\n\n### By Object Type\n| Type | Total | CLEAN | FIX | RETIRE | DORMANT | OTHER |\n\n### By Package\n| Package | Objects | Compliance | Clean% | Fix% | Retire% | Risk |\n\n### Interfaces (RFC/External)\n| Object | Type | RFC? | Internal Callers | Classification | Action |\nWARNING: RFC-enabled objects may be called by external systems invisible\nto this analysis. Confirm each with the integration/middleware team.\n\n### Regulated Objects (GXP/SOX)\n| Object | Package | Compliance | Classification | Note |\nThese objects require formal change control before any modification.\n```\n\n#### Effort and Cost Sections\n\n```markdown\n### Effort Estimate\n| Category | Objects | Per Object | Total |\n| Quick fix | {n} | 5 min | {h} hours |\n| Refactor | {n} | 2-4 hours | {d} days |\n| Redesign | {n} | 1-3 days | {d} days |\n| Total | | | {d} person-days |\n\n### Cost Summary (fill in rates)\n| Item | Days | Rate/day | Cost |\n| Quick fix | {d} | _______ | _______ |\n| Refactor | {d} | _______ | _______ |\n| Redesign | {d} | _______ | _______ |\n| Total | | | _______ |\n```\n\nLeave Rate/day and Cost columns blank with underscores — the consultant fills these in based on their engagement terms.\n\n#### Clean Core Readiness\n\n```markdown\n### Clean Core Readiness (if ARS_W_API_STATE was available)\n| Category | Count | % |\n|---|---|---|\n| Cloud-ready (all table APIs released) | {n} | {pct}% |\n| Needs API migration (uses unreleased tables) | {n} | {pct}% |\n| Not checked (ECC system / no ARS data) | {n} | {pct}% |\n\nTop unreleased APIs used by custom code:\n| SAP Table/View | Release State | Used By (count) | Successor |\n(List top 10 most-referenced unreleased APIs with their successors)\n\nNote: Clean Core check covers TABLE dependencies. FM/class API\ndependencies require cross-reference analysis (future enhancement).\n```\n\nIf ARS_W_API_STATE was not available during DISCOVER, write: \"Clean Core readiness data not available — ARS_W_API_STATE table not found on this system (likely ECC or pre-2022 S/4HANA).\"\n\n#### Retirement and Gaps\n\n```markdown\n### Retirement Candidates ({n} objects)\nThese objects are CANDIDATES based on analysis. Retirement requires:\n1. Business confirmation (via worksheets or direct approval)\n2. Dependency resolution\n3. Data archiving decisions for tables\n4. Formal change control for regulated objects\n5. Future execution via /abap-retire skill\n\n### Objects Not Analyzed\n{List objects skipped due to cross-ref budget limits, errors, or timeouts — with reason}\n```\n\n#### Next Steps\n\n```markdown\n### Next Steps\n1. Distribute worksheets to functional leads (if not done)\n2. Import responses and reclassify\n3. Run /abap-upgrade-fix on FIX objects\n4. Use RETIRE_CANDIDATE list for future /abap-retire process\n5. Escalate REDESIGN items to steering committee\n```\n\nTailor the next steps to the actual project state. If worksheets were already imported, omit steps 1-2. If no REDESIGN objects exist, omit step 5.\n\n---\n\n## Workflow Paths\n\n### Path A — Full analysis, chain to fix\n\n```\n/abap-cca → all phases → report\n→ chain to /abap-upgrade-fix for FIX objects\n→ hand RETIRE list to future /abap-retire\n```\n\nExecute all phases in order: DISCOVER → ANALYZE → CLASSIFY → REPORT. Skip IMPORT unless the user requests worksheets. After REPORT, offer to chain to `/abap-upgrade-fix`.\n\n### Path B — Worksheet review\n\n```\n/abap-cca → analysis → worksheets → leads respond → import → reclassify → report\n→ chain to /abap-upgrade-fix\n```\n\nExecute DISCOVER → ANALYZE → CLASSIFY → generate worksheets → pause for human review → IMPORT responses → reclassify → REPORT.\n\n### Path C — Get PRD usage data\n\n```\n/abap-cca → analysis (LIMITED on DEV) → offer PRD data options:\n 1. SE16 export from PRD (lightest — export UPL table via SE16)\n 2. SolMan export (if SolMan is connected)\n 3. Activate UPL in PRD (config change, collect 3-6 months)\n 4. Generated extractor program (via change management, NOT $TMP in PRD)\n→ import PRD data → reclassify with higher confidence → report\n```\n\nWhen running on a DEV system without UPL data, present these options to the user. Each option produces a file the user can place in `.cspeach/cca/projects/{id}/imports/` for the IMPORT phase.\n\n### Path D — Export and stop\n\n```\n/abap-cca → analysis → export full CSV to disk → done\n```\n\nRun DISCOVER → ANALYZE → CLASSIFY → export objects.json as CSV. No REPORT phase. Use this when the user only needs the raw data for external tools.\n\n### Path Combination\n\nPaths combine freely. State file persists across sessions. Resume any project by loading its project.json and objects.json.\n\n---\n\n## Handoff to /abap-upgrade-fix\n\nWhen the user chooses Path A or Path B (after import), generate a secondary baseline file for `/abap-upgrade-fix`.\n\nSave to: `.cspeach/cca/projects/{id}/fix-baseline.json`\n\n```json\n{\n \"source\": \"abap-cca\",\n \"projectId\": \"{id}\",\n \"created\": \"{timestamp}\",\n \"fixSession\": { \"fixed\": [] },\n \"objects\": [\n {\n \"name\": \"ZFI_AGING_REPORT\",\n \"type\": \"PROG\",\n \"package\": \"ZCUSTOM_FI\",\n \"classification\": \"REFACTOR\",\n \"atcFindings\": [...]\n }\n ]\n}\n```\n\nInclude only objects where classification is QUICK_FIX, REFACTOR, or REDESIGN (or where humanReview.decision = \"FIX\"). Exclude CLEAN, RETIRE_CANDIDATE, DORMANT, TEST, UNKNOWN, and INTERFACE_VERIFY objects.\n\n---\n\n## Guardrails and Limitations\n\n### Known Limitations\n\n| Limitation | Impact | Workaround |\n|---|---|---|\n| sap_syntax_check only checks written source | Cannot pre-check proposed code | Write then check then restore if fail |\n| UPL does not track classes | Classes show zero UPL | Transitive classification via callers |\n| UPL may not track inbound RFC | RFC FMs may show zero | TFDIR.FMODE probe + INTERFACE_VERIFY |\n| ATC takes 3-6 hours for large scopes | Session timeouts | Checkpoint per package, resume |\n| Cross-ref capped at 550 calls | Large scopes have unanalyzed objects | Tiered priority, worksheet fallback |\n| CMOD exits not in TADIR | Exit includes invisible | Explicit MODSAP/MODACT probe |\n| ADT maxRows unknown per system | Silent truncation risk | Pagination strategy |\n| DDIC classification is transitive | Depends on consumer analysis quality | If consumers are UNKNOWN, DDIC is UNKNOWN |\n| Clean Core covers TABLE deps only | FM/class API deps not checked | Future enhancement (WBCROSSGT) |\n\n### SQL Gotchas\n\n**Underscore in LIKE:** In ABAP SQL, `_` is a single-character wildcard. `LIKE 'ZFI_%'` matches `ZFIXANYTHING` too. Use exact match (`DEVCLASS = '{package}'`) instead of LIKE with underscores wherever possible.\n\n**Namespace slashes:** Table names like `/SDF/MON_RECS` contain forward slashes. ADT Data Preview handles this, but verify on the connected system.\n\n**No JOINs in V1:** Use two separate queries and merge in skill logic. JOINs may work on some ADT endpoints but are not guaranteed.\n\n### Research Required\n\n| Item | How to verify |\n|---|---|\n| UPL table variant + field names | Probe all 4 variants with SELECT * UP TO 1 ROWS |\n| ADT maxRows actual limit | Test with 5000, 10000 on connected system |\n| ATC variant existence | Probe in DISCOVER, not ANALYZE |\n| SEOCLASS field names | SELECT * FROM SEOCLASS UP TO 1 ROWS |\n| D010TAB row volume for Z* | Test COUNT(*) to gauge pagination needs |\n| sap_sql_query column headers | Does the tool return column names in the response? |\n\n### Privacy Note\n\nobjects.json contains developer user IDs (author, changedBy) from SAP repository metadata. Treat this file as internal working data. Do not share externally without review. Worksheets and reports deliberately exclude developer identity.\n\n### System Load\n\nATC runs consume work processes. On shared development systems, run during off-hours or configure delay between ATC runs. Never launch parallel ATC runs on the same system.\n\n### Out of Scope\n\nThese use cases are explicitly NOT covered by `/abap-cca`. Each deserves its own spec:\n\n- **Quarterly Clean Core Monitoring** (`/abap-cca-monitor`): run history, delta reporting, regression detection, trend projection\n- **CI/CD Transport Gate** (`/abap-transport-gate`): transport-scoped analysis, pass/fail on unreleased APIs\n- **Multi-System Consolidation** (`/abap-consolidate`): cross-project comparison, overlap detection, naming conflicts\n\nDo not attempt to implement these within `/abap-cca`. Redirect users to the appropriate future skill.\n\n---\n\n## REPORT Phase Completion\n\nWhen the report is generated and saved, print:\n\n```\nOutputs:\n - Open in the viewer -> <{id}-cca-assessment-{date}.cspeach.json> (the saved envelope)\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\n\nSummary:\n Total objects: {n}\n No work needed: {n} ({pct}%)\n Needs code changes: {n} ({pct}%)\n Can be retired: {n} ({pct}%)\n Needs investigation: {n} ({pct}%)\n Estimated effort: {d} person-days\n Clean Core ready: {pct}%\n\nNext steps:\n - Chain to /abap-upgrade-fix for code remediation\n - Distribute worksheets (if not done)\n - Use RETIRE list for future /abap-retire process\n```\n\nThen write the `step-8-done.json` checkpoint (REPORT is phase 8 —\nthe terminal phase). Phase complete; CCA analysis finished.\n\n## Manifest Emission (CLI integration)\n\nAfter the REPORT-phase completion summary above, emit a hidden\nHTML-comment manifest block as the last thing in the response.\nThe CSPeach CLI parses this block (via\n`cspeach-cli/src/projects/extract-cca.ts`) into a\n`cca-assessment` envelope so the file picker, `--from` chain\nvisibility, and team-flow status renderer all see the run.\n\nThe block must appear EXACTLY once per response, after all human\noutput, in this format:\n\n```\n<!-- cspeach:cca-manifest\nartefact: cca-assessment\ntitle: <one-line title — e.g. \"ZFI / ZSD — S/4HANA 2023 estate\">\ndetail_path: .cspeach/cca/projects/<slug>/project.json\nproject_id: <slug>\nscope: <scope expression — e.g. \"ZFI* + ZSD*\" or \"All Z*\">\natc_variant: <variant id used in CLASSIFY phase>\ntotal_objects: <n>\nkeep: <n>\nfix: <n>\nretire: <n>\nredesign: <n>\nuncategorized: <n>\nclassifications:\n item-001 | <objectName> | <objectType> | <classification> | <confidence>\n item-002 | <objectName> | <objectType> | <classification> | <confidence>\n ...up to 50 rows; full detail in detail_path...\n-->\n```\n\nField rules:\n- `classification` is one of: `keep`, `fix`, `retire`, `redesign`\n (lowercase). Map the internal CCA verdict (CLEAN, BATCH_ACTIVE,\n TEST → `keep`; QUICK_FIX, REFACTOR → `fix`; RETIRE_CANDIDATE →\n `retire`; REDESIGN → `redesign`; DORMANT, UNKNOWN, INTERFACE_VERIFY\n → `uncategorized` in the summary, omit from classifications table).\n- `confidence` is one of: `high`, `medium`, `low`. Use the per-object\n confidence the CLASSIFY phase already assigns.\n- `total_objects` is the sum across `keep + fix + retire + redesign +\n uncategorized`. Self-check the math before emitting.\n- **Counts must equal rows.** The CLI extractor cross-checks every\n per-class summary count (`keep`/`fix`/`retire`/`redesign`) against\n the table rows and REJECTS the manifest on any contradiction — a\n `retire: 0` next to retire rows fails the save, loudly.\n- The classifications table is **capped at 50 rows** to keep the\n envelope small. If more classified objects exist than rows shown,\n you MUST mark the truncation — directly above `classifications:`:\n ```\n classifications_truncated: true\n classifications_shown: <rows actually in the table>\n classifications_total: <full classified count in detail_path>\n ```\n Then emit the 50 highest-priority rows (sort: `redesign` first,\n then `fix`, then `retire`, then `keep`, ordered by confidence DESC\n within each group). The full per-object data lives in the\n `detail_path` project.json. An unmarked cap is rejected by the\n extractor (counts exceed rows → save fails).\n- `detail_path` is **project-relative** (resolved from the git root\n or current workspace), never absolute. Same convention as the\n upgrade manifests. Emit `detail_path` only AFTER the project.json\n detail file is confirmed written to disk — a manifest pointing at a\n file that was never saved corrupts every downstream chain step.\n\n## Post-Save Chain Prompt\n\nAfter the CLI prints the save confirmation (`Saved: ...`), ask once:\n\n> \"Assessment ready. {fix_count} objects classified for FIX or REDESIGN.\n> Run /abap-upgrade-scan on the in-scope packages now to baseline ATC\n> findings for those objects?\"\n>\n> Options:\n> - Run /abap-upgrade-scan on the FIX/REDESIGN packages (recommended)\n> - Distribute worksheets to functional leads first (Path B)\n> - End turn\n\nIf the user chooses /abap-upgrade-scan, the cross-pipeline link is a\nhint, not a `--from` injection (CCA classifications and ATC findings\nare different inputs). Use the hint pattern:\n\n> `→ Run \\`/abap-upgrade-scan\\` against {comma-separated package list},\n> then \\`/abap-upgrade-fix --from\\` once the scan envelope is saved.`\n\nIf the user has multiple parallel CCA assessments saved (one per\nconsultant slice), suggest `/abap-cca-merge` instead:\n\n> `→ Multiple cca-assessment files in workspace. Run\n> \\`/abap-cca-merge --inputs @<a> @<b> ...\\` to consolidate before\n> chaining to /abap-upgrade-scan.`\n",
16
- "sha256": "dc6f122942d1ff462bf1e746cda09b9fe79445cc31a76a17408c837d1c119248",
22
+ "body": "---\r\nname: abap-cca\r\ndescription: >\r\n Custom Code Analysis for S/4HANA migration and Clean Core initiatives.\r\n Discovers data sources, inventories all custom objects, analyzes usage\r\n and ATC findings, classifies with 20 heuristic rules, generates review\r\n worksheets for functional leads, and produces a consulting-grade\r\n assessment report. Chains to /abap-upgrade-fix for remediation.\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nforge_rules: [6]\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.3.0\"\r\nwidget: bar-chart\r\n---\r\n\r\n# abap-cca\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n\r\n## Purpose\r\n\r\nAnalyze custom ABAP code before an S/4HANA migration or Clean Core initiative.\r\nThe skill walks through eight phases: discover data sources, inventory all custom\r\nobjects, analyze usage and ATC and Clean Core findings, classify each object with\r\n20 heuristic rules, generate review worksheets for functional leads, import\r\nfunctional decisions, and produce a consulting-grade assessment report.\r\n\r\nThis is a read-only analysis -- it does not change any code (Forge Rule 6: read\r\nonly by default). Remediation chains to `/abap-upgrade-fix` for FIX-classified\r\nobjects, and hands off RETIRE lists for a future `/abap-retire` skill.\r\n\r\n## When to Use\r\n\r\n- Before S/4HANA migration -- assess the custom code landscape\r\n- Clean Core initiative -- identify unreleased API usage\r\n- Custom code right-sizing -- find objects to retire\r\n- Management reporting -- effort estimates and risk assessment\r\n\r\nDo NOT use for:\r\n- Greenfield development (use `/abap-generate` or `/abap-rap`)\r\n- Single-object fixes (use `/abap-incident` or `/abap-atc-fix`)\r\n- Transport management (use `/abap-transport`)\r\n\r\n## Required Tools\r\n\r\n| Tool | Phase | Purpose |\r\n|---|---|---|\r\n| `sap_sql_query` | DISCOVER, INVENTORY, ANALYZE | System probes, object queries, usage data |\r\n| `sap_atc_run` | ANALYZE | ATC findings per package |\r\n| `sap_api_state` | ANALYZE | Clean Core release state check |\r\n| `sap_usage_references` | ANALYZE | Cross-reference analysis |\r\n| `sap_search_object` | DISCOVER | Object search and validation |\r\n\r\n## SQL Rules (apply to ALL phases)\r\n\r\n**Prefer simple queries.** Aggregate SQL (`COUNT(*)`, `GROUP BY`, `SUM`,\r\n`DISTINCT`-with-aggregation) USUALLY works on ADT SQL endpoints — verified\r\non S/4HANA 2023 — but support varies by release and patch level. Default\r\nto the simplest query that answers the question:\r\n- `SELECT COUNT(*) FROM table WHERE single_predicate` is reliable\r\n everywhere — use it for existence + scale checks.\r\n- A single `GROUP BY DEVCLASS` over `TADIR` is fine when you need\r\n per-package counts in one round-trip; if the endpoint returns HTTP 400\r\n or empty-when-data-exists, fall back to per-package queries +\r\n skill-side counting.\r\n- AVOID `DISTINCT` combined with aggregation in the SAME query (e.g.\r\n `SELECT COUNT(DISTINCT x)`) — those reliably fail on ADT SQL. Run two\r\n queries instead.\r\n\r\n**No JOINs:** Use separate queries and merge results in the skill.\r\n\r\n**Keyset pagination:** ADT SQL has no OFFSET. Use `WHERE key > '{last_value}'\r\nORDER BY key` to paginate.\r\n\r\n**Result size limit:** MCP tool output has a character limit. Never SELECT all\r\nobjects across all packages in one query. Query per package or per type.\r\n\r\n**Existence check — the canonical pattern.** Before declaring a package\r\nempty / out-of-scope / not-worth-analyzing, run EXACTLY:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'\r\n```\r\n\r\nNO additional filter clauses. NO `AND OBJECT <> 'DEVC'`. NO\r\n`AND PGMID = 'R3TR'`. NO `AND DELFLAG = ''`. Each extra `AND` is an\r\nopportunity to nuke the result and falsely declare a populated package\r\nempty. Result interpretation:\r\n\r\n- `COUNT = 0` → no TADIR entry; verify package itself exists via TDEVC.\r\n- `COUNT = 1` → only the package's own DEVC entry exists; truly empty.\r\n- `COUNT >= 2` → at least one custom object; proceed.\r\n\r\n**Cross-validate before concluding a package is empty.** ALWAYS run\r\n`sap_search_object('{pkg}*')` (note trailing `*`) as a second probe. If\r\nthe search returns more matches than just the single package row, the\r\npackage has objects and the COUNT query was wrong — re-run with NO\r\nfilter and trust the new result. Two probes disagreeing is a strong\r\nsignal the model added a stray filter.\r\n\r\n**Do NOT conflate `isAddingObjectsAllowed=\"false\"` with \"package is\r\nempty\".** The `pak:isAddingObjectsAllowed=\"false\"` attribute (visible\r\nin `sap_object_structure` package XML) means SAP has frozen the package\r\nagainst further additions — not that it contains nothing. Frozen\r\nproduction packages routinely hold dozens of active objects. The only\r\nauthoritative emptiness test is a bare TADIR row count, cross-validated\r\nwith `sap_search_object`.\r\n\r\n## Phase Overview\r\n\r\n```\r\n/abap-cca --> INIT --> DISCOVER --> INVENTORY --> ANALYZE --> CLASSIFY\r\n --> REVIEW (worksheets) --> IMPORT --> REPORT\r\n --> chain to /abap-upgrade-fix (for FIX objects)\r\n --> hand off RETIRE list (for future /abap-retire)\r\n```\r\n\r\n## CLI Flags\r\n\r\nTwo flags shape multi-developer / multi-session CCA work. Both are\r\noptional — without them, the skill runs as before with interactive\r\nscope selection and the existing project.json-based resume.\r\n\r\n### `--scope` (slice the estate for parallel runs)\r\n\r\nFilter the inventory before classification so each consultant's\r\nslice is independent. Three syntaxes (priority order — first wins):\r\n\r\n```\r\n--scope @<file> # one obj/line OR comma-list, file in workspace\r\n--scope \"ZFI*,ZSD*\" # comma-separated patterns, * wildcard ok\r\n--scope \"ZFI*\" # single pattern\r\n```\r\n\r\nApply the filter AFTER INIT (namespace discovery) and DISCOVER\r\n(estate map) phases, BEFORE INVENTORY paginated read. Each\r\nconsultant's run only inventories + classifies their slice — no\r\nduplicate work. Record the scope expression in\r\n`project.json.scope` so the manifest envelope and merge step can\r\nreconcile slices later.\r\n\r\n### `--resume <project-name>` or `--resume @<envelope>`\r\n\r\nExplicit resume flag. Two resolutions:\r\n\r\n- `--resume <project-name>` → load\r\n `.cspeach/cca/projects/<project-name>/project.json`. Falls back\r\n to the most-recent project under `.cspeach/cca/projects/` if no\r\n name supplied (`--resume` with no arg). For older projects, if the\r\n `.cspeach/cca/projects/` path is absent, fall back to the legacy\r\n `.abapforge/cca/projects/` location.\r\n- `--resume @<envelope>` → resolve the workspace envelope (a\r\n saved `cca-assessment` `.cspeach.json` file), read its\r\n `content.projectId`, and load that project.\r\n\r\nResume logic in priority order:\r\n1. Find the highest `step-N-done.json` checkpoint under\r\n `.cspeach/cca/projects/<id>/checkpoints/` (older projects: the legacy\r\n `.abapforge/cca/projects/<id>/checkpoints/`).\r\n2. Resume the next phase (N+1) using the cached output of step N.\r\n3. If no checkpoints exist, fall back to the existing\r\n `project.json.currentPhase` field (legacy resume).\r\n4. If neither exists, start fresh from INIT.\r\n\r\n## Checkpoints\r\n\r\nAfter each phase finishes successfully, write a checkpoint to:\r\n\r\n```\r\n.cspeach/cca/projects/<id>/checkpoints/step-<N>-done.json\r\n```\r\n\r\nWhere `<N>` is the phase number (1=INIT, 2=DISCOVER, 3=INVENTORY,\r\n4=ANALYZE, 5=CLASSIFY, 6=REVIEW, 7=IMPORT, 8=REPORT).\r\n\r\nCheckpoint format:\r\n\r\n```json\r\n{\r\n \"phase\": 5,\r\n \"phaseName\": \"CLASSIFY\",\r\n \"completedAt\": \"2026-05-09T10:30:00Z\",\r\n \"projectId\": \"<slug>\",\r\n \"outputs\": [\r\n \"objects.json\",\r\n \"project.json\"\r\n ],\r\n \"checksum\": \"<sha256 of project.json bytes at end of this phase>\"\r\n}\r\n```\r\n\r\nOn `--resume`, scan the checkpoints directory, find the highest\r\n`step-N-done.json`, validate the checksum still matches\r\n`project.json` (warn if drift), and resume from step N+1. The\r\ncached `outputs[]` files are the input to the next phase.\r\n\r\nCrashes / Ctrl+C / VPN drops mid-phase are recoverable: the\r\nunfinished phase has no `step-N-done.json`, so resume picks up at\r\nphase N (re-running it). Idempotent phase logic is required —\r\neach phase must tolerate being re-run after a partial completion\r\n(use `existsSync` checks before recreating files, etc.).\r\n\r\nDo NOT write `step-N-wip.json` or `step-N-failed.json` — only\r\nwrite `step-N-done.json` on phase success. Intra-phase recovery\r\nis a future enhancement.\r\n\r\n## Session Resume\r\n\r\nAt startup, check `.cspeach/cca/projects/` for existing project files (for older\r\nprojects, also check the legacy `.abapforge/cca/projects/`). Use the\r\n`Read` tool to list the directory. If project JSON files are found:\r\n\r\n1. Read each `project.json` file.\r\n2. List projects with their name, current phase, and progress summary (e.g.,\r\n \"ACME_MIGRATION -- ANALYZE phase, 340/512 objects classified\").\r\n3. Ask: \"Resume an existing project, or start a new one?\"\r\n\r\nIf the directory does not exist or is empty, proceed directly to INIT.\r\n\r\nWhen the user invoked the skill with `--resume`, skip the interactive\r\nprompt above and resume the named project directly (see CLI Flags\r\nsection). When the user invoked with `--scope`, skip the interactive\r\nscope selection in INIT (see Scope Selection below).\r\n\r\n## INIT Phase\r\n\r\n### First-Time Onboarding\r\n\r\nDisplay this text once at the start of a new project:\r\n\r\n```\r\nCustom Code Analysis helps you understand your custom ABAP code\r\nbefore an S/4HANA migration or Clean Core initiative. It will:\r\n\r\n1. Find all your custom programs, classes, tables, and enhancements\r\n2. Check which ones are still being used\r\n3. Identify which ones have S/4 compatibility issues\r\n4. Produce a report with effort estimates\r\n\r\nThis is a read-only analysis -- it will not change any code.\r\n```\r\n\r\n### Scope Selection\r\n\r\nPresent three options:\r\n\r\n```\r\nHow would you like to define the scope?\r\n 1. I know my package names (type them)\r\n 2. Find all custom packages on this system (recommended)\r\n 3. I'm not sure -- help me\r\n```\r\n\r\n**Option 1 (expert):** Accept direct entry. Valid formats:\r\n- Single package: `ZCUSTOM_FI`\r\n- Comma-separated list: `ZCUSTOM_FI, ZCUSTOM_SD, ZCUSTOM_MM`\r\n- Wildcards: `Z*`, `ZCUSTOM_*`\r\n- Mixed: `ZCUSTOM_FI, /ACME/CORE, YLEGACY_*`\r\n- Namespace prefixes: `/ACME/*`\r\n\r\nStore the raw input. If wildcards are present, resolve them in the wildcard\r\nresolution step below before proceeding.\r\n\r\n**Option 2 (discovery-first):** Run separate queries per prefix via `sap_sql_query`.\r\nDo NOT combine prefixes with OR — the `/%` pattern can cause silent failures on\r\nsome ADT endpoints, and large result sets may timeout returning empty instead of\r\nan error.\r\n\r\n```sql\r\n-- Query 1: Z-packages\r\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE 'Z%' ORDER BY DEVCLASS\r\n\r\n-- Query 2: Y-packages\r\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE 'Y%' ORDER BY DEVCLASS\r\n```\r\n\r\n**Query 3: Namespace packages.** Customer namespace packages (e.g., `/ACME/CORE`)\r\nalso need discovery. Do NOT run a blanket `LIKE '/%'` — that returns hundreds of\r\nSAP-delivered namespace packages. Instead, first discover which customer namespaces\r\nexist:\r\n\r\n```sql\r\nSELECT NAMESPACE FROM TRNSPACE WHERE OWNER <> 'SAP' AND OWNER <> '' ORDER BY NAMESPACE\r\n```\r\n\r\nIf TRNSPACE is not accessible, ask the user: \"Does your system have custom namespace\r\npackages (e.g., /ACME/*, /MYCO/*)? If yes, type the namespace prefix.\"\r\n\r\nFor each customer namespace found, run:\r\n```sql\r\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE '{namespace}%' ORDER BY DEVCLASS\r\n```\r\n\r\nMerge all namespace packages into the results alongside Z/Y packages.\r\n\r\n**Empty result safety:** If a TDEVC query returns zero rows, retry once. If still\r\nempty, fall back to TADIR: `SELECT DEVCLASS FROM TADIR WHERE DEVCLASS LIKE 'Z%'\r\nORDER BY DEVCLASS`. TADIR is larger but confirms whether packages with objects\r\nexist.\r\n\r\nMerge results from all queries. Display grouped by prefix.\r\n\r\n**Object counts per package — for DISPLAY in the user-facing list.** Use\r\nthis filtered count when rendering \"package X has Y objects\" so the DEVC\r\nrow doesn't make empty packages look populated:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}' AND OBJECT <> 'DEVC'\r\n```\r\n\r\nThis is for DISPLAY only. It is NOT the empty-package test (see SQL\r\nRules above — the existence check uses the BARE `WHERE DEVCLASS = '{pkg}'`\r\nquery with no extra filters). Mixing the two has caused real\r\nhallucinations where the model added a filter, got 0 from the count,\r\nand declared a populated package empty.\r\n\r\n**Optimization:** Do NOT run one query per package — that causes 37+ sequential\r\ncalls. Two viable approaches, both faster:\r\n\r\n```sql\r\n-- Approach A: GROUP BY in one round-trip (works on S/4HANA 2023+;\r\n-- if it returns HTTP 400 on your release, fall back to Approach B).\r\nSELECT DEVCLASS, COUNT(*) AS N FROM TADIR\r\nWHERE DEVCLASS LIKE '{prefix}%' AND OBJECT <> 'DEVC'\r\nGROUP BY DEVCLASS\r\n\r\n-- Approach B: IN-batched per-row, count client-side.\r\nSELECT OBJ_NAME, DEVCLASS FROM TADIR\r\nWHERE DEVCLASS IN ('{pkg1}','{pkg2}','{pkg3}',...) AND OBJECT <> 'DEVC'\r\nORDER BY DEVCLASS\r\n```\r\n\r\nFor Approach B, batch packages into groups of 10-15 per query (to stay\r\nwithin SQL length limits). Count results per DEVCLASS in the skill.\r\nThis turns 37 queries into 3-4 queries.\r\n\r\nIf a single batch returns too many results (>5000 rows), fall back to per-package\r\nCOUNT(*) queries for that batch only.\r\n\r\nShow the count next to each package name. Hide packages with 0 objects (empty\r\npackages). Do NOT attempt to SELECT all packages in one query on systems with\r\n200+ packages.\r\n\r\nLet the user confirm all packages or select specific ones.\r\n\r\n**Option 3 (help me):** Run the same discovery query as option 2, but prefix the\r\nresults with this explanation:\r\n\r\n```\r\nPackages are containers for custom ABAP objects. Custom packages start\r\nwith Z, Y, or a company namespace like /ACME/. I found the following\r\ncustom packages on your system:\r\n```\r\n\r\nThen show the same grouped list and let the user select.\r\n\r\n### Namespace Extraction\r\n\r\nAfter resolving the package list, extract unique namespace prefixes. For each\r\npackage, determine the prefix: `Z` for Z-packages, `Y` for Y-packages, or the\r\nfull namespace (e.g., `/ACME/`) for namespace packages. Store in\r\n`scope.namespaces`. Example: if packages are `ZCUSTOM_FI, YCUSTOM_SD,\r\n/ACME/CORE`, then `scope.namespaces = [\"Z\", \"Y\", \"/ACME/\"]`.\r\n\r\nEvery query in DISCOVER, INVENTORY, and ANALYZE that uses `LIKE '{prefix}%'`\r\nmust run once PER namespace. A query with `LIKE 'Z%'` will miss `/ACME/` objects.\r\n\r\n### Wildcard Resolution\r\n\r\nIf `scope.packages` contains any wildcard entries (e.g., `Z*`, `ZCUSTOM_*`),\r\nresolve them before proceeding. Run via `sap_sql_query`:\r\n\r\n```sql\r\nSELECT DEVCLASS FROM TDEVC WHERE DEVCLASS LIKE '{pattern}'\r\nORDER BY DEVCLASS\r\n```\r\n\r\nReplace the wildcard entry with the explicit list of matching packages. Show the\r\nresolved list to the user for confirmation. If no packages match a wildcard\r\npattern, warn the user and remove that entry.\r\n\r\n### Compliance Tagging\r\n\r\nAfter scope is confirmed, ask:\r\n\r\n```\r\nAny packages subject to regulatory or quality compliance?\r\nExamples:\r\n ZPHARMA_QM --> GXP (pharmaceutical/FDA validation)\r\n ZPHARMA_FI --> SOX (financial controls / Sarbanes-Oxley)\r\n ZMU_QM --> QUALITY (ISO 9001 / quality management)\r\n Others --> NONE\r\n\r\nEnter compliance tags as: PACKAGE_NAME=TAG (comma-separated)\r\nor press Enter for NONE on all packages.\r\n```\r\n\r\nCompliance levels and their effects on classification:\r\n\r\n| Level | Effect |\r\n|---|---|\r\n| **GXP** | Objects NEVER auto-classified as RETIRE. Formal change control required. |\r\n| **SOX** | Objects NEVER auto-classified as RETIRE. SOX control owner sign-off required. |\r\n| **QUALITY** | Extra scrutiny: DORMANT instead of RETIRE_CANDIDATE when zero usage. |\r\n| **NONE** | Normal classification rules apply. |\r\n\r\nStore compliance tags in `scope.compliance` as a map of package name to tag.\r\n\r\n**Object-level compliance overrides:** If the user needs finer granularity (e.g.,\r\n3 GxP objects in a 160-object package), accept overrides in this format:\r\n\r\n```\r\nZPACKAGE/OBJECT_NAME=TAG\r\n```\r\n\r\nStore these in `scope.complianceOverrides` as a map of `package/object` to tag.\r\nObject-level overrides take precedence over package-level tags.\r\n\r\n### Per-Package Phase Tracking\r\n\r\nInitialize `packagePhases` as a map of package name to current phase. Set all\r\npackages to `INIT`. The project-level `currentPhase` is always the minimum phase\r\nacross all packages (i.e., the least-advanced package determines the overall\r\nphase).\r\n\r\n### Delta Scope Changes\r\n\r\nThe user can add or remove packages later in any phase. When packages are added:\r\n- New packages start at INIT phase.\r\n- Existing results are preserved -- no re-analysis of already-processed packages.\r\n- `currentPhase` recalculates as the minimum across all `packagePhases`.\r\n\r\nWhen packages are removed:\r\n- Remove the package from `packagePhases` and `scope.packages`.\r\n- Preserve the data in the project file (mark as `excluded: true`) in case the\r\n user wants to re-add later.\r\n\r\n### Save Project State\r\n\r\nAt the end of INIT, save the project file. Create the directory if it does not\r\nexist:\r\n\r\n```\r\n.cspeach/cca/projects/{project_name}/project.json\r\n```\r\n\r\nThe `project_name` is derived from the user's description or a default like\r\n`cca_{date}`. If the user stated a project id anywhere in the prompt (e.g.\r\n\"project id: acme-s4-2026\"), use it VERBATIM as `project_name`/`projectId` —\r\nno renaming, no normalisation — parallel team slices must share one identity\r\nso `/abap-cca-merge` can reconcile them. The project.json structure:\r\n\r\n```json\r\n{\r\n \"name\": \"{project_name}\",\r\n \"created\": \"{ISO timestamp}\",\r\n \"currentPhase\": \"INIT\",\r\n \"scope\": {\r\n \"packages\": [\"ZCUSTOM_FI\", \"ZCUSTOM_SD\"],\r\n \"namespaces\": [\"Z\"],\r\n \"compliance\": {\r\n \"ZCUSTOM_FI\": \"SOX\",\r\n \"ZCUSTOM_SD\": \"NONE\"\r\n },\r\n \"complianceOverrides\": {}\r\n },\r\n \"packagePhases\": {\r\n \"ZCUSTOM_FI\": \"INIT\",\r\n \"ZCUSTOM_SD\": \"INIT\"\r\n }\r\n}\r\n```\r\n\r\n### INIT Phase Completion\r\n\r\nPrint confirmation:\r\n\r\n```\r\nProject: {project_name}\r\nPackages: {count} packages in scope\r\nCompliance: {summary of tagged packages, or \"none\"}\r\nSaved to: .cspeach/cca/projects/{project_name}/project.json\r\n\r\nScope set. Moving to DISCOVER phase.\r\n```\r\n\r\nPhase complete. Move to DISCOVER.\r\n\r\n---\r\n\r\n## Full project.json Schema\r\n\r\nThis is the canonical schema for the project state file. Create it during INIT and\r\nupdate it after every phase. All fields shown here are valid; omit fields that have\r\nnot been populated yet. Always preserve existing data when updating -- never\r\noverwrite the entire file; read first, merge changes, then write.\r\n\r\n```json\r\n{\r\n \"version\": \"3.0\",\r\n \"projectId\": \"S4H_Migration_ZCUSTOM\",\r\n \"system\": { \"sid\": \"S4H\", \"client\": \"100\", \"release\": \"758 SP02\" },\r\n \"scope\": {\r\n \"packages\": [\"ZCUSTOM_FI\", \"ZCUSTOM_SD\"],\r\n \"namespaces\": [\"Z\", \"Y\"],\r\n \"compliance\": {\r\n \"ZPHARMA_QM\": \"GXP\",\r\n \"ZPHARMA_FI\": \"SOX\",\r\n \"ZMU_QM\": \"QUALITY\",\r\n \"ZCUSTOM_SD\": \"NONE\"\r\n },\r\n \"complianceOverrides\": {\r\n \"ZINT_LIMS_BATCH_SEND\": \"GXP\",\r\n \"ZINT_LIMS_BATCH_RECV\": \"GXP\"\r\n }\r\n },\r\n \"currentPhase\": \"ANALYZE\",\r\n \"packagePhases\": {\r\n \"ZCUSTOM_FI\": \"CLASSIFY\",\r\n \"ZCUSTOM_SD\": \"ANALYZE\"\r\n },\r\n \"discovery\": {\r\n \"dataSources\": {\r\n \"TADIR\": { \"available\": true, \"rowCount\": 512 },\r\n \"TDEVC\": { \"available\": true },\r\n \"TRDIR\": { \"available\": true },\r\n \"UPL\": {\r\n \"available\": true,\r\n \"table\": \"/SDF/MON_RECS\",\r\n \"progField\": \"PROG_NAME\",\r\n \"countField\": \"COUNTER\"\r\n },\r\n \"SCMON\": { \"available\": false },\r\n \"TBTCP\": { \"available\": true },\r\n \"SXC_ATTR\": { \"available\": true, \"count\": 12 },\r\n \"MODATTR\": { \"available\": true, \"count\": 3 },\r\n \"ARS_W_API_STATE\": { \"available\": true },\r\n \"ARS_W_API_SCCSSR\": { \"available\": true },\r\n \"TFDIR\": { \"available\": true, \"rfcCount\": 8 },\r\n \"ATC_VARIANT\": { \"available\": true, \"variant\": \"S4HANA_READINESS\" }\r\n },\r\n \"confidence\": \"HIGH\"\r\n },\r\n \"progress\": {\r\n \"inventory\": { \"completedPackages\": [], \"pendingPackages\": [] },\r\n \"analysis\": {\r\n \"atcCompleted\": [],\r\n \"usageCollected\": false,\r\n \"crossRefCompleted\": 0\r\n },\r\n \"review\": {\r\n \"worksheetsGenerated\": false,\r\n \"responsesReceived\": [],\r\n \"responsesPending\": []\r\n }\r\n }\r\n}\r\n```\r\n\r\n### Schema Notes\r\n\r\n- `version`: Always `\"3.0\"` for this skill version.\r\n- `system`: Populated during INIT from the connected SAP system metadata.\r\n- `scope.namespaces`: Extracted from packages. Every `LIKE '{prefix}%'` query runs\r\n once per namespace entry.\r\n- `scope.compliance` and `scope.complianceOverrides`: Object-level overrides take\r\n precedence over package-level tags.\r\n- `currentPhase`: Always the minimum phase across all `packagePhases`.\r\n- `discovery.dataSources`: Each key maps to a probe result. Set `available: false`\r\n if the table does not exist or returns an authorization error.\r\n- `discovery.confidence`: Preliminary estimate after DISCOVER. Recalculated after\r\n CLASSIFY when actual UPL coverage is known.\r\n- `progress`: Tracks completion within each phase. Updated incrementally.\r\n\r\n---\r\n\r\n## DISCOVER Phase\r\n\r\n### Purpose\r\n\r\nProbe the SAP system to determine which data sources are available for analysis.\r\nNot every system has UPL enabled, SCMON configured, or Clean Core tables present.\r\nDISCOVER finds out what exists before INVENTORY and ANALYZE depend on it.\r\n\r\n### Session Resume\r\n\r\nIf `discovery.dataSources` already has results for all 12 probes in project.json,\r\nskip DISCOVER and move directly to INVENTORY. If only some probes have results,\r\nresume from the first missing probe.\r\n\r\n### Entry Criteria\r\n\r\n- INIT phase is complete.\r\n- `scope.packages` and `scope.namespaces` are populated in project.json.\r\n\r\n### Probe Execution\r\n\r\nRun all 12 probes via `sap_sql_query`. For each probe, record the result in\r\n`discovery.dataSources`. If a query fails with \"table not found\" or similar, set\r\n`available: false`. If it fails with an authorization error, set `available: false`\r\nwith `reason: \"authorization\"`. Log the gap but do NOT hard-stop.\r\n\r\n**Multi-namespace rule:** Every query that uses `LIKE '{prefix}%'` must run once\r\nPER namespace from `scope.namespaces`. A query with `LIKE 'Z%'` will MISS all\r\n`/OLDCO/` objects. Loop over `scope.namespaces` and run each query per namespace,\r\nmerging results.\r\n\r\n### The 12 Probes\r\n\r\n| # | Probe | What | Query/Method | Notes |\r\n|---|-------|------|-------------|-------|\r\n| 1 | TADIR | Object count | `SELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'` per package | Always exists |\r\n| 2 | TDEVC | Package hierarchy + wildcard resolution | Already done in INIT | Always exists |\r\n| 3 | TRDIR | Program metadata | `SELECT COUNT(*) FROM TRDIR WHERE NAME LIKE '{prefix}%'` | Always exists |\r\n| 4a-d | UPL variants | Usage logging | Try each: `/SDF/MON_RECS`, `/SDF/MON_HEADER`, `ZQLREC`, `/SDF/MON` with `SELECT * UP TO 1 ROWS FROM {table}` | First success wins |\r\n| 5 | SCMON | System call monitoring | `SELECT * UP TO 1 ROWS FROM /SDF/CUS_MEASURE` | S/4 only usually |\r\n| 6 | TBTCP | Batch job history | `SELECT COUNT(*) FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'` | Always exists |\r\n| 7 | SXC_ATTR | Active BAdI implementations | `SELECT IMP_NAME, EXIT_NAME FROM SXC_ATTR WHERE IMP_NAME LIKE '{prefix}%' AND ACTIVE = 'X'` | Run per namespace |\r\n| 8 | MODATTR + MODACT | Active CMOD user exit projects | See CMOD resolution below | Run per namespace |\r\n| 9 | ARS_W_API_STATE | Clean Core release states | `SELECT * UP TO 1 ROWS FROM ARS_W_API_STATE` | S/4 only |\r\n| 10 | ARS_W_API_SCCSSR | Successor API mapping | `SELECT * UP TO 1 ROWS FROM ARS_W_API_SCCSSR` | Probe separately |\r\n| 11 | TFDIR | RFC-enabled FMs | `SELECT FUNCNAME, FMODE FROM TFDIR WHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'` | Run per namespace |\r\n| 12 | ATC variant | S4HANA_READINESS check | Use `sap_atc_run` with variant parameter or query ATC config | Probe before ANALYZE |\r\n\r\n### Probe 1: TADIR (Object Count)\r\n\r\nRun per package in `scope.packages`:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TADIR WHERE DEVCLASS = '{pkg}'\r\n```\r\n\r\nSum the counts across all packages. Store in `discovery.dataSources.TADIR`:\r\n```json\r\n{ \"available\": true, \"rowCount\": 512 }\r\n```\r\n\r\n### Probe 2: TDEVC (Package Hierarchy)\r\n\r\nAlready completed during INIT wildcard resolution. Mark as available:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\n### Probe 3: TRDIR (Program Metadata)\r\n\r\nRun per namespace:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TRDIR WHERE NAME LIKE '{prefix}%'\r\n```\r\n\r\nMerge counts across namespaces. Store in `discovery.dataSources.TRDIR`:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\n### Probes 4a-d: UPL (Usage and Procedure Logging)\r\n\r\nTry each UPL table variant in order. Stop at the first one that succeeds:\r\n\r\n1. `/SDF/MON_RECS`\r\n2. `/SDF/MON_HEADER`\r\n3. `ZQLREC`\r\n4. `/SDF/MON`\r\n\r\nFor each, run:\r\n\r\n```sql\r\nSELECT * UP TO 1 ROWS FROM {table}\r\n```\r\n\r\nIf the query succeeds, parse the response to discover column names. Apply these\r\nheuristics to identify key columns:\r\n\r\n- **Program field:** Column name containing `PROG`, `PROGRAM`, or `OBJECT_NAME`.\r\n- **Count field:** Column name containing `COUNT`, `COUNTER`, or `EXECUTION`.\r\n\r\nIf heuristic matching fails, present the column names to the user and ask:\r\n\r\n```\r\nUPL table {table} found, but I cannot auto-detect the column mapping.\r\nColumns: {comma-separated column list}\r\n\r\nWhich column contains the program name?\r\nWhich column contains the execution counter?\r\n```\r\n\r\nStore the result in `discovery.dataSources.UPL`:\r\n```json\r\n{\r\n \"available\": true,\r\n \"table\": \"/SDF/MON_RECS\",\r\n \"progField\": \"PROG_NAME\",\r\n \"countField\": \"COUNTER\"\r\n}\r\n```\r\n\r\nIf none of the four tables exist, store:\r\n```json\r\n{ \"available\": false }\r\n```\r\n\r\n### Probe 5: SCMON (System Call Monitoring)\r\n\r\n```sql\r\nSELECT * UP TO 1 ROWS FROM /SDF/CUS_MEASURE\r\n```\r\n\r\nStore in `discovery.dataSources.SCMON`:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\nOr `{ \"available\": false }` if the table does not exist. SCMON is typically only\r\navailable on S/4HANA systems.\r\n\r\n### Probe 6: TBTCP (Batch Job History)\r\n\r\nRun per namespace:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'\r\n```\r\n\r\nMerge counts. Store in `discovery.dataSources.TBTCP`:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\n### Probe 7: SXC_ATTR (Active BAdI Implementations)\r\n\r\nRun per namespace:\r\n\r\n```sql\r\nSELECT IMP_NAME, EXIT_NAME FROM SXC_ATTR\r\nWHERE IMP_NAME LIKE '{prefix}%' AND ACTIVE = 'X'\r\n```\r\n\r\nMerge results across namespaces. Store count in `discovery.dataSources.SXC_ATTR`:\r\n```json\r\n{ \"available\": true, \"count\": 12 }\r\n```\r\n\r\n### Probe 8: MODATTR + MODACT (CMOD User Exit Projects)\r\n\r\nFirst, try a JOIN query:\r\n\r\n```sql\r\nSELECT NAME, MEMBER, TYP FROM MODSAP\r\nINNER JOIN MODACT ON MODSAP~NAME = MODACT~NAME\r\nWHERE MODACT~STATUS = 'A'\r\n```\r\n\r\nIf the JOIN fails (common on some ADT SQL endpoints), fall back to two sequential\r\nqueries:\r\n\r\n1. Get active projects:\r\n```sql\r\nSELECT NAME FROM MODACT WHERE STATUS = 'A'\r\n```\r\n\r\n2. Get members of active projects (use the names from step 1):\r\n```sql\r\nSELECT NAME, MEMBER, TYP FROM MODSAP WHERE NAME IN ({active_names})\r\n```\r\n\r\nMatch exit includes (names starting with `EXIT_` or `ZX`) against the inventory.\r\nTag matched objects with `isCMODExit = true` in later phases.\r\n\r\nStore in `discovery.dataSources.MODATTR`:\r\n```json\r\n{ \"available\": true, \"count\": 3 }\r\n```\r\n\r\n### Probe 9: ARS_W_API_STATE (Clean Core Release States)\r\n\r\n```sql\r\nSELECT * UP TO 1 ROWS FROM ARS_W_API_STATE\r\n```\r\n\r\nThis table exists only on S/4HANA systems with Clean Core metadata. Store:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\nOr `{ \"available\": false }` if the table does not exist.\r\n\r\n### Probe 10: ARS_W_API_SCCSSR (Successor API Mapping)\r\n\r\nProbe separately from probe 9 -- this table may exist independently:\r\n\r\n```sql\r\nSELECT * UP TO 1 ROWS FROM ARS_W_API_SCCSSR\r\n```\r\n\r\nStore in `discovery.dataSources.ARS_W_API_SCCSSR`:\r\n```json\r\n{ \"available\": true }\r\n```\r\n\r\nOr `{ \"available\": false }`.\r\n\r\n### Probe 11: TFDIR (RFC-Enabled Function Modules)\r\n\r\nRun per namespace:\r\n\r\n```sql\r\nSELECT FUNCNAME, FMODE FROM TFDIR\r\nWHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'\r\n```\r\n\r\nThis detects RFC-enabled function modules -- a critical blind spot because RFC\r\ninterfaces are often undocumented and may have external consumers that break during\r\nmigration.\r\n\r\nMerge results across namespaces. This count is SYSTEM-WIDE (all Z/Y/namespace\r\nFMs), not scoped to the selected packages. The per-package RFC count is\r\ndetermined in INVENTORY when individual FMs are mapped to packages via TFDIR.\r\n\r\nStore in `discovery.dataSources.TFDIR`:\r\n```json\r\n{ \"available\": true, \"rfcCountSystemWide\": 8 }\r\n```\r\n\r\nIn the DISCOVER summary, show: `\"RFC-enabled FMs: {n} system-wide (scoped count in INVENTORY)\"`\r\nDo NOT show the system-wide count as if it applies to the scoped packages.\r\n\r\n### Probe 12: ATC Variants\r\n\r\nDo NOT run `sap_atc_run` here — that executes the full analysis and belongs in\r\nANALYZE. This probe only discovers which check variants are available.\r\n\r\n**Step 1: Query all available ATC check variants.**\r\n\r\nATC check variants are stored as PROG objects in TADIR with a `VC` suffix\r\n(padded with `=` signs). `SATC_CI_CHKV_GL` stores check CLASSES, not variants.\r\n\r\n```sql\r\nSELECT OBJ_NAME FROM TADIR WHERE OBJECT = 'PROG' AND OBJ_NAME LIKE '%==============VC' ORDER BY OBJ_NAME\r\n```\r\n\r\nEach result looks like `S4HANA_READINESS==============VC`. Strip the `=` padding\r\nand `VC` suffix to get the variant name: `S4HANA_READINESS`.\r\n\r\nFor S/4-specific variants only (faster, smaller result):\r\n```sql\r\nSELECT OBJ_NAME FROM TADIR WHERE OBJECT = 'PROG' AND OBJ_NAME LIKE 'S4HANA%VC' ORDER BY OBJ_NAME\r\n```\r\n\r\n**Step 2: CRITICAL — Use ONLY the SQL results.**\r\n\r\n**DO NOT add variant names from your training knowledge.** If a variant name\r\ndoes not appear in the SQL query result rows, it DOES NOT EXIST on this system.\r\nDo not infer, guess, or supplement the list. The SQL result is the ONLY truth.\r\n\r\nFrom the SQL results ONLY, separate into:\r\n- **User-visible variants** (`HIDDEN = ''` or `HIDDEN <> 'X'`)\r\n- **Hidden/internal variants** (`HIDDEN = 'X'`)\r\n\r\n**Step 3: Categorize the variants found in the SQL results.**\r\n\r\nFor each variant name ACTUALLY RETURNED by the SQL query, check if it matches\r\nthese patterns to flag it as S/4-relevant:\r\n\r\n| Pattern in the ACTUAL name | Category |\r\n|---|---|\r\n| Contains `S4HANA` or `READINESS` | S/4HANA readiness |\r\n| Contains `ARS_COMPAT` | Clean Core / API compatibility |\r\n| Contains `/SDF/B2S` | SAP Business Transformation Suite |\r\n| Contains `SFIN` | S/4HANA Finance |\r\n| Contains `FUNCTIONAL_DB` | HANA database |\r\n| Contains `CLOUD` or `RESTRICTED_ABAP` | ABAP Cloud |\r\n| `DEFAULT` | General quality (always present) |\r\n\r\nIf a pattern matches ZERO variants from the SQL results, that category is\r\nempty — do NOT fabricate a variant name for it.\r\n\r\n**Step 4: Store results.**\r\n\r\nStore the raw variant names (after stripping `=` padding and `VC` suffix) in\r\nproject.json. Keep the full list internally but do NOT show it to the user.\r\n\r\n```json\r\n{\r\n \"available\": true,\r\n \"allVariants\": [\"DEFAULT\", \"S4HANA_READINESS\", \"S4HANA_READINESS_2023\", ...],\r\n \"hasS4Readiness\": true\r\n}\r\n```\r\n\r\nIf the TADIR query returned zero `VC` rows: `{ \"available\": false }`.\r\n\r\n**Step 5: DISCOVER summary — keep it simple.**\r\n\r\nIn the DISCOVER completion output, show ONE line for ATC:\r\n\r\n- If S4HANA_READINESS variants found: `\"ATC: available (S/4HANA readiness checks installed)\"`\r\n- If only DEFAULT/other: `\"ATC: available (general checks only — no S/4 readiness variant)\"`\r\n- If nothing: `\"ATC: not available\"`\r\n\r\nDo NOT list all 18 variant names in the DISCOVER summary. The user doesn't\r\nneed that detail yet. Variant selection happens in ANALYZE.\r\n\r\n**Step 6: Variant selection happens in ANALYZE, not DISCOVER.**\r\n\r\nDuring ANALYZE, ask the user's target release and recommend the right variant:\r\n\r\n```\r\nATC variant selection:\r\n\r\nWhat is your target S/4HANA release?\r\n 1. S/4HANA 2023 (latest)\r\n 2. S/4HANA 2022\r\n 3. S/4HANA 2021\r\n 4. S/4HANA 2020\r\n 5. Not sure / latest available\r\n 6. Type a custom variant name\r\n\r\n(Version-specific variants check for incompatibilities specific to that\r\ntarget release. Using the wrong version may miss or over-report findings.)\r\n```\r\n\r\nMap the user's answer to the matching variant from the discovered list:\r\n- S/4HANA 2023 → `S4HANA_READINESS_2023` (if available, else `S4HANA_READINESS`)\r\n- S/4HANA 2022 → `S4HANA_READINESS_2022`\r\n- Not sure → `S4HANA_READINESS` (generic, latest)\r\n\r\nIf the user's target release has no matching variant, use the closest available\r\nor the generic `S4HANA_READINESS`.\r\n\r\n### Authorization Errors\r\n\r\nIf any probe returns an authorization error (not \"table not found\" but \"no\r\nauthorization for table X\"), treat that data source as unavailable:\r\n\r\n```json\r\n{ \"available\": false, \"reason\": \"authorization\" }\r\n```\r\n\r\nLog the gap in the summary output. Do NOT hard-stop the entire DISCOVER phase for\r\na single authorization failure.\r\n\r\n### Data Availability Summary\r\n\r\nAfter all probes, summarize what data is available across TWO dimensions:\r\n\r\n**Technical data** (ATC + Clean Core) — always the primary deliverable:\r\n- ATC: available / not available (and which variant)\r\n- Clean Core: ARS tables available / not available\r\n\r\n**Usage data** (UPL + batch + cross-ref) — needed for keep/retire decisions:\r\n- UPL: available / not available\r\n- Batch: available / count\r\n- Cross-ref: always available (via sap_usage_references)\r\n\r\nStore `discovery.dataAvailability`:\r\n```json\r\n{\r\n \"technical\": { \"atc\": true, \"cleanCore\": true },\r\n \"usage\": { \"upl\": false, \"batch\": true, \"crossRef\": true },\r\n \"summary\": \"Technical analysis: FULL. Usage analysis: LIMITED (no UPL).\"\r\n}\r\n```\r\n\r\nDo NOT show a single \"confidence: LOW\" — that hides the reason. Show exactly\r\nwhat is available and what is missing. The user needs to know: \"Your technical\r\nreadiness assessment is complete. Usage data is missing — here's how to get it.\"\r\n\r\n### Save Results\r\n\r\nAfter all 12 probes complete, update project.json with the full `discovery` object.\r\nSet `currentPhase` to `\"DISCOVER\"` and update all `packagePhases` entries from\r\n`\"INIT\"` to `\"DISCOVER\"`.\r\n\r\n### DISCOVER Phase Completion\r\n\r\nPrint this summary:\r\n\r\n```\r\nDISCOVER complete. Data sources found:\r\n TADIR: {rowCount} objects across {pkg_count} packages\r\n UPL: {available/not} ({table name if found})\r\n Batch jobs: {count} programs with scheduled jobs\r\n BAdI implementations: {count} active\r\n CMOD exits: {count} active\r\n RFC-enabled FMs: {count}\r\n ARS_W_API_STATE: {available/not} (Clean Core check {enabled/disabled})\r\n ARS_W_API_SCCSSR: {available/not} (Successor API mapping {enabled/disabled})\r\n ATC variant: {variant name or \"not found\"}\r\n Confidence: {HIGH/MEDIUM/LOW}\r\n```\r\n\r\nIf any data sources are unavailable, append:\r\n\r\n```\r\nGaps:\r\n - {source}: {reason}\r\n```\r\n\r\n**If UPL is unavailable AND batch data is zero (confidence = LOW):**\r\n\r\nRuntime usage data is critical for accurate classification. Without it, most\r\nobjects will be classified as DORMANT or UNKNOWN. Before proceeding, offer the\r\nuser options to get production usage data:\r\n\r\n```\r\nNo runtime usage data found on this system. Without it, classification\r\nconfidence will be LOW — most objects will need manual review.\r\n\r\nOptions:\r\n 1. Continue anyway (static analysis only — fast but low confidence)\r\n 2. Import PRD usage data (if you have an SE16 export or SolMan extract)\r\n 3. I'll get usage data and come back (pause here, resume later)\r\n\r\nIf you have a CSV/spreadsheet with program names and execution counts from\r\nproduction, I can import it to dramatically improve classification accuracy.\r\n```\r\n\r\nIf user picks 1 → proceed to INVENTORY.\r\nIf user picks 2 → ask for the file path. Parse program name + count columns.\r\nStore as UPL-equivalent data in objects.json during ANALYZE. Set confidence\r\nto MEDIUM.\r\nIf user picks 3 → save checkpoint, print resume instructions, stop.\r\n\r\nPhase complete. Move to INVENTORY.\r\n\r\n---\r\n\r\n## objects.json Per-Object Schema\r\n\r\nThis file stores per-object metadata at\r\n`.cspeach/cca/projects/{id}/objects.json`. Each top-level key is the SAP object\r\nname. Create the file during INVENTORY, then enrich it during ANALYZE and CLASSIFY.\r\n\r\n```json\r\n{\r\n \"ZFI_AGING_REPORT\": {\r\n \"objectType\": \"PROG\",\r\n \"package\": \"ZCUSTOM_FI\",\r\n \"author\": \"JSMITH\",\r\n \"lastChangedDate\": \"20230412\",\r\n \"isTestObject\": false,\r\n \"isRFCEnabled\": false,\r\n \"isEnhancement\": false,\r\n \"isCMODExit\": false,\r\n \"compliance\": \"NONE\",\r\n \"usage\": {\r\n \"uplRuns\": 45230,\r\n \"batchRuns\": 0,\r\n \"lastRunDate\": \"20260401\",\r\n \"hasStaticCallers\": true,\r\n \"usageSource\": \"UPL\"\r\n },\r\n \"atcFindings\": [\r\n { \"priority\": \"1\", \"messageId\": \"...\", \"category\": \"tableReplacement\" }\r\n ],\r\n \"cleanCore\": {\r\n \"ready\": true,\r\n \"unreleased\": [],\r\n \"successors\": []\r\n },\r\n \"technicalReadiness\": {\r\n \"status\": \"NEEDS_FIX\",\r\n \"atcSummary\": \"1 P1 (BSEG table replacement)\",\r\n \"cleanCoreReady\": true,\r\n \"cloudReady\": false\r\n },\r\n \"usageStatus\": {\r\n \"status\": \"ACTIVE\",\r\n \"evidence\": \"UPL=45230 runs, last run 2026-04-01\",\r\n \"source\": \"UPL\"\r\n },\r\n \"classification\": {\r\n \"primary\": \"REFACTOR\",\r\n \"secondary\": [\"HAS_ATC_FINDINGS\"],\r\n \"evidence\": \"Active (UPL=45230) + needs fix (BSEG replacement)\",\r\n \"confidence\": \"HIGH\"\r\n },\r\n \"humanReview\": {\r\n \"used\": null,\r\n \"decision\": null,\r\n \"comments\": null,\r\n \"overrideReason\": null,\r\n \"reviewedBy\": null\r\n }\r\n }\r\n}\r\n```\r\n\r\n### Schema Notes\r\n\r\n- Each object is keyed by its SAP object name (e.g., `ZFI_AGING_REPORT`).\r\n- `objectType`: One of `PROG`, `CLAS`, `FUGR`, `FUNC`, `TABL`, `DTEL`, `DOMA`,\r\n `DDLS`, `SRVD`, `SRVB`, `INTF`, `MSAG`, `TTYP`, `VIEW`, or any other TADIR\r\n object type code.\r\n- `compliance`: Inherited from the package-level tag in `scope.compliance`, or\r\n overridden at object level via `scope.complianceOverrides`. Default is `\"NONE\"`.\r\n- `usage`: Populated during ANALYZE. All fields are `null` until then.\r\n- `technicalReadiness`: Populated at end of ANALYZE from ATC + Clean Core data.\r\n This is INDEPENDENT of usage data. Values: `CLEAN` (no issues), `MINOR_FIX`\r\n (P2/P3 only), `NEEDS_FIX` (P1 findings — table replacements etc.),\r\n `NEEDS_REDESIGN` (structural issues), `CLOUD_READY` (RAP framework object),\r\n `NOT_CHECKED` (ATC didn't run). This dimension is ALWAYS available — it does\r\n not require UPL.\r\n- `usageStatus`: Populated during CLASSIFY from UPL/batch/cross-ref. Values:\r\n `ACTIVE` (UPL confirms), `BATCH_ACTIVE` (batch jobs confirm), `DORMANT`\r\n (no recent usage), `UNVERIFIED` (no usage data available — cannot determine),\r\n `RETIRE_CANDIDATE` (confirmed unused >36 months). When UPL is unavailable,\r\n most objects will be `UNVERIFIED` — this is honest, not misleading.\r\n- `classification`: The combined recommendation derived from technicalReadiness\r\n + usageStatus. Populated during CLASSIFY. This is what appears on worksheets\r\n and reports.\r\n- `humanReview`: Populated during IMPORT when review worksheets are returned. All\r\n fields are `null` until the REVIEW phase generates worksheets and the user\r\n returns completed responses.\r\n- For FUGR objects: Store both the FUGR entry AND a separate FUNC entry for each\r\n child function module. The FUNC entry's `objectType` is `\"FUNC\"` and its key is\r\n the function module name (e.g., `Z_CALCULATE_TAX`). Cross-reference the parent\r\n FUGR by storing `\"parentFugr\": \"ZFUGR_NAME\"` on each FUNC entry.\r\n\r\n---\r\n\r\n## INVENTORY Phase\r\n\r\n### Purpose\r\n\r\nBuild the complete object inventory from TADIR and enrich it with metadata from\r\nTRDIR, SEOCLASS, TFDIR, and cross-reference tables. Produce objects.json with one\r\nentry per custom object.\r\n\r\n### Session Resume\r\n\r\nIf `progress.inventory.completedPackages` contains packages, skip those. Resume\r\nfrom the first package in `scope.packages` that is NOT in `completedPackages`.\r\n\r\n### Entry Criteria\r\n\r\n- DISCOVER phase complete.\r\n- `discovery.dataSources` populated in project.json.\r\n- `scope.packages` and `scope.namespaces` populated.\r\n\r\n### Pagination Strategy\r\n\r\nLarge systems can have thousands of objects per package. Use keyset pagination to\r\navoid timeouts and result truncation.\r\n\r\n**Step 1: Get the package list from `scope.packages`.**\r\n\r\n**Step 2: For each package, query TADIR:**\r\n\r\n```sql\r\nSELECT OBJ_NAME, OBJECT, DEVCLASS FROM TADIR\r\nWHERE DEVCLASS = '{package}'\r\nORDER BY OBJ_NAME\r\n```\r\n\r\nUse keyset pagination if results exceed maxRows. Take the last `OBJ_NAME` from\r\nthe current result set and issue the next query:\r\n\r\n```sql\r\nSELECT OBJ_NAME, OBJECT, DEVCLASS FROM TADIR\r\nWHERE DEVCLASS = '{package}' AND OBJ_NAME > '{last_name}'\r\nORDER BY OBJ_NAME\r\n```\r\n\r\nRepeat until the query returns fewer rows than maxRows.\r\n\r\n**Step 3: Sub-paginate by object type if needed.** If a single package has too\r\nmany objects for even the keyset approach, break the query down by object type:\r\n\r\n```sql\r\nSELECT OBJ_NAME, OBJECT FROM TADIR\r\nWHERE DEVCLASS = '{package}' AND OBJECT = 'PROG'\r\nORDER BY OBJ_NAME\r\n```\r\n\r\nThen CLAS, FUGR, TABL, DTEL, DOMA, DDLS, and so on. Apply keyset pagination\r\nwithin each object type if necessary.\r\n\r\n### TRDIR Enrichment (Programs)\r\n\r\nAfter collecting TADIR entries, enrich program metadata. Run per namespace:\r\n\r\n```sql\r\nSELECT NAME, SUBC, CNAM, CDAT, UNAM, UDAT FROM TRDIR\r\nWHERE NAME LIKE '{prefix}%'\r\n```\r\n\r\nMap the results onto the objects.json entries:\r\n- `author` = `CNAM`\r\n- `lastChangedDate` = `UDAT` (or `CDAT` if `UDAT` is empty)\r\n- `isTestObject` = `true` if `SUBC = 'T'`\r\n\r\n### SEOCLASS Enrichment (Classes)\r\n\r\nRun per namespace:\r\n\r\n```sql\r\nSELECT CLSNAME, AUTHOR, CREATEDON, CHANGEDBY, CHANGEDON FROM SEOCLASS\r\nWHERE CLSNAME LIKE '{prefix}%'\r\n```\r\n\r\nMap the results onto class entries in objects.json:\r\n- `author` = `AUTHOR`\r\n- `lastChangedDate` = `CHANGEDON` (or `CREATEDON` if `CHANGEDON` is empty)\r\n\r\n### FUGR to FM Resolution\r\n\r\nFor every FUGR found in TADIR, resolve individual function modules. Query TFDIR:\r\n\r\n```sql\r\nSELECT FUNCNAME, PNAME FROM TFDIR WHERE PNAME LIKE 'SAPL{fugr_name}'\r\n```\r\n\r\nNote: TFDIR stores the FM's program name as `SAPL{FUGR_NAME}` (uppercase, prefixed\r\nwith `SAPL`).\r\n\r\nFor each function module returned, create a separate FUNC entry in objects.json:\r\n\r\n```json\r\n{\r\n \"Z_CALCULATE_TAX\": {\r\n \"objectType\": \"FUNC\",\r\n \"package\": \"ZCUSTOM_FI\",\r\n \"parentFugr\": \"ZFUGR_TAX\",\r\n \"author\": null,\r\n \"lastChangedDate\": null,\r\n \"isTestObject\": false,\r\n \"isRFCEnabled\": false,\r\n \"isEnhancement\": false,\r\n \"isCMODExit\": false,\r\n \"compliance\": \"NONE\",\r\n \"usage\": null,\r\n \"atcFindings\": null,\r\n \"cleanCore\": null,\r\n \"classification\": null,\r\n \"humanReview\": null\r\n }\r\n}\r\n```\r\n\r\n### RFC Detection Per FM\r\n\r\nCross-reference with DISCOVER probe 11 results (TFDIR with `FMODE='R'`). For each\r\nfunction module in objects.json, check if it appears in the RFC-enabled FM list.\r\nSet `isRFCEnabled = true` on matching entries.\r\n\r\nIf probe 11 results were not cached, re-query per namespace:\r\n\r\n```sql\r\nSELECT FUNCNAME FROM TFDIR\r\nWHERE FUNCNAME LIKE '{prefix}%' AND FMODE = 'R'\r\n```\r\n\r\n### DDIC Dependency Mapping\r\n\r\n**Tables to consuming programs:** Run per namespace:\r\n\r\n```sql\r\nSELECT TABNAME, MASTER FROM D010TAB WHERE TABNAME LIKE '{prefix}%'\r\n```\r\n\r\nStore a `dependentPrograms` list on each table object in objects.json. This data\r\nfeeds the CLASSIFY phase (Rule 3: tables with many consumers are harder to retire).\r\n\r\n**Data elements to consuming tables:** Run per namespace:\r\n\r\n```sql\r\nSELECT TABNAME, FIELDNAME, ROLLNAME FROM DD03L\r\nWHERE ROLLNAME LIKE '{prefix}%' AND AS4LOCAL = 'A'\r\n```\r\n\r\nStore the consuming tables on each data element entry. This data feeds impact\r\nanalysis during CLASSIFY.\r\n\r\n### Transport Lock Check\r\n\r\nIdentify objects currently locked in active transports. Run per namespace:\r\n\r\n```sql\r\nSELECT OBJ_NAME, OBJECT FROM E071 WHERE OBJ_NAME LIKE '{prefix}%'\r\n```\r\n\r\nThen check the transport status for the returned transport requests:\r\n\r\n```sql\r\nSELECT TRKORR, TRSTATUS FROM E070 WHERE TRKORR IN ({transport_list})\r\n```\r\n\r\nObjects in active transports (`TRSTATUS = 'D'` modifiable or `TRSTATUS = 'L'`\r\nmodifiable protected) get `inActiveTransport = true` in objects.json. This is an\r\ninformational flag -- it does not block classification but is surfaced in the\r\nREPORT phase.\r\n\r\n### Test Object Detection\r\n\r\nApply these rules to flag test objects:\r\n\r\n- Programs with `SUBC = 'T'` from TRDIR enrichment: set `isTestObject = true`.\r\n- Objects with names ending in `_TEST`: set `isTestObject = true`.\r\n- Objects in packages whose name contains `_TEST` or `_UNIT`: set\r\n `isTestObject = true`.\r\n\r\nTest objects are auto-classified as `TEST` during CLASSIFY -- they skip all other\r\nclassification rules.\r\n\r\n### BAdI / Enhancement Tagging\r\n\r\nCross-reference with DISCOVER probe 7 (SXC_ATTR) results:\r\n- For each BAdI implementation class found in probe 7, locate the matching CLAS\r\n entry in objects.json and set `isEnhancement = true`.\r\n\r\nCross-reference with DISCOVER probe 8 (MODATTR/MODACT) results:\r\n- For each CMOD exit include found in probe 8, locate the matching PROG or\r\n include entry in objects.json and set `isCMODExit = true`.\r\n\r\n### Compliance Inheritance\r\n\r\nFor each object, set the `compliance` field:\r\n1. Check `scope.complianceOverrides` for an object-level override (key format:\r\n `PACKAGE/OBJECT_NAME`). If found, use that tag.\r\n2. Otherwise, inherit from `scope.compliance` using the object's package.\r\n3. Default to `\"NONE\"` if neither is set.\r\n\r\n### Checkpoint and Save\r\n\r\nSave objects.json every 5 packages. Update `progress.inventory.completedPackages`\r\nwith the packages just processed. On resume, skip completed packages.\r\n\r\nAfter each checkpoint, also update project.json:\r\n- Add completed packages to `progress.inventory.completedPackages`.\r\n- Move completed packages from `progress.inventory.pendingPackages`.\r\n- Update `packagePhases` for completed packages to `\"INVENTORY\"`.\r\n\r\n### SQL Gotchas\r\n\r\n- Underscore `_` is a single-character wildcard in ABAP SQL LIKE clauses. Use\r\n exact match (`DEVCLASS = '{package}'`) instead of LIKE with underscores wherever\r\n possible. If LIKE is unavoidable, escape underscores with `#_` (ABAP escape\r\n character).\r\n- Namespace slashes in table names (e.g., `/SDF/MON_RECS`) -- ADT handles this\r\n natively but verify that the slash encoding does not corrupt the query.\r\n- No JOINs in V1 of the SQL tool. Use separate queries and merge results in the\r\n skill logic.\r\n\r\n### INVENTORY Phase Completion\r\n\r\nPrint this summary:\r\n\r\n```\r\nINVENTORY complete.\r\n Total objects: {n} across {pkg_count} packages\r\n Programs: {n}\r\n Classes: {n}\r\n Function Groups: {n} ({fm_count} function modules)\r\n Tables: {n}\r\n Other: {n}\r\n Test objects: {n} (will be classified as TEST)\r\n RFC-enabled FMs: {n}\r\n Active BAdI implementations: {n}\r\n CMOD exits: {n}\r\n Objects in active transports: {n}\r\n\r\nPhase complete. Move to ANALYZE.\r\n```\r\n\r\nUpdate project.json: set `currentPhase` to `\"INVENTORY\"` and update all\r\n`packagePhases` entries from `\"DISCOVER\"` to `\"INVENTORY\"`.\r\n\r\nPhase complete. Move to ANALYZE.\r\n\r\n---\r\n\r\n## ANALYZE Phase\r\n\r\n### Purpose\r\n\r\nCollect three categories of data for every object in the inventory: usage data\r\n(UPL + batch + cross-references), ATC findings, and Clean Core readiness. Each\r\ncategory is independent and can be collected in any order.\r\n\r\n### Session Resume\r\n\r\nCheck `progress.analysis` in project.json:\r\n- If `usageCollected` is `true`, skip Part 1 (usage collection).\r\n- If `atcCompleted` contains package names, skip those packages in Part 3.\r\n- If `crossRefCompleted` is non-zero, resume cross-reference analysis from that\r\n offset.\r\n- If all three parts are complete, skip ANALYZE and move to CLASSIFY.\r\n\r\n### Entry Criteria\r\n\r\n- INVENTORY phase complete.\r\n- objects.json populated with all objects and metadata.\r\n- `discovery.dataSources` populated from DISCOVER phase.\r\n\r\n### Multi-Namespace Rule\r\n\r\nEvery query using `LIKE '{prefix}%'` runs once PER namespace from\r\n`scope.namespaces`. A query with `LIKE 'Z%'` will miss `/ACME/` objects. Loop\r\nover `scope.namespaces` and run each query per namespace, merging results.\r\n\r\n### Authorization Errors\r\n\r\nIf any query returns an authorization error, treat that data source as\r\nunavailable. Log the gap and reduce confidence. Do not hard-stop.\r\n\r\n---\r\n\r\n### Part 1: Usage Data Collection\r\n\r\n#### UPL Collection\r\n\r\nOnly run if `discovery.dataSources.UPL.available = true`. Use the field names\r\ndiscovered in DISCOVER: `discovery.dataSources.UPL.progField` and\r\n`discovery.dataSources.UPL.countField`. Use the table name from\r\n`discovery.dataSources.UPL.table`.\r\n\r\nUse keyset pagination. No aggregate SQL (SUM/GROUP BY not guaranteed on all ADT\r\nendpoints). No offset parameter in `sap_sql_query`.\r\n\r\n```sql\r\n-- Page 1 (run per namespace):\r\nSELECT {progField}, {countField} FROM {uplTable}\r\nWHERE {progField} LIKE '{prefix}%'\r\nORDER BY {progField}\r\n\r\n-- Page 2+ (using last program name from previous page):\r\nSELECT {progField}, {countField} FROM {uplTable}\r\nWHERE {progField} LIKE '{prefix}%' AND {progField} > '{last_name}'\r\nORDER BY {progField}\r\n```\r\n\r\nSet maxRows to 5000 (default). Keep paginating until returned rows < maxRows.\r\n\r\n**Skill-side aggregation:** Process each page immediately -- sum counts per\r\nprogram name, track max date. Do NOT accumulate all pages in memory.\r\nProcess-and-discard per page. Update objects.json incrementally.\r\n\r\n**Important: UPL tracks PROGRAMS, not classes.** Classes show zero UPL. This is\r\na known data gap handled in CLASSIFY via transitive classification.\r\n\r\nFor each matched program in objects.json, update:\r\n- `usage.uplRuns`: summed count value\r\n- `usage.lastRunDate`: most recent date if available\r\n- `usage.usageSource`: `\"UPL\"`\r\n\r\n#### Batch Job Collection\r\n\r\nAlways available. Two queries, no JOIN. Run per namespace:\r\n\r\n```sql\r\nSELECT PROGNAME, JOBNAME, JOBCOUNT FROM TBTCP WHERE PROGNAME LIKE '{prefix}%'\r\n```\r\n\r\nThen for each distinct JOBNAME found:\r\n\r\n```sql\r\nSELECT JOBNAME, JOBCOUNT, STRTDATE FROM TBTCO WHERE STATUS = 'F' AND JOBNAME = '{job}'\r\n```\r\n\r\nFor each object matched, update:\r\n- `usage.batchRuns`: count of finished job executions\r\n- `usage.lastRunDate`: most recent STRTDATE\r\n- `usage.usageSource`: set to `\"BATCH\"` if no UPL data exists, or keep `\"UPL\"`\r\n if UPL data already present\r\n\r\n#### Active Enhancement Verification\r\n\r\nCross-reference SXC_ATTR BAdI implementations (from DISCOVER probe 7) and\r\nMODATTR/MODACT CMOD exits (from DISCOVER probe 8) with the objects in inventory.\r\nFor matched objects:\r\n- Set `isEnhancement = true` for BAdI implementation classes.\r\n- Set `isCMODExit = true` for CMOD exit includes.\r\n\r\nThese flags are already set during INVENTORY but verify and update here if new\r\ndata is available from re-probing.\r\n\r\n#### Usage Collection Checkpoint\r\n\r\nAfter all usage data is collected, set `progress.analysis.usageCollected = true`\r\nin project.json. Save objects.json.\r\n\r\n---\r\n\r\n### Part 2: Cross-Reference Analysis (Tiered)\r\n\r\nThe cross-reference budget is split into tiers. Tier 2 is SEPARATE from Tier 1,\r\nnot shared.\r\n\r\n| Tier | What | Budget | Priority |\r\n|---|---|---|---|\r\n| Tier 1 | Classes with zero UPL | Up to 500 calls | All classes |\r\n| Tier 2 | RFC-enabled FMs (from TFDIR probe) | Up to 50 calls (separate) | All RFC FMs |\r\n| Tier 3 | Programs with zero UPL, zero batch | Remaining from Tier 1 | Best effort |\r\n\r\n**Total cap: 550 calls** (500 Tier 1 + 50 Tier 2).\r\n\r\n#### Tier 1 Execution\r\n\r\nFor each class in objects.json with zero UPL data, call `sap_usage_references`\r\nto find callers. Store callers in the object's `usage` data for use in\r\ntransitive classification during CLASSIFY.\r\n\r\nIf there are more than 500 classes needing cross-references, ask the user which\r\npackages to prioritize:\r\n\r\n```\r\n{count} classes need cross-reference analysis. Budget is 500.\r\nWhich packages should I prioritize? (critical business packages first)\r\n```\r\n\r\nFor each call, update:\r\n- `usage.hasStaticCallers`: `true` if any callers found, `false` if none\r\n- Store caller list for CLASSIFY transitive analysis\r\n\r\n#### Tier 2 Execution\r\n\r\nFor each RFC-enabled FM (`isRFCEnabled = true` in objects.json), call\r\n`sap_usage_references` to find internal callers. This budget is separate from\r\nTier 1.\r\n\r\nFor each call, update:\r\n- `usage.hasStaticCallers`: `true` if any callers found, `false` if none\r\n\r\n#### Tier 3 Execution\r\n\r\nUse whatever remains from the 500 Tier 1 budget for programs with zero UPL AND\r\nzero batch jobs.\r\n\r\n#### Cross-Reference Progress\r\n\r\nReport every 50 calls: `\"Cross-reference: {n}/{total} completed.\"`\r\n\r\nCheckpoint: Save objects.json and update `progress.analysis.crossRefCompleted`\r\nevery 100 calls.\r\n\r\nObjects beyond the budget: set `usage.usageSource = \"NONE\"` with note\r\n`\"Cross-reference skipped due to volume.\"`.\r\n\r\n---\r\n\r\n### Part 3: ATC Analysis\r\n\r\n#### Honest Timing\r\n\r\nFor 35+ packages, ATC takes 3-6 hours. Tell the user upfront:\r\n\r\n```\r\nATC analysis for {count} packages will take approximately {estimate}.\r\nProgress saves after each package. Sessions will likely need to resume.\r\nProceed? [y/n]\r\n```\r\n\r\nIf the user declines, skip ATC and note the gap. All other phases continue\r\nwithout ATC data.\r\n\r\n#### ATC Variant Selection\r\n\r\nUse the variants discovered in Probe 12 (`discovery.dataSources.ATC_VARIANT`).\r\n\r\n**If S/4-relevant variants were found** (e.g., `ARS_COMPATIBILIY_CHECK`,\r\n`S4HANA_READINESS`, `/SDF/B2S_SFIN`), recommend the best one:\r\n\r\n```\r\nATC check variants available on this system:\r\n\r\n S/4-relevant:\r\n {list s4Relevant variants}\r\n\r\n General:\r\n DEFAULT — standard code quality\r\n\r\n Recommended: {recommended variant}\r\n\r\n Which variant? (type name, or press Enter for recommended)\r\n You can also type a custom variant name if you have one.\r\n```\r\n\r\n**If no S/4-relevant variants found** but DEFAULT exists:\r\n\r\n```\r\nNo S/4HANA-specific ATC variant found on this system.\r\nAvailable: DEFAULT (general code quality only — will NOT detect\r\ntable replacements like BSEG/KONV/VBUK or obsolete APIs).\r\n\r\nOptions:\r\n 1. Use DEFAULT anyway (better than nothing)\r\n 2. Type a custom variant name (if your team created one)\r\n 3. Ask Basis to install S/4 readiness checks (pause and resume later)\r\n 4. Skip ATC entirely (not recommended)\r\n\r\nWhich option?\r\n```\r\n\r\n**If no variants found at all:** skip ATC, note the gap.\r\n\r\n**Custom variant:** The user may have a project-specific variant created by\r\ntheir Basis team or a consulting partner. Accept any variant name the user\r\ntypes — `sap_atc_run` will validate it. If it fails, report the error and\r\nask for another name.\r\n\r\nAfter variant selection, proceed with ATC execution per package.\r\n\r\n#### ATC Execution\r\n\r\nRun `sap_atc_run` per package with the selected variant:\r\n\r\n```\r\nsap_atc_run (scope: \"{package}\", variant: \"{variant}\")\r\n```\r\n\r\nParse results. For each finding, update the corresponding object in objects.json:\r\n\r\n```json\r\n{\r\n \"priority\": \"1\",\r\n \"messageId\": \"CHECK_BSEG_ACCESS\",\r\n \"category\": \"tableReplacement\"\r\n}\r\n```\r\n\r\nAppend each finding to the object's `atcFindings` array.\r\n\r\n#### ATC Checkpoint\r\n\r\nSave objects.json after each package completes. Update\r\n`progress.analysis.atcCompleted` with the package name. On resume, skip\r\ncompleted packages.\r\n\r\n#### System Load Management\r\n\r\nATC runs consume work processes. On shared development systems, add a\r\nconfigurable delay (default 30 seconds) between package ATC runs:\r\n\r\n```\r\nNote: Adding 30-second delay between ATC runs to reduce system load.\r\nTo change: \"Set delay to 0 seconds\" or \"Set delay to 60 seconds\"\r\n```\r\n\r\nIf the user requests a different delay, apply it for all subsequent ATC runs in\r\nthe session.\r\n\r\n#### Timeout Handling\r\n\r\nIf `sap_atc_run` takes longer than 5 minutes for one package, save the status as\r\n`\"pending\"` for that package in `progress.analysis.atcCompleted`. On the next\r\nsession, check if results are available before re-running.\r\n\r\n---\r\n\r\n### Part 4: Clean Core Analysis\r\n\r\nOnly run if `discovery.dataSources.ARS_W_API_STATE.available = true`. On ECC\r\nsystems without this table, skip entirely and set all `cleanCore` fields to\r\n`null`.\r\n\r\n#### Step 1: Extract SAP API Dependencies Per Custom Object\r\n\r\nQuery D010TAB for which SAP standard tables each custom program uses. Run per\r\nnamespace:\r\n\r\n```sql\r\nSELECT MASTER, TABNAME FROM D010TAB\r\nWHERE MASTER LIKE '{prefix}%'\r\nAND TABNAME NOT LIKE 'Z%' AND TABNAME NOT LIKE 'Y%'\r\nAND TABNAME NOT LIKE '/%'\r\n```\r\n\r\nThis gives: custom program -> SAP standard tables it references.\r\n\r\n#### Step 2: Check Each SAP Dependency Against ARS_W_API_STATE\r\n\r\nCollect all unique SAP table/view names from Step 1. Check in bulk using\r\n`sap_api_state`:\r\n\r\n```\r\nsap_api_state (objects: \"TABL:VBAK,TABL:BSEG,TABL:KONV,...\")\r\n```\r\n\r\nBatch in groups of 20-30 per call to avoid request size limits.\r\n\r\n#### Step 3: Tag Each Custom Object\r\n\r\nFor each custom object, based on its SAP dependencies:\r\n- ALL dependencies released -> `cleanCore.ready = true`\r\n- ANY dependency not released -> `cleanCore.ready = false`, record unreleased\r\n APIs in `cleanCore.unreleased`\r\n- No dependencies extracted -> `cleanCore.ready = \"unknown\"`\r\n\r\n#### Step 4: Successor Lookup\r\n\r\nFor unreleased APIs, `sap_api_state` already returns successor information from\r\nARS_W_API_SCCSSR. Store successors in `cleanCore.successors` on the affected\r\nobject:\r\n\r\n```json\r\n{\r\n \"cleanCore\": {\r\n \"ready\": false,\r\n \"unreleased\": [\"BSEG\", \"KONV\"],\r\n \"successors\": [\r\n { \"old\": \"BSEG\", \"new\": \"I_JournalEntryItem\", \"type\": \"CDS\" },\r\n { \"old\": \"KONV\", \"new\": \"I_PricingConditionRecord\", \"type\": \"CDS\" }\r\n ]\r\n }\r\n}\r\n```\r\n\r\n#### Clean Core Limitation\r\n\r\nD010TAB captures TABLE references only. FM/class API dependencies require\r\ncross-reference tables (WBCROSSGT) which may be too large to query. For V1,\r\nClean Core check covers TABLE dependencies only. Document this limitation in the\r\nREPORT phase.\r\n\r\n---\r\n\r\n### ANALYZE Phase Completion\r\n\r\nPrint this summary:\r\n\r\n```\r\nANALYZE complete.\r\n Objects with UPL data: {n} ({pct}%)\r\n Objects with batch data: {n}\r\n Cross-references completed: {n}\r\n ATC findings: {n} total ({p1} priority 1, {p2} priority 2)\r\n Clean Core checked: {n} ({ready_count} cloud-ready, {not_ready} needs migration)\r\n Objects with no usage data: {n} ({pct}%)\r\n\r\nPhase complete. Move to CLASSIFY.\r\n```\r\n\r\nUpdate project.json: set `currentPhase` to `\"ANALYZE\"` and update all\r\n`packagePhases` entries from `\"INVENTORY\"` to `\"ANALYZE\"`.\r\n\r\nSave objects.json with all collected analysis data.\r\n\r\n### Compute Technical Readiness (end of ANALYZE)\r\n\r\nBefore moving to CLASSIFY, compute `technicalReadiness` for every object. This\r\nuses ATC + Clean Core data ONLY — no usage data needed. This dimension is always\r\navailable and always meaningful, even on DEV systems with zero UPL.\r\n\r\nFor each object in objects.json:\r\n\r\n| Condition | technicalReadiness.status |\r\n|---|---|\r\n| RAP framework object (BDEF, DDLS, DDLX, DCLS, SRVD, SRVB, STOB, G4BA) | `CLOUD_READY` |\r\n| Zero ATC findings AND cleanCore.ready = true | `CLEAN` |\r\n| Zero ATC findings AND cleanCore.ready = false | `NEEDS_FIX` (unreleased APIs) |\r\n| Zero ATC findings AND cleanCore.ready = unknown | `CLEAN` (no known issues) |\r\n| Only P2/P3 ATC findings, no P1 | `MINOR_FIX` |\r\n| Any P1 ATC findings (table replacements, structural) | `NEEDS_FIX` |\r\n| P1 ATC + cleanCore.ready = false (double impact) | `NEEDS_REDESIGN` |\r\n| ATC not run (skipped or unavailable) | `NOT_CHECKED` |\r\n\r\nPrint technical readiness summary:\r\n\r\n```\r\nTechnical Readiness (independent of usage data):\r\n CLOUD_READY: {n} — RAP/ABAP Cloud objects, no changes needed\r\n CLEAN: {n} — no S/4 compatibility issues found\r\n MINOR_FIX: {n} — minor issues (P2/P3), quick fixes\r\n NEEDS_FIX: {n} — S/4 incompatibilities (table replacements, unreleased APIs)\r\n NEEDS_REDESIGN: {n} — significant rework needed\r\n NOT_CHECKED: {n} — ATC didn't run\r\n\r\n Clean Core coverage: {n} of {total} programs checked\r\n {If n < total: \"{gap} programs had no SAP table dependencies in D010TAB — Clean Core status unknown for those.\"}\r\n```\r\n\r\nThis is the answer to \"which objects will BREAK on S/4HANA\" — regardless of\r\nwhether anyone runs them.\r\n\r\nPhase complete. Move to CLASSIFY.\r\n\r\n---\r\n\r\n## CLASSIFY Phase\r\n\r\nAssign a usage-based classification and combine it with technical readiness to\r\nproduce the final recommendation. The CLASSIFY phase adds the usage dimension\r\n(UPL, batch, cross-references) on top of the technical readiness computed in\r\nANALYZE.\r\n\r\n### CRITICAL: No-UPL System Behavior\r\n\r\nWhen `discovery.dataSources.UPL.available = false`, the usage-based rules\r\nchange fundamentally:\r\n\r\n**Rules 1-4 (active usage) CANNOT fire** — they require runtime data.\r\n**Rules 10-15 (dormant/retire based on zero usage) CANNOT fire** — \"zero\r\nruntime\" means \"no data,\" not \"not used.\" Classifying as DORMANT when we\r\nsimply have no measurement is dishonest.\r\n\r\nOn no-UPL systems, usage status for programs/classes is:\r\n- `UNVERIFIED` — default when no UPL data. Honest: \"we don't know.\"\r\n- `BATCH_ACTIVE` — if batch job data confirms execution (batch IS available)\r\n- `HAS_CALLERS` — if cross-reference found callers (static evidence)\r\n- `ACTIVE` — only if user imports PRD usage data confirming execution\r\n\r\nDo NOT classify any program as `DORMANT` or `RETIRE_CANDIDATE` solely because\r\nUPL shows zero. UPL doesn't exist on this system — zero is not a measurement,\r\nit's an absence of data.\r\n\r\n**Rules 5-7 (enhancement/CMOD) still fire** — structural, not runtime.\r\n**Rule 0b (RFC) still fires** — structural.\r\n**Rule 17b (RAP objects) still fires** — architectural.\r\n**Rules 8-9 (transitive) still fire** but callers may be UNVERIFIED too.\r\n\r\n**RAP behavior pool classes** (classes with `RAP_STACK` tag or whose name\r\nmatches a known behavior pool pattern like `ZBP_*`) are RAP framework objects.\r\nClassify them as `CLEAN` with `CLOUD_READY` via Rule 17b, NOT as `UNKNOWN`\r\nvia Rule 8.\r\n\r\n### Session Resume\r\n\r\nIf some objects already have classifications in objects.json, skip those. Resume\r\nfrom the first unclassified object. Check each object for a `classification`\r\nfield — if present and non-null, treat it as already classified.\r\n\r\n### Entry Criteria\r\n\r\nBefore starting CLASSIFY, verify:\r\n- ANALYZE phase is complete (`currentPhase` is `\"ANALYZE\"` in project.json)\r\n- Usage data populated: `usage.uplRuns` exists on program-type objects\r\n- ATC findings populated: `atc` field exists on objects that were scanned\r\n- Clean Core data populated: `cleanCore` field exists on objects with\r\n dependency data\r\n\r\nIf any of these are missing, stop and print:\r\n```\r\nCLASSIFY cannot start — ANALYZE phase incomplete.\r\nMissing: {list of missing data categories}\r\nRun ANALYZE first.\r\n```\r\n\r\n### Classification Output Format\r\n\r\nFor each object, write these fields into objects.json:\r\n\r\n```json\r\n{\r\n \"classification\": {\r\n \"primary\": \"QUICK_FIX\",\r\n \"secondary\": [\"STALE_USAGE\", \"HAS_DATA(45000)\"],\r\n \"rule\": \"Rule 3\",\r\n \"evidence\": \"UPL=12000, 2 P2 ATC findings (quick-win fixable)\",\r\n \"classifiedAt\": \"2026-04-09T14:30:00Z\"\r\n }\r\n}\r\n```\r\n\r\nFields:\r\n- `primary` — one of: `CLEAN`, `QUICK_FIX`, `REFACTOR`, `REDESIGN`,\r\n `RETIRE_CANDIDATE`, `DORMANT`, `BATCH_ACTIVE`, `INTERFACE_VERIFY`,\r\n `UNKNOWN`\r\n- `secondary` — array of zero or more tags: `REGULATED`, `QUALITY_REVIEW`,\r\n `STALE_USAGE`, `STALE_TRANSITIVE`, `HAS_DATA({n})`, `RFC_EXTERNAL`\r\n- `rule` — which rule determined the primary classification\r\n- `evidence` — human-readable string explaining WHY this classification was\r\n chosen, including key data points (UPL count, ATC counts, caller names)\r\n- `classifiedAt` — ISO timestamp of when classification was applied\r\n\r\n### Rule Evaluation Order\r\n\r\nEvaluate rules in strict priority order. Once a primary classification is\r\nassigned, stop evaluating further rules for that object (secondary tags may\r\nstill be added by earlier rules).\r\n\r\nPriority order:\r\n1. Rule 0a — GXP/SOX compliance gate (can override RETIRE to DORMANT)\r\n2. Rule 0a2 — QUALITY compliance gate (can override RETIRE to DORMANT)\r\n3. Rule 0b — RFC-enabled FM with zero internal callers\r\n4. Rule 0c — Stale UPL (skips Rules 1-4 but NOT 5-7)\r\n5. Rules 1-7 — Active usage and enhancement rules (detailed in Part 2)\r\n6. Rule 8 — Transitive classification for classes\r\n7. Rule 9 — DDIC classification by consumers\r\n8. Rules 10-15 — Batch, dormant, retirement rules (detailed in Part 2)\r\n\r\n---\r\n\r\n### Rule 0a: GXP/SOX Compliance Gate — Highest Priority\r\n\r\nCheck the object's `compliance` field. This field is inherited from the\r\nobject's package (set during INVENTORY) or from an object-level override.\r\n\r\nIf compliance is `GXP` or `SOX`:\r\n- The object is NEVER classified as `RETIRE_CANDIDATE` regardless of what\r\n other rules determine.\r\n- If technical rules (Rules 10-15) would yield `RETIRE_CANDIDATE`, override\r\n primary to `DORMANT`.\r\n- Add secondary tag: `REGULATED`\r\n- Set evidence: `\"Package {pkg} is tagged {GXP/SOX}. Retirement requires formal change control. Cannot auto-classify as retirement candidate.\"`\r\n\r\nApply Rule 0a FIRST, before all other rules. After all other rules have run,\r\ncheck the result: if primary is `RETIRE_CANDIDATE` and the object has GXP/SOX\r\ncompliance, force primary to `DORMANT` and add the `REGULATED` tag.\r\n\r\nImplementation approach: run all rules normally, then apply Rule 0a as a\r\npost-filter. This is simpler than checking compliance inside every rule.\r\n\r\n```\r\npseudocode:\r\n classify(object)\r\n if object.compliance in ['GXP', 'SOX'] AND object.classification.primary == 'RETIRE_CANDIDATE':\r\n object.classification.primary = 'DORMANT'\r\n object.classification.secondary.append('REGULATED')\r\n object.classification.evidence += ' [Rule 0a override: regulated package]'\r\n```\r\n\r\n---\r\n\r\n### Rule 0a2: QUALITY Compliance Gate\r\n\r\nIf compliance is `QUALITY`:\r\n- Object gets extra scrutiny but is NOT as strictly protected as GXP/SOX.\r\n- If technical rules would yield `RETIRE_CANDIDATE`, override primary to\r\n `DORMANT`.\r\n- Add secondary tag: `QUALITY_REVIEW`\r\n- Normal classifications (`CLEAN`, `REFACTOR`, `QUICK_FIX`, `REDESIGN`,\r\n `BATCH_ACTIVE`, etc.) are NOT affected — only `RETIRE_CANDIDATE` is blocked.\r\n\r\nApply as a post-filter alongside Rule 0a:\r\n\r\n```\r\npseudocode:\r\n if object.compliance == 'QUALITY' AND object.classification.primary == 'RETIRE_CANDIDATE':\r\n object.classification.primary = 'DORMANT'\r\n object.classification.secondary.append('QUALITY_REVIEW')\r\n object.classification.evidence += ' [Rule 0a2 override: quality-controlled package]'\r\n```\r\n\r\n---\r\n\r\n### Rule 0b: RFC-Enabled FM with Zero Internal Callers — Second Highest Priority\r\n\r\nCheck: `isRFCEnabled == true` AND cross-reference data shows zero callers\r\nwithin the SAP system.\r\n\r\nIf true:\r\n- Primary: `INTERFACE_VERIFY`\r\n- Add secondary tag: `RFC_EXTERNAL`\r\n- NEVER auto-RETIRE — external systems (middleware, third-party apps, other\r\n SAP systems) may call this FM and those calls do not appear in UPL or\r\n cross-reference data.\r\n- Evidence: `\"RFC-enabled, zero SAP callers — may be called by external systems. Confirm with integration team.\"`\r\n\r\nIf the RFC-enabled FM HAS internal callers (cross-reference shows > 0 callers),\r\nskip this rule and proceed to normal classification via remaining rules. The\r\nRFC nature is not special when internal callers exist.\r\n\r\nEvaluate Rule 0b BEFORE Rules 1-7. If Rule 0b assigns `INTERFACE_VERIFY`,\r\nstop — do not evaluate further rules for this object.\r\n\r\n---\r\n\r\n### Rule 0c: Stale UPL Data\r\n\r\nCheck: `usage.uplRuns > 0` AND `usage.lastRunDate` is more than 12 months\r\nbefore today's date.\r\n\r\nIf true:\r\n- The UPL data is historical, not reflecting current runtime behavior.\r\n- Add secondary tag: `STALE_USAGE`\r\n- **Skip Rules 1-4** — these are active-usage rules that require recent\r\n runtime data. Applying them to stale UPL would produce misleading\r\n classifications.\r\n- **Do NOT skip Rules 5-7** — enhancement rules (BAdI active, CMOD exit\r\n active, enhancement spot implementations). Enhancement status is\r\n STRUCTURAL, registered in SXC_ATTR/MODATTR, and does not depend on runtime\r\n execution. An active BAdI with stale UPL is still an active BAdI. Skipping\r\n Rules 5-7 would misclassify enhancement objects as DORMANT or\r\n RETIRE_CANDIDATE.\r\n- After Rules 5-7, fall through to Rules 10-15 (batch/dormant/retire rules).\r\n\r\nIf `usage.uplRuns == 0` (no UPL data at all), Rule 0c does not apply — that\r\nsituation is handled by Rule 14 (no usage data → UNKNOWN).\r\n\r\n```\r\npseudocode:\r\n if object.usage.uplRuns > 0 AND monthsSince(object.usage.lastRunDate) > 12:\r\n object.classification.secondary.append('STALE_USAGE')\r\n skip_rules = [1, 2, 3, 4] # skip active-usage rules\r\n # Rules 5-7 still apply\r\n # Then fall through to Rules 10-15\r\n```\r\n\r\n---\r\n\r\n### Rule 8: Transitive Classification for Classes\r\n\r\nClasses do not have direct UPL data. Their CALLERS do. Determine class usage\r\ntransitively from caller classifications.\r\n\r\n**Prerequisite:** All non-class objects must be classified first. Process\r\nclasses AFTER all programs, function modules, and other directly-classifiable\r\nobjects have been classified. This ensures caller classifications are available\r\nfor transitive lookup.\r\n\r\n**Procedure:**\r\n\r\n1. For each class WITHOUT an existing classification AND that was not already\r\n classified by Rules 5-7 (enhancement classes):\r\n\r\n2. Retrieve the class's callers from cross-reference data collected in\r\n ANALYZE Tier 1.\r\n\r\n3. Check each caller's CLASSIFICATION (not raw UPL data):\r\n\r\n | Caller mix | Transitive result |\r\n |---|---|\r\n | ANY caller is `CLEAN`, `QUICK_FIX`, `REFACTOR`, `REDESIGN`, or `BATCH_ACTIVE` | Class is transitively USED |\r\n | ALL callers are `RETIRE_CANDIDATE` | Class is transitively RETIRE-able |\r\n | ALL callers are `DORMANT` (including those with `STALE_USAGE`) | Class inherits `DORMANT` with secondary tag `STALE_TRANSITIVE` |\r\n | No callers found in cross-reference | Class is `UNKNOWN` |\r\n\r\n4. After determining transitive usage status, apply the class's own ATC\r\n findings to select the specific primary classification:\r\n\r\n - Transitively USED + 0 ATC findings → `CLEAN`\r\n - Transitively USED + only P2 quick-win findings → `QUICK_FIX`\r\n - Transitively USED + P1 findings or many P2 → `REFACTOR`\r\n - Transitively USED + structural issues → `REDESIGN`\r\n - Transitively RETIRE-able → `RETIRE_CANDIDATE`\r\n - Transitively DORMANT → `DORMANT`\r\n - No callers → `UNKNOWN`\r\n\r\n**Why use caller CLASSIFICATION, not raw UPL:** Rule 0c marks programs with\r\nstale UPL as `DORMANT`. If Rule 8 checked raw UPL (> 0), it would see \"caller\r\nhas usage\" and classify the class as actively USED — even though the caller\r\nitself is `DORMANT` due to stale data. Using the caller's classification\r\nensures consistency: a class is never rated higher than its callers.\r\n\r\n**Evidence format:**\r\n\r\n```\r\n\"Transitive — called by {classification} program {name} (UPL={n}). Own ATC: {n} P1, {n} P2.\"\r\n```\r\n\r\nExample:\r\n```\r\nZCL_ORDER_PROCESSOR:\r\n Callers: ZSD_ORDER_ENTRY (CLEAN, UPL=89000), ZSD_BATCH_ORDER (BATCH_ACTIVE)\r\n → Transitive: USED (caller ZSD_ORDER_ENTRY is CLEAN)\r\n → Own ATC: 2 P2 findings (quick-win)\r\n → Primary: QUICK_FIX\r\n → Rule: Rule 8\r\n → Evidence: \"Transitive — called by CLEAN program ZSD_ORDER_ENTRY (UPL=89000). Own ATC: 0 P1, 2 P2.\"\r\n```\r\n\r\n**Circular references:** If class A calls class B and class B calls class A,\r\nand neither has any non-class callers, classify both as `UNKNOWN` with\r\nevidence noting the circular dependency. Do not recurse indefinitely.\r\n\r\n---\r\n\r\n### Rule 9: DDIC Classification by Consumers\r\n\r\nTables, data elements, and domains are classified by their consuming programs.\r\nConsumer lists were collected via D010TAB and DD03L during INVENTORY.\r\n\r\n**Simple rule: If ANY consumer is NOT `RETIRE_CANDIDATE`, the DDIC object is\r\nCLEAN.** Period. No exceptions.\r\n\r\n`UNKNOWN` is NOT `RETIRE_CANDIDATE`. `DORMANT` is NOT `RETIRE_CANDIDATE`.\r\nOnly the literal classification `RETIRE_CANDIDATE` counts. Everything else —\r\n`CLEAN`, `DORMANT`, `UNKNOWN`, `INTERFACE_VERIFY`, `BATCH_ACTIVE`, `REFACTOR`,\r\n`REDESIGN`, `QUICK_FIX`, `TEST` — means the DDIC object is needed and stays\r\n`CLEAN`.\r\n\r\nDDIC objects are infrastructure; removing them while any program still\r\nreferences them causes syntax errors.\r\n\r\n| Consumer mix | DDIC classification |\r\n|---|---|\r\n| ALL consumers are literally `RETIRE_CANDIDATE` | `RETIRE_CANDIDATE` |\r\n| ANY consumer has ANY other classification (including `UNKNOWN` or `DORMANT`) | `CLEAN` |\r\n| No consumers found in D010TAB/DD03L | `UNKNOWN` |\r\n\r\n**Common mistake:** Do NOT classify a table as `UNKNOWN` just because its\r\nconsumers are `UNKNOWN`. A consumer exists — that means the table is referenced\r\nby code. `UNKNOWN` consumers are still consumers. Only classify as `UNKNOWN`\r\nwhen the consumer list is EMPTY (zero rows from D010TAB/DD03L).\r\n\r\nEvidence format:\r\n```\r\n\"Used by {n} programs. {n} RETIRE_CANDIDATE, {n} active. Needed by active code.\"\r\n```\r\nor:\r\n```\r\n\"All {n} consumers are RETIRE_CANDIDATE. Safe to retire with consumers.\"\r\n```\r\n\r\n**Table data volume check — execute during CLASSIFY, not deferred:**\r\n\r\nFor every custom table (object type `TABL`) classified as `RETIRE_CANDIDATE`,\r\nimmediately run a row count query:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM {table_name}\r\n```\r\n\r\nUse `sap_sql_query` to execute this. If the query fails (table does not exist\r\nin DB, authorization error), note the failure in evidence and add secondary\r\ntag `DATA_CHECK_FAILED`.\r\n\r\nIf rows > 0:\r\n- Add secondary tag: `HAS_DATA({row_count})`\r\n- Update evidence: append `\"Table has {n} rows — archive decision needed before retirement.\"`\r\n\r\nIf rows == 0:\r\n- No additional tag needed. Evidence: append `\"Table is empty — no archive needed.\"`\r\n\r\nThis surfaces the data issue in the assessment report immediately, rather than\r\ndeferring it to a RETIRE phase that may never execute. Functional leads need\r\nto see data volume to make informed archive-or-delete decisions.\r\n\r\n**Performance note:** Only query tables classified as `RETIRE_CANDIDATE`. Do\r\nnot query tables classified as `CLEAN` or `UNKNOWN` — their row counts are\r\nirrelevant to the assessment.\r\n\r\n---\r\n\r\n### CLASSIFY Progress Tracking\r\n\r\nAfter processing each batch of objects (or every 50 objects, whichever is\r\nsmaller), print a progress line:\r\n\r\n```\r\nCLASSIFY: {n}/{total} objects classified. {clean} CLEAN, {qf} QUICK_FIX, {ref} REFACTOR, {red} REDESIGN, {ret} RETIRE, {dor} DORMANT, {bat} BATCH, {ifc} INTERFACE_VERIFY, {unk} UNKNOWN.\r\n```\r\n\r\nSave objects.json after each batch to enable session resume.\r\n\r\n---\r\n\r\n### Full Classification Rules Table\r\n\r\nApply these 20 rules in the execution order specified in the next section. Each\r\nrule has a condition, a primary classification, optional secondary tags, and\r\nnotes.\r\n\r\n| # | Rule | Condition | Primary | Secondary | Notes |\r\n|---|------|-----------|---------|-----------|-------|\r\n| 0a | GXP/SOX gate | compliance = GXP or SOX | (keep technical primary) | REGULATED | NEVER RETIRE_CANDIDATE. If other rules yield RETIRE_CANDIDATE, override to DORMANT. |\r\n| 0a2 | QUALITY gate | compliance = QUALITY | (keep technical primary) | QUALITY_REVIEW | DORMANT instead of RETIRE_CANDIDATE. Other primaries unchanged. |\r\n| 0b | RFC external | isRFCEnabled = true AND zero internal callers | INTERFACE_VERIFY | — | Never auto-RETIRE. |\r\n| 0c | Stale UPL | uplRuns > 0 AND lastRunDate > 12 months ago | (skip to Rules 5-7, then 10-15) | STALE_USAGE | Skip Rules 1-4 but NOT Rules 5-7. |\r\n| 1 | Active, clean | Runtime > 0 AND lastRun within 12 months, zero ATC findings | CLEAN | — | Best case. |\r\n| 2 | Active, quick-win | Runtime > 0 AND lastRun within 12 months, ATC findings are all quick-win (pragma suppression, minor fixes) | QUICK_FIX | — | Estimated 5 min per finding. |\r\n| 3 | Active, table replacement | Runtime > 0 AND lastRun within 12 months, ATC findings include table replacement (BSEG, KONV, VBUK etc.) | REFACTOR | HAS_ATC_FINDINGS | Estimated 2-4 hours per object. |\r\n| 4 | Active, functional redesign | Runtime > 0 AND lastRun within 12 months, ATC findings require functional redesign | REDESIGN | HAS_ATC_FINDINGS | Estimated 1-3 days per object. |\r\n| 5 | Active BAdI with ATC | isEnhancement = true AND has ATC findings | REFACTOR | WIRED_IN | Enhancement is structurally active. |\r\n| 6 | Active BAdI clean | isEnhancement = true AND no ATC findings | CLEAN | WIRED_IN | Enhancement is active and compatible. |\r\n| 7 | CMOD exit | isCMODExit = true | CLEAN | WIRED_IN, CMOD | CMOD exits are structural. |\r\n| 8 | Class transitive | Class with zero UPL, classified via callers | (transitive — see Rule 8 logic) | — | Use caller's classification status. |\r\n| 9 | DDIC transitive | Table/DTEL/DOMA classified via consumers | (transitive — see Rule 9 logic) | — | Use consumer's classification status. |\r\n| 10 | Batch active | Zero runtime, batch jobs in last 12 months | BATCH_ACTIVE | — | Job scheduler confirms usage. |\r\n| 11 | Stale batch (12-15mo) | Zero runtime, batch jobs older than 12 months but within 15 months | DORMANT | STALE_BATCH, CHECK_ANNUAL | May be quarterly/annual job. |\r\n| 11b | Stale batch (>15mo) | Zero runtime, batch jobs older than 15 months | DORMANT | STALE_BATCH | Likely genuinely stale. |\r\n| 12 | Has callers | Zero runtime, has static callers (from cross-ref), no batch | DORMANT | HAS_CALLERS | Code references exist but no execution. |\r\n| 13 | Recently changed | Zero runtime, zero callers, changed <= 12 months ago | DORMANT | RECENTLY_CHANGED | Developer recently touched it. If object has P1 ATC findings, add secondary tag HAS_ATC_P1 — surfaces in report as \"needs fix IF confirmed used.\" |\r\n| 14 | Old and unused | Zero runtime, zero callers, changed > 36 months ago | RETIRE_CANDIDATE | — | Candidate only — human confirms. |\r\n| 15 | Medium-age unused | Zero runtime, zero callers, changed 13-36 months ago | DORMANT | VERIFY | Gray zone — needs human input. |\r\n| 16 | In development | Object in active transport (from E071/E070 check) | (see note) | IN_DEVELOPMENT | Applied AFTER all other rules. If primary is RETIRE_CANDIDATE, override to DORMANT. All other primaries unchanged. |\r\n| 17 | Test object | SUBC = 'T' or name ends with *_TEST | TEST | SKIP_MIGRATION | Auto-detected in INVENTORY. |\r\n| 17b | RAP framework object | Object type is BDEF, DDLS, DDLX, DCLS, SRVD, SRVB, STOB, G4BA, OR class is a RAP behavior pool (ZBP_* with RAP_STACK tag) | CLEAN | CLOUD_READY | RAP objects ARE the ABAP Cloud target architecture. Cloud-ready by construction. Never classify as UNKNOWN. Includes behavior pool classes. |\r\n| 18 | No data | No usage, no callers, no ATC, no batch, not a RAP object — nothing | UNKNOWN | NEEDS_REVIEW | Truly unanalyzable. Rule 17b must be checked first. |\r\n\r\n---\r\n\r\n### Classification Execution Order\r\n\r\nThe order in which objects are classified matters because transitive rules (8,\r\n9) depend on their inputs being classified first. Follow this order exactly.\r\n\r\n**Step 1: Classify PROGRAMS, FUNCTION MODULES, and ENHANCEMENT CLASSES first.**\r\n\r\nApply Rules 0a-7 and 10-18 to:\r\n- All PROG objects.\r\n- All FUNC objects.\r\n- All CLAS objects where `isEnhancement = true` or `isCMODExit = true`.\r\n\r\nEnhancement classes (BAdI implementations, CMOD exit classes) are structurally\r\nwired into SAP standard processing. Evaluate them via Rules 5-7, NOT via\r\ntransitive Rule 8.\r\n\r\n**Step 2: Classify REMAINING CLASSES second (Rule 8 — transitive from callers).**\r\n\r\nOnly classes NOT already classified in Step 1. These are regular classes without\r\nenhancement flags.\r\n\r\nWithin Step 2, classify in dependency order (topological sort on the\r\nclass-calls-class graph):\r\n- Leaf classes (only called by programs/FMs from Step 1) first.\r\n- Classes that call other classes: classify AFTER their callees.\r\n- If circular dependencies exist (A calls B calls A), treat the cycle as a\r\n group:\r\n - If ANY class in the cycle has a non-class caller that is actively used,\r\n the entire cycle is USED.\r\n - If no external callers, the entire cycle is UNKNOWN.\r\n\r\n**Step 3: Classify DDIC objects last (Rule 9 — transitive from consumers).**\r\n\r\nWithin Step 3, classify in this order:\r\n1. TABLES first (their consumers are programs/classes from Steps 1-2).\r\n2. DATA ELEMENTS second (their consumers include tables, now classified).\r\n3. DOMAINS last (their consumers include data elements, now classified).\r\n\r\n**Step 4: Apply Rule 16 (IN_DEVELOPMENT) as a final pass across ALL objects.**\r\n\r\nCheck every object against the transport lock data. If an object is in an\r\nactive transport AND its primary classification is `RETIRE_CANDIDATE`, override\r\nto `DORMANT` with `IN_DEVELOPMENT` secondary tag.\r\n\r\n**Step 5: Apply Rule 17 (TEST) as a final pass.**\r\n\r\nObjects flagged as `isTestObject = true` during INVENTORY get `TEST` primary\r\nand `SKIP_MIGRATION` secondary, regardless of other rules.\r\n\r\n**Step 6: Apply compliance gates (Rules 0a, 0a2) as a final post-filter.**\r\n\r\nScan all classified objects. Any `RETIRE_CANDIDATE` in a GXP/SOX package:\r\noverride to `DORMANT` + `REGULATED`. Any `RETIRE_CANDIDATE` in a QUALITY\r\npackage: override to `DORMANT` + `QUALITY_REVIEW`.\r\n\r\n**Step 7: Tag DORMANT objects with P1 ATC findings.**\r\n\r\nScan all objects classified as `DORMANT` (any secondary tag). If the object has\r\nany ATC finding with priority = \"1\", add secondary tag `HAS_ATC_P1`. This\r\nensures the report surfaces: \"If confirmed used, these objects need code changes.\"\r\nThe management bucket \"Needs code changes\" should include a footnote:\r\n`\"Plus {n} DORMANT objects with P1 findings that need fixes if confirmed used.\"`\r\n\r\n**Step 8: Recognize RAP stacks as units.**\r\n\r\nAfter all objects are classified, detect RAP stacks: groups of objects sharing\r\nthe same root entity name across types (TABL, DDLS, BDEF, CLAS, SRVD, SRVB,\r\nDDLX, DCLS, STOB). If a group of 3+ objects shares a common root name (e.g.,\r\n`TCRSCOURSE` appears in `ZTCRS_COURSE`, `ZI_TCRSCOURSE`, `ZC_TCRSCOURSE`,\r\n`ZBP_TCRSCOURSE`, etc.), add secondary tag `RAP_STACK:{root_name}` to each.\r\nIn the report, list RAP stacks as units with one classification per stack\r\n(use the most significant classification in the group).\r\n\r\n---\r\n\r\n### Vocabulary Translation Table\r\n\r\nMap internal classification codes to business-friendly language for worksheets.\r\nWorksheets NEVER show raw internal codes.\r\n\r\n| Internal Code | Worksheet Shows |\r\n|---|---|\r\n| CLEAN | No changes needed |\r\n| QUICK_FIX | Minor technical changes needed |\r\n| REFACTOR | Code changes required for S/4HANA |\r\n| REDESIGN | Significant rework — business decision required |\r\n| RETIRE_CANDIDATE | Candidate for removal — please confirm |\r\n| BATCH_ACTIVE | Runs in background — keep |\r\n| INTERFACE_VERIFY | Verify with integration team — may be called externally |\r\n| DORMANT | Not recently used — please verify |\r\n| UNKNOWN | Needs your input |\r\n| TEST | Test object — skip |\r\n\r\nStore internal codes in objects.json. When generating any worksheet or report\r\noutput, translate using this table. Never expose raw codes like\r\n`INTERFACE_VERIFY` or `RETIRE_CANDIDATE` to business users.\r\n\r\n---\r\n\r\n### Per-Object Confidence\r\n\r\nAssign a confidence level to each object based on the quality of available data.\r\nStore in `classification.confidence` for each object in objects.json.\r\n\r\n| Data Available | Object Confidence |\r\n|---|---|\r\n| UPL data for this object | HIGH |\r\n| No UPL but transitive (class with classified callers) | MEDIUM |\r\n| Batch job data only | MEDIUM |\r\n| Cross-reference only (static callers, no runtime) | LOW |\r\n| RAP framework object (cloud-ready by construction) | MEDIUM |\r\n| ATC findings available (even without UPL) | LOW |\r\n| No data at all | NONE |\r\n\r\n**Project confidence display:**\r\n\r\nIf UPL is available: show percentage `\"{pct}% (HIGH+MEDIUM)\"`\r\n\r\nIf UPL is NOT available (discovery.dataSources.UPL.available = false):\r\nDo NOT show a percentage — it will always be near 0% and misleads the user.\r\nInstead show:\r\n\r\n```\r\nClassification basis: Static analysis only (no runtime usage data)\r\n - ATC findings: {n} objects checked\r\n - Clean Core: {n} objects checked\r\n - Cross-references: {n} objects checked\r\n - RAP objects: {n} (cloud-ready by construction)\r\n```\r\n\r\nThis tells the user WHAT the classification is based on, not a meaningless\r\npercentage.\r\n\r\n---\r\n\r\n### CLASSIFY Phase Completion\r\n\r\nWhen all objects are classified, print a TWO-PART summary showing both\r\ndimensions separately. This is the key insight: technical readiness and\r\nusage status are independent questions.\r\n\r\n```\r\nCLASSIFY complete.\r\n\r\nTECHNICAL READINESS (will this break on S/4HANA?):\r\n Cloud-ready: {n} ({pct}%) — RAP/ABAP Cloud, no changes needed\r\n Clean: {n} ({pct}%) — no compatibility issues\r\n Minor fix: {n} ({pct}%) — small changes (P2/P3 findings)\r\n Needs fix: {n} ({pct}%) — table replacements, unreleased APIs\r\n Needs redesign: {n} ({pct}%) — significant rework\r\n Not checked: {n} ({pct}%) — ATC didn't run\r\n\r\nUSAGE STATUS (should we keep or retire?):\r\n {If UPL available:}\r\n Active: {n} ({pct}%) — confirmed by UPL/batch\r\n Dormant: {n} ({pct}%) — evidence suggests not used\r\n Retire candidate:{n} ({pct}%) — confirmed unused >36 months\r\n Interface: {n} ({pct}%) — RFC, verify with integration team\r\n {If UPL NOT available:}\r\n Unverified: {n} (100%) — no runtime data on this system\r\n\r\n Usage cannot be determined without production runtime data.\r\n Technical readiness above is fully assessed regardless.\r\n\r\nCOMBINED RECOMMENDATION:\r\n No work needed: {n} ({pct}%) — technically clean (cloud-ready + clean)\r\n Needs code changes: {n} ({pct}%) — S/4 incompatibilities found\r\n Can be retired: {n} ({pct}%) — confirmed unused (requires UPL)\r\n Usage unverified: {n} ({pct}%) — technically clean, usage unknown\r\n\r\n**COMBINED bucket mapping:**\r\n- \"No work needed\" = technicalReadiness in (CLOUD_READY, CLEAN) AND usageStatus != RETIRE_CANDIDATE\r\n- \"Needs code changes\" = technicalReadiness in (MINOR_FIX, NEEDS_FIX, NEEDS_REDESIGN). Count these\r\n even if usageStatus is UNVERIFIED — they need fixing IF the system migrates to S/4.\r\n- \"Can be retired\" = usageStatus = RETIRE_CANDIDATE (only possible with UPL data)\r\n- \"Usage unverified\" = usageStatus = UNVERIFIED AND technicalReadiness in (CLEAN, CLOUD_READY)\r\n These are technically fine but we don't know if they're used.\r\n```\r\n\r\nPhase complete.\r\n\r\n### What Happens Next — Clear Direction\r\n\r\n### Report Generation (AUTOMATIC — not optional)\r\n\r\nThe report is the primary deliverable of CCA. It is NOT one of several options.\r\nAfter CLASSIFY completes, ALWAYS generate the report immediately. Move to the\r\nREPORT phase, produce the assessment report, save it, then present next steps.\r\n\r\nDo NOT ask \"do you want a report?\" — just generate it.\r\n\r\n### After Report — Next Steps\r\n\r\nAfter the report is generated and saved, present tailored next steps based on\r\nfindings. Lead with what the data tells you.\r\n\r\n```\r\nOutputs:\r\n - Open in the viewer -> <{id}-cca-assessment-{date}.cspeach.json> (the saved envelope)\r\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\r\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\r\n\r\n{Show the key findings summary from the report}\r\n\r\n---\r\nWhat's next?\r\n\r\n{If objects with NEEDS_FIX or NEEDS_REDESIGN exist:}\r\n >> {n} objects need code changes for S/4HANA:\r\n {list object names with specific issues — BSEG, KONV, etc.}\r\n\r\n{If UPL not available:}\r\n >> Usage is unknown for {n} objects — cannot determine keep vs retire\r\n without production runtime data.\r\n\r\n{If RAP stack detected:}\r\n >> RAP stack {name} ({n} objects) — clean, cloud-ready, no action needed.\r\n\r\nYour options:\r\n 1. Send worksheets to functional leads for usage verification\r\n 2. Start fixing the {n} objects with S/4 issues (/abap-upgrade-fix)\r\n 3. Done for now — resume later with /abap-cca\r\n\r\n (You can also ask me to export raw data as CSV at any time)\r\n```\r\n\r\nIf user picks 1 → move to REVIEW phase (worksheets).\r\nIf user picks 2 → generate fix-baseline.json, chain to /abap-upgrade-fix.\r\nIf user picks 3 → save state, print resume instructions, done.\r\n\r\n**Flow after worksheets come back:**\r\n```\r\nUser returns → /abap-cca → resume project → IMPORT phase\r\n → Import worksheet responses → Reclassify with usage decisions\r\n → Updated report (automatic) → Offer /abap-upgrade-fix for confirmed FIX objects\r\n```\r\n\r\n**Upload/Download summary — present to user if they ask:**\r\n\r\n| Direction | What | When | Format |\r\n|---|---|---|---|\r\n| Out | Assessment report | After CLASSIFY (automatic) | Markdown |\r\n| Out | Review worksheets | When user requests | CSV |\r\n| Out | Raw data export | Anytime on request | CSV/JSON |\r\n| In | PRD usage data | After DISCOVER if no UPL | CSV |\r\n| In | Worksheet responses | After leads fill worksheets | CSV |\r\n\r\n---\r\n\r\n## REVIEW Phase — Worksheets for Functional Leads\r\n\r\n### Purpose\r\n\r\nGenerate review worksheets as CSV files for functional leads to review and make decisions on classified objects. Worksheets use business-friendly vocabulary, not internal codes.\r\n\r\n### Session Resume\r\n\r\nIf `progress.review.worksheetsGenerated = true`, skip generation. Offer to regenerate or proceed to IMPORT.\r\n\r\n### Entry Criteria\r\n\r\nCLASSIFY phase complete. All objects have classifications in objects.json.\r\n\r\n---\r\n\r\n### Step 1: Module Assignment (Interactive)\r\n\r\nBefore generating worksheets, ask the user to assign packages to functional modules. This determines how worksheets are split.\r\n\r\nPresent each package found in objects.json and suggest a module based on the package name:\r\n\r\n```\r\nAssign packages to modules for worksheet generation:\r\n ZCUSTOM_FI → FI (your suggestion / type your own)\r\n ZCUSTOM_SD → SD\r\n ZPROJECT_ALPHA → ? (please assign)\r\n```\r\n\r\nWait for user response. The user assigns each package to a module name. Store the mapping in project.json under `review.moduleAssignment`:\r\n\r\n```json\r\n{\r\n \"review\": {\r\n \"moduleAssignment\": {\r\n \"ZCUSTOM_FI\": \"FI\",\r\n \"ZCUSTOM_SD\": \"SD\",\r\n \"ZPROJECT_ALPHA\": \"PP\"\r\n }\r\n }\r\n}\r\n```\r\n\r\nGenerate one worksheet per module. Objects from packages assigned to the same module appear in the same worksheet.\r\n\r\n---\r\n\r\n### Step 2: Transaction Code Enrichment\r\n\r\nBefore generating worksheets, query TSTC to map programs to transaction codes. Run per namespace prefix:\r\n\r\n```sql\r\nSELECT TCODE, PGMNA FROM TSTC WHERE PGMNA LIKE '{prefix}%'\r\n```\r\n\r\nThis adds a \"Transaction\" column so functional leads see `ZFD01 — Food Inventory Aging Report` instead of just `ZFI_AGING_REPORT (PROG)`.\r\n\r\nAlso query program titles for description enrichment:\r\n\r\n```sql\r\nSELECT NAME, DESCRIPT FROM TRDIR WHERE NAME IN (...)\r\n```\r\n\r\nUse the program names from the current module's objects. Batch in groups of 50 to stay under query limits. Cache results across modules to avoid duplicate queries.\r\n\r\n---\r\n\r\n### Step 3: Generate Worksheet CSV Files\r\n\r\nGenerate CSV files with comma delimiter. 13 columns per row. Quote all values.\r\n\r\n#### Header Row\r\n\r\n```csv\r\n\"Object Name\",\"Transaction\",\"Description\",\"Object Type\",\"Package\",\"Last Changed\",\"Technical Issues\",\"Recommended Action\",\"Regulatory Status\",\"RFC/External?\",\"Still Used? (Y/N)\",\"Your Decision (Keep/Retire/Fix/Redesign)\",\"Comments\"\r\n```\r\n\r\n#### Column Specifications\r\n\r\n| Column | Source | Notes |\r\n|---|---|---|\r\n| Object Name | object key from objects.json | For function modules show: `\"ZINT_MES_BATCH_SEND (FUGR: ZINT_MES)\"` |\r\n| Transaction | TSTC lookup | Blank if no t-code mapped |\r\n| Description | TRDIR program title or class description | Best effort; blank if unavailable |\r\n| Object Type | objectType | PROG, CLAS, FUNC, TABL, etc. |\r\n| Package | package | From objects.json |\r\n| Last Changed | lastChangedDate | Format: YYYY-MM-DD |\r\n| Technical Issues | ATC finding count + summary | E.g., `\"1 issue (table replacement)\"` or `\"0\"` |\r\n| Recommended Action | VOCABULARY TRANSLATED primary classification | See translation table below |\r\n| Regulatory Status | compliance tag | GXP, SOX, QUALITY, or NONE |\r\n| RFC/External? | isRFCEnabled | `\"YES - RFC enabled\"` with warning, or `\"No\"` |\r\n| Still Used? (Y/N) | BLANK — for lead to fill | Pre-populate only if evidence is overwhelming |\r\n| Your Decision | BLANK — for lead to fill | Options: Keep / Retire / Fix / Redesign |\r\n| Comments | Pre-filled warnings | See pre-fill rules below |\r\n\r\n#### Classification Vocabulary Translation\r\n\r\nTranslate internal classification codes to business-friendly language in the \"Recommended Action\" column:\r\n\r\n| Internal Classification | Worksheet Text |\r\n|---|---|\r\n| CLEAN | No changes needed |\r\n| QUICK_FIX | Minor technical changes needed |\r\n| REFACTOR | Code changes required for S/4HANA |\r\n| REDESIGN | Significant rework — business decision required |\r\n| RETIRE_CANDIDATE | Candidate for removal — please confirm |\r\n| BATCH_ACTIVE | Runs in background — keep |\r\n| INTERFACE_VERIFY | Verify with integration team — may be called externally |\r\n| DORMANT | Not recently used — please verify |\r\n| TEST | Test object — skip |\r\n| UNKNOWN | Needs your input |\r\n\r\n#### Pre-filled Comments Rules\r\n\r\nApply these rules in order. Concatenate multiple applicable comments with ` | ` separator:\r\n\r\n- For GXP objects: `\"GxP validated - formal change control required\"`\r\n- For SOX objects: `\"SOX controlled - sign-off required\"`\r\n- For RFC-enabled function modules: `\"WARNING: External systems may call this\"`\r\n- For RETIRE_CANDIDATE tables with data: `\"Table has {n} rows — archive decision needed\"`\r\n- For INTERFACE_VERIFY objects: `\"May have external consumers — confirm with integration team\"`\r\n- For STALE_USAGE objects: `\"Last executed {date} — usage may be historical\"`\r\n- Otherwise: blank\r\n\r\n#### Sample Rows\r\n\r\n```csv\r\n\"Object Name\",\"Transaction\",\"Description\",\"Object Type\",\"Package\",\"Last Changed\",\"Technical Issues\",\"Recommended Action\",\"Regulatory Status\",\"RFC/External?\",\"Still Used? (Y/N)\",\"Your Decision (Keep/Retire/Fix/Redesign)\",\"Comments\"\r\n\"ZFI_AGING_REPORT\",\"ZFIA1\",\"FI Aging Report\",\"PROG\",\"ZCUSTOM_FI\",\"2023-04-12\",\"1 issue (table replacement)\",\"Code changes required for S/4HANA\",\"NONE\",\"No\",\"\",\"\",\"\"\r\n\"ZINT_LIMS_BATCH_SEND (FUGR: ZINT_RFC)\",\"\",\"LIMS Batch Result Send\",\"FUNC\",\"ZINT_RFC\",\"2024-01-15\",\"0\",\"Verify with integration team — may be called externally\",\"NONE\",\"YES - RFC enabled\",\"\",\"\",\"WARNING: External systems may call this\"\r\n\"ZQM_BATCH_RELEASE\",\"ZQM01\",\"QM Batch Release\",\"PROG\",\"ZPHARMA_QM\",\"2022-08-10\",\"0\",\"No changes needed\",\"GXP\",\"No\",\"\",\"\",\"GxP validated - formal change control required\"\r\n```\r\n\r\n---\r\n\r\n### Step 4: Write Worksheet Files\r\n\r\nWrite CSV files to: `.cspeach/cca/projects/{id}/worksheets/`\r\n\r\nCreate the directory if it does not exist. Name files by module: `worksheet_{module}.csv` (e.g., `worksheet_FI.csv`, `worksheet_SD.csv`). Use lowercase module name in the filename.\r\n\r\nAfter writing all files, report the absolute file paths:\r\n\r\n```\r\nWorksheets generated:\r\n .cspeach/cca/projects/{id}/worksheets/worksheet_fi.csv ({n} objects)\r\n .cspeach/cca/projects/{id}/worksheets/worksheet_sd.csv ({n} objects)\r\n\r\nDistribute these to functional leads. They should fill in:\r\n - \"Still Used?\" column (Y or N)\r\n - \"Your Decision\" column (Keep, Retire, Fix, or Redesign)\r\n - \"Comments\" for any context\r\n\r\nWhen responses come back, use /abap-cca to import them.\r\n```\r\n\r\nUpdate project.json:\r\n- Set `progress.review.worksheetsGenerated = true`\r\n- List modules in `progress.review.responsesPending` (array of module names)\r\n- Record `progress.review.generatedAt` with current timestamp\r\n\r\n---\r\n\r\n### \"Generate Report NOW\" Escape\r\n\r\nAt any phase — DISCOVER, INVENTORY, ANALYZE, CLASSIFY, or REVIEW — the user can say \"give me the report now\" or \"generate report NOW.\" When this happens, generate a report with whatever data is available. Jump directly to REPORT phase logic.\r\n\r\nPrint a confidence disclaimer:\r\n\r\n```\r\nReport generated with current data.\r\nConfidence: {LOW/MEDIUM} (analysis incomplete — {n}/{total} objects analyzed)\r\nCaveat: {X} objects not yet classified. Run /abap-cca to resume full analysis.\r\n\r\nOutputs:\r\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\r\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\r\n```\r\n\r\nThis serves users under deadline pressure who need something to show management today, even if incomplete. Do not block or warn excessively — generate the report immediately.\r\n\r\n---\r\n\r\n### REVIEW Phase Completion\r\n\r\nWhen all worksheets are generated, print this summary:\r\n\r\n```\r\nREVIEW complete.\r\n Worksheets: {n} files generated for {n} modules\r\n Objects included: {n}\r\n Awaiting responses from: {module list}\r\n\r\nNext steps:\r\n 1. Distribute worksheets to functional leads\r\n 2. When responses come back, run /abap-cca to import them\r\n 3. Or say \"generate report now\" for a report without functional input\r\n\r\nPhase complete. Move to IMPORT (when responses are available).\r\n```\r\n\r\n---\r\n\r\n## Phase: IMPORT\r\n\r\n### Purpose\r\n\r\nImport functional lead responses from returned worksheet CSVs. Validate the data, detect conflicts, apply decisions, and update objects.json with human review input.\r\n\r\n### Session Resume\r\n\r\nIf `progress.review.responsesReceived` already contains modules, those are already imported. Only process new/pending modules.\r\n\r\n### Entry Criteria\r\n\r\nREVIEW worksheets have been generated and at least one response file is available. The user provides the file path.\r\n\r\n---\r\n\r\n### 11-Point Validation\r\n\r\nWhen the user provides a returned worksheet file, run these 11 validation steps in order.\r\n\r\n#### 1. BOM Strip\r\n\r\nIf the file starts with a UTF-8 BOM (bytes EF BB BF), strip it before parsing.\r\n\r\n#### 2. XLSX Detection\r\n\r\nIf the file is binary (starts with PK signature — ZIP/XLSX), error immediately:\r\n\r\n```\r\nThis is an Excel file (.xlsx). Please re-save as CSV:\r\n File → Save As → CSV (Comma delimited) (*.csv)\r\n```\r\n\r\nDo not attempt to parse XLSX. Stop and ask the user to re-export.\r\n\r\n#### 3. Delimiter Detection\r\n\r\nAuto-detect the delimiter by checking the header row:\r\n- Count commas, semicolons, and tabs in the first line\r\n- Use the most frequent as the delimiter\r\n- Default to comma if ambiguous\r\n\r\n#### 4. Header Matching\r\n\r\nMatch columns by header NAME, not position. The lead may have reordered columns. Required headers (case-insensitive, fuzzy match):\r\n\r\n| Header | Required? | Purpose |\r\n|---|---|---|\r\n| \"Object Name\" | Required — matching key | Links row to objects.json entry |\r\n| \"Still Used?\" | Optional | Usage confirmation from lead |\r\n| \"Your Decision\" or \"Decision\" | Optional | Keep/Retire/Fix/Redesign |\r\n| \"Comments\" | Optional | Free text from lead |\r\n\r\nIf \"Object Name\" column is missing, error immediately:\r\n\r\n```\r\nCannot find 'Object Name' column. Available columns: {list}\r\n```\r\n\r\nDo not guess. Stop and report.\r\n\r\n#### 5. Value Normalization\r\n\r\nNormalize common response values before processing:\r\n\r\n**Used column:**\r\n- Y / Yes / Ja / Oui / 1 / TRUE → `Y`\r\n- N / No / Nein / 0 / FALSE → `N`\r\n- blank → `null`\r\n\r\n**Decision column:**\r\n- Keep / keep / KEEP → `KEEP`\r\n- Retire / retire / RETIRE / Remove / Delete → `RETIRE`\r\n- Fix / fix / FIX / Change / Modify → `FIX`\r\n- Redesign / redesign / REDESIGN / Rewrite → `REDESIGN`\r\n- blank → `null`\r\n\r\n#### 6. Conflict Detection\r\n\r\nCheck each row for conflicts BEFORE applying decisions. Present conflicts to the user and wait for resolution.\r\n\r\n**BAdI conflict — lead retires a BAdI implementation:**\r\n\r\nIf `isEnhancement = true` in objects.json and the lead marked the object as \"Retire\":\r\n\r\n```\r\nCONFLICT: {object_name} is an active BAdI implementation.\r\nThe lead marked it for retirement, but it is structurally wired into SAP.\r\n\r\nOptions:\r\n a) Override lead's decision → keep as {current_classification}/WIRED_IN (recommended)\r\n b) Accept with warning → mark RETIRE but flag \"Active BAdI — deactivation required first\"\r\n c) Ask lead to clarify → generate follow-up for this specific object\r\n\r\nWhich option? [a/b/c]\r\n```\r\n\r\nWait for the user's choice. Apply the selected option.\r\n\r\n**GXP/SOX conflict — lead retires a regulated object:**\r\n\r\nIf the object is in a GXP or SOX package and the lead marked it as \"Retire\":\r\n\r\n```\r\nCONFLICT: {object_name} is in a {GXP/SOX} package.\r\nRetirement requires formal change control. Cannot auto-approve.\r\n\r\nThe object will be marked as RETIRE with REGULATED flag.\r\nA formal change control process must be followed.\r\n```\r\n\r\nApply automatically — no user choice needed. Set `humanReview.regulatedRetire = true`.\r\n\r\n**RFC conflict — lead says RFC FM is not used:**\r\n\r\nIf the object is an RFC-enabled function module (TFDIR.FMODE = 'R') and the lead says \"Not used\":\r\n\r\n```\r\nWARNING: {object_name} is RFC-enabled (TFDIR.FMODE = 'R').\r\nThe lead says it's not used, but external systems may call it.\r\nConfirm with the integration/middleware team before retiring.\r\n```\r\n\r\nSet `humanReview.rfcWarning = true`. Do not auto-retire.\r\n\r\n#### 7. Partial Decisions\r\n\r\nIf the lead filled \"Still Used?\" but left \"Decision\" blank:\r\n- Set `humanReview.used` to the normalized value\r\n- Set status: `USAGE_CONFIRMED_DECISION_PENDING`\r\n- The object keeps its technical classification — do not override\r\n\r\n#### 8. Default for Used-but-Undecided\r\n\r\nIf \"Still Used?\" = Y but \"Decision\" is blank:\r\n- Treat as CLEAN (conservative — the lead confirmed usage but did not decide)\r\n- Set `humanReview.decision = null` (not overridden, just confirmed used)\r\n- Do not change the primary classification\r\n\r\n#### 8b. Decision Mapping — How Lead Decisions Change Classification\r\n\r\nApply these rules when the lead provides an explicit decision:\r\n\r\n| Lead writes | Effect on primary classification |\r\n|---|---|\r\n| Keep | Primary stays unchanged. Set `humanReview.decision = \"KEEP\"`. |\r\n| Fix | Primary becomes REFACTOR if it was CLEAN/DORMANT/UNKNOWN. If already REFACTOR or QUICK_FIX, keep current. Set `humanReview.decision = \"FIX\"`. Chains to /abap-upgrade-fix. |\r\n| Retire | Primary becomes RETIRE_CANDIDATE if it was DORMANT/UNKNOWN. If it was CLEAN or REFACTOR (actively used), this is a conflict — warn the user before applying. Set `humanReview.decision = \"RETIRE\"`. |\r\n| Redesign | Primary becomes REDESIGN. Set `humanReview.decision = \"REDESIGN\"`. Escalated to steering committee. |\r\n\r\n#### 9. No-Response vs Blank-Response Distinction\r\n\r\nDistinguish between two different \"no answer\" states:\r\n\r\n- Row EXISTS in CSV but Used + Decision are both blank → `REVIEWED_NO_ANSWER` (lead saw it, skipped it)\r\n- Row is MISSING from CSV (lead deleted the row) → `NOT_REVIEWED` (lead may not have seen it)\r\n\r\nBoth stay at their technical classification — no override applied. Track counts of each for the project manager.\r\n\r\n#### 10. Unmatched Object Names\r\n\r\nIf a row in the CSV has an object name not found in objects.json, report as warning:\r\n\r\n```\r\nWARNING: Object '{name}' from worksheet not found in inventory. Skipped.\r\n```\r\n\r\nDo not create new entries. Do not error out — continue processing remaining rows.\r\n\r\n#### 11. Follow-up Worksheet\r\n\r\nAfter import, if any objects still have `DECISION_PENDING` status, generate a targeted follow-up CSV with only those objects:\r\n\r\n```\r\n{n} objects still need decisions. Generated follow-up worksheet:\r\n .cspeach/cca/projects/{id}/worksheets/followup_{module}_{date}.csv\r\n```\r\n\r\nThe follow-up worksheet uses the same format as the original worksheet but contains only the pending objects. Include the original technical classification and any partial responses already received.\r\n\r\n---\r\n\r\n### Import Summary\r\n\r\nAfter validation and import, print this summary:\r\n\r\n```\r\nIMPORT complete for module {module}.\r\n Rows processed: {n}\r\n Decisions applied: {n} (Keep: {n}, Fix: {n}, Retire: {n}, Redesign: {n})\r\n Conflicts resolved: {n}\r\n No response (reviewed, skipped): {n}\r\n Not reviewed (rows missing): {n}\r\n Unmatched names: {n}\r\n Follow-up needed: {n} objects\r\n\r\n{remaining_modules} modules still pending import.\r\n```\r\n\r\nUpdate `progress.review.responsesReceived` with the module name. Remove the module from `responsesPending`.\r\n\r\n---\r\n\r\n### Reclassification After Import\r\n\r\nAfter all responses are imported (or the user says \"done importing\"), reclassify objects that changed:\r\n\r\n1. Objects with decision = \"Fix\" → ensure primary = REFACTOR\r\n2. Objects with decision = \"Retire\" → ensure primary = RETIRE_CANDIDATE (with conflict checks from step 6)\r\n3. Objects with decision = \"Redesign\" → ensure primary = REDESIGN\r\n4. Rerun compliance gates (Rules 0a, 0a2) on any changed objects\r\n5. Write updated objects.json\r\n\r\n---\r\n\r\n### IMPORT Phase Completion\r\n\r\nWhen all modules are imported and reclassification is done, print:\r\n\r\n```\r\nIMPORT complete.\r\n All modules imported: {module list}\r\n Total decisions: {n}\r\n Reclassified objects: {n}\r\n Objects awaiting follow-up: {n}\r\n\r\nPhase complete. Move to REPORT.\r\n```\r\n\r\n---\r\n\r\n## REPORT Phase\r\n\r\n### Purpose\r\n\r\nGenerate the consulting-grade assessment report. The report is the primary deliverable of the CCA process.\r\n\r\n### Entry Criteria\r\n\r\nCLASSIFY phase must be complete (minimum). IMPORT phase improves the report but is optional.\r\n\r\n### Output Location\r\n\r\nSave reports to: `.cspeach/cca/projects/{id}/reports/report_{date}.md`\r\n\r\n### Report Generation\r\n\r\nGenerate a Markdown report with the following sections. Fill every placeholder with actual data from objects.json and project.json. Do not leave any placeholder unfilled — if data is missing, write \"N/A\" or \"not available\" with a reason.\r\n\r\n#### Header\r\n\r\n```markdown\r\n## Custom Code Analysis Report\r\n### System: {SID} / Client {client} / Release {release}\r\n### Scope: {packages}\r\n### Date: {date}\r\n### Session: {projectId}\r\n### Prepared by: {developer} / {company} — prepared with CSPeach\r\n\r\n### Data Sources Used\r\n{Table showing each discovery probe result and confidence level}\r\n```\r\n\r\n#### Executive Summary\r\n\r\nGenerate the management-facing 4-bucket summary:\r\n\r\n```markdown\r\n### Executive Summary (management view — 4 buckets)\r\nTotal: {N} custom objects\r\n\r\n| Category | Count | % | What it means |\r\n|---|---|---|---|\r\n| No work needed | {n} | {pct}% | Clean, batch-active, test — migrate as-is |\r\n| Needs code changes | {n} | {pct}% | Quick fixes + refactoring + redesign |\r\n| Can be retired | {n} | {pct}% | Not used — candidates for deletion (saves effort) |\r\n| Needs investigation | {n} | {pct}% | Unknown, dormant, interfaces — verify before deciding |\r\n\r\nEstimated remediation effort: **{d} person-days** for code changes.\r\nPotential savings from retirement: **{d} person-days** avoided.\r\n```\r\n\r\nMap classifications to buckets as follows:\r\n- \"No work needed\" = CLEAN + BATCH_ACTIVE + TEST\r\n- \"Needs code changes\" = QUICK_FIX + REFACTOR + REDESIGN\r\n- \"Can be retired\" = RETIRE_CANDIDATE\r\n- \"Needs investigation\" = UNKNOWN + DORMANT + INTERFACE_VERIFY\r\n\r\n#### Detailed Classification\r\n\r\n```markdown\r\n### Detailed Classification (technical view)\r\n\r\n| Classification | Count | % | Confidence | Risk | Est. Effort |\r\n|---|---|---|---|---|---|\r\n| CLEAN | {n} | {pct}% | HIGH | Low | test only |\r\n| QUICK_FIX | {n} | {pct}% | HIGH | Low | {h} hours |\r\n| REFACTOR | {n} | {pct}% | HIGH | Medium | {d} days |\r\n| REDESIGN | {n} | {pct}% | HIGH | High | {d} days |\r\n| RETIRE_CANDIDATE | {n} | {pct}% | varies | None | 0 (pending confirmation) |\r\n| BATCH_ACTIVE | {n} | {pct}% | MEDIUM | Low | verify schedules |\r\n| INTERFACE_VERIFY | {n} | {pct}% | LOW | High | confirm with integration team |\r\n| DORMANT | {n} | {pct}% | LOW | Low | verify with business |\r\n| TEST | {n} | {pct}% | N/A | None | skip |\r\n| UNKNOWN | {n} | {pct}% | NONE | Unknown | needs review |\r\n\r\nNote: REGULATED and QUALITY_REVIEW are secondary tags, not primary\r\nclassifications. Count them separately:\r\n \"Of the {n} CLEAN objects, {m} are in regulated packages (GXP/SOX).\"\r\n \"Of the {n} DORMANT objects, {m} are in QUALITY packages.\"\r\n```\r\n\r\n#### Coverage and Breakdown Sections\r\n\r\n```markdown\r\n### Coverage\r\nObjects with HIGH confidence: {n} ({pct}%)\r\nObjects with MEDIUM confidence: {n} ({pct}%)\r\nObjects with LOW/NONE confidence: {n} ({pct}%)\r\n\r\n### By Object Type\r\n| Type | Total | CLEAN | FIX | RETIRE | DORMANT | OTHER |\r\n\r\n### By Package\r\n| Package | Objects | Compliance | Clean% | Fix% | Retire% | Risk |\r\n\r\n### Interfaces (RFC/External)\r\n| Object | Type | RFC? | Internal Callers | Classification | Action |\r\nWARNING: RFC-enabled objects may be called by external systems invisible\r\nto this analysis. Confirm each with the integration/middleware team.\r\n\r\n### Regulated Objects (GXP/SOX)\r\n| Object | Package | Compliance | Classification | Note |\r\nThese objects require formal change control before any modification.\r\n```\r\n\r\n#### Effort and Cost Sections\r\n\r\n```markdown\r\n### Effort Estimate\r\n| Category | Objects | Per Object | Total |\r\n| Quick fix | {n} | 5 min | {h} hours |\r\n| Refactor | {n} | 2-4 hours | {d} days |\r\n| Redesign | {n} | 1-3 days | {d} days |\r\n| Total | | | {d} person-days |\r\n\r\n### Cost Summary (fill in rates)\r\n| Item | Days | Rate/day | Cost |\r\n| Quick fix | {d} | _______ | _______ |\r\n| Refactor | {d} | _______ | _______ |\r\n| Redesign | {d} | _______ | _______ |\r\n| Total | | | _______ |\r\n```\r\n\r\nLeave Rate/day and Cost columns blank with underscores — the consultant fills these in based on their engagement terms.\r\n\r\n#### Clean Core Readiness\r\n\r\n```markdown\r\n### Clean Core Readiness (if ARS_W_API_STATE was available)\r\n| Category | Count | % |\r\n|---|---|---|\r\n| Cloud-ready (all table APIs released) | {n} | {pct}% |\r\n| Needs API migration (uses unreleased tables) | {n} | {pct}% |\r\n| Not checked (ECC system / no ARS data) | {n} | {pct}% |\r\n\r\nTop unreleased APIs used by custom code:\r\n| SAP Table/View | Release State | Used By (count) | Successor |\r\n(List top 10 most-referenced unreleased APIs with their successors)\r\n\r\nNote: Clean Core check covers TABLE dependencies. FM/class API\r\ndependencies require cross-reference analysis (future enhancement).\r\n```\r\n\r\nIf ARS_W_API_STATE was not available during DISCOVER, write: \"Clean Core readiness data not available — ARS_W_API_STATE table not found on this system (likely ECC or pre-2022 S/4HANA).\"\r\n\r\n#### Retirement and Gaps\r\n\r\n```markdown\r\n### Retirement Candidates ({n} objects)\r\nThese objects are CANDIDATES based on analysis. Retirement requires:\r\n1. Business confirmation (via worksheets or direct approval)\r\n2. Dependency resolution\r\n3. Data archiving decisions for tables\r\n4. Formal change control for regulated objects\r\n5. Future execution via /abap-retire skill\r\n\r\n### Objects Not Analyzed\r\n{List objects skipped due to cross-ref budget limits, errors, or timeouts — with reason}\r\n```\r\n\r\n#### Next Steps\r\n\r\n```markdown\r\n### Next Steps\r\n1. Distribute worksheets to functional leads (if not done)\r\n2. Import responses and reclassify\r\n3. Run /abap-upgrade-fix on FIX objects\r\n4. Use RETIRE_CANDIDATE list for future /abap-retire process\r\n5. Escalate REDESIGN items to steering committee\r\n```\r\n\r\nTailor the next steps to the actual project state. If worksheets were already imported, omit steps 1-2. If no REDESIGN objects exist, omit step 5.\r\n\r\n---\r\n\r\n## Workflow Paths\r\n\r\n### Path A — Full analysis, chain to fix\r\n\r\n```\r\n/abap-cca → all phases → report\r\n→ chain to /abap-upgrade-fix for FIX objects\r\n→ hand RETIRE list to future /abap-retire\r\n```\r\n\r\nExecute all phases in order: DISCOVER → ANALYZE → CLASSIFY → REPORT. Skip IMPORT unless the user requests worksheets. After REPORT, offer to chain to `/abap-upgrade-fix`.\r\n\r\n### Path B — Worksheet review\r\n\r\n```\r\n/abap-cca → analysis → worksheets → leads respond → import → reclassify → report\r\n→ chain to /abap-upgrade-fix\r\n```\r\n\r\nExecute DISCOVER → ANALYZE → CLASSIFY → generate worksheets → pause for human review → IMPORT responses → reclassify → REPORT.\r\n\r\n### Path C — Get PRD usage data\r\n\r\n```\r\n/abap-cca → analysis (LIMITED on DEV) → offer PRD data options:\r\n 1. SE16 export from PRD (lightest — export UPL table via SE16)\r\n 2. SolMan export (if SolMan is connected)\r\n 3. Activate UPL in PRD (config change, collect 3-6 months)\r\n 4. Generated extractor program (via change management, NOT $TMP in PRD)\r\n→ import PRD data → reclassify with higher confidence → report\r\n```\r\n\r\nWhen running on a DEV system without UPL data, present these options to the user. Each option produces a file the user can place in `.cspeach/cca/projects/{id}/imports/` for the IMPORT phase.\r\n\r\n### Path D — Export and stop\r\n\r\n```\r\n/abap-cca → analysis → export full CSV to disk → done\r\n```\r\n\r\nRun DISCOVER → ANALYZE → CLASSIFY → export objects.json as CSV. No REPORT phase. Use this when the user only needs the raw data for external tools.\r\n\r\n### Path Combination\r\n\r\nPaths combine freely. State file persists across sessions. Resume any project by loading its project.json and objects.json.\r\n\r\n---\r\n\r\n## Handoff to /abap-upgrade-fix\r\n\r\nWhen the user chooses Path A or Path B (after import), generate a secondary baseline file for `/abap-upgrade-fix`.\r\n\r\nSave to: `.cspeach/cca/projects/{id}/fix-baseline.json`\r\n\r\n```json\r\n{\r\n \"source\": \"abap-cca\",\r\n \"projectId\": \"{id}\",\r\n \"created\": \"{timestamp}\",\r\n \"fixSession\": { \"fixed\": [] },\r\n \"objects\": [\r\n {\r\n \"name\": \"ZFI_AGING_REPORT\",\r\n \"type\": \"PROG\",\r\n \"package\": \"ZCUSTOM_FI\",\r\n \"classification\": \"REFACTOR\",\r\n \"atcFindings\": [...]\r\n }\r\n ]\r\n}\r\n```\r\n\r\nInclude only objects where classification is QUICK_FIX, REFACTOR, or REDESIGN (or where humanReview.decision = \"FIX\"). Exclude CLEAN, RETIRE_CANDIDATE, DORMANT, TEST, UNKNOWN, and INTERFACE_VERIFY objects.\r\n\r\n---\r\n\r\n## Guardrails and Limitations\r\n\r\n### Known Limitations\r\n\r\n| Limitation | Impact | Workaround |\r\n|---|---|---|\r\n| sap_syntax_check only checks written source | Cannot pre-check proposed code | Write then check then restore if fail |\r\n| UPL does not track classes | Classes show zero UPL | Transitive classification via callers |\r\n| UPL may not track inbound RFC | RFC FMs may show zero | TFDIR.FMODE probe + INTERFACE_VERIFY |\r\n| ATC takes 3-6 hours for large scopes | Session timeouts | Checkpoint per package, resume |\r\n| Cross-ref capped at 550 calls | Large scopes have unanalyzed objects | Tiered priority, worksheet fallback |\r\n| CMOD exits not in TADIR | Exit includes invisible | Explicit MODSAP/MODACT probe |\r\n| ADT maxRows unknown per system | Silent truncation risk | Pagination strategy |\r\n| DDIC classification is transitive | Depends on consumer analysis quality | If consumers are UNKNOWN, DDIC is UNKNOWN |\r\n| Clean Core covers TABLE deps only | FM/class API deps not checked | Future enhancement (WBCROSSGT) |\r\n\r\n### SQL Gotchas\r\n\r\n**Underscore in LIKE:** In ABAP SQL, `_` is a single-character wildcard. `LIKE 'ZFI_%'` matches `ZFIXANYTHING` too. Use exact match (`DEVCLASS = '{package}'`) instead of LIKE with underscores wherever possible.\r\n\r\n**Namespace slashes:** Table names like `/SDF/MON_RECS` contain forward slashes. ADT Data Preview handles this, but verify on the connected system.\r\n\r\n**No JOINs in V1:** Use two separate queries and merge in skill logic. JOINs may work on some ADT endpoints but are not guaranteed.\r\n\r\n### Research Required\r\n\r\n| Item | How to verify |\r\n|---|---|\r\n| UPL table variant + field names | Probe all 4 variants with SELECT * UP TO 1 ROWS |\r\n| ADT maxRows actual limit | Test with 5000, 10000 on connected system |\r\n| ATC variant existence | Probe in DISCOVER, not ANALYZE |\r\n| SEOCLASS field names | SELECT * FROM SEOCLASS UP TO 1 ROWS |\r\n| D010TAB row volume for Z* | Test COUNT(*) to gauge pagination needs |\r\n| sap_sql_query column headers | Does the tool return column names in the response? |\r\n\r\n### Privacy Note\r\n\r\nobjects.json contains developer user IDs (author, changedBy) from SAP repository metadata. Treat this file as internal working data. Do not share externally without review. Worksheets and reports deliberately exclude developer identity.\r\n\r\n### System Load\r\n\r\nATC runs consume work processes. On shared development systems, run during off-hours or configure delay between ATC runs. Never launch parallel ATC runs on the same system.\r\n\r\n### Out of Scope\r\n\r\nThese use cases are explicitly NOT covered by `/abap-cca`. Each deserves its own spec:\r\n\r\n- **Quarterly Clean Core Monitoring** (`/abap-cca-monitor`): run history, delta reporting, regression detection, trend projection\r\n- **CI/CD Transport Gate** (`/abap-transport-gate`): transport-scoped analysis, pass/fail on unreleased APIs\r\n- **Multi-System Consolidation** (`/abap-consolidate`): cross-project comparison, overlap detection, naming conflicts\r\n\r\nDo not attempt to implement these within `/abap-cca`. Redirect users to the appropriate future skill.\r\n\r\n---\r\n\r\n## REPORT Phase Completion\r\n\r\nWhen the report is generated and saved, print:\r\n\r\n```\r\nOutputs:\r\n - Open in the viewer -> <{id}-cca-assessment-{date}.cspeach.json> (the saved envelope)\r\n - Human report -> .cspeach/cca/projects/{id}/reports/report_{date}.md\r\n - Internal detail -> .cspeach/cca/projects/{id}/project.json (no need to open)\r\n\r\nSummary:\r\n Total objects: {n}\r\n No work needed: {n} ({pct}%)\r\n Needs code changes: {n} ({pct}%)\r\n Can be retired: {n} ({pct}%)\r\n Needs investigation: {n} ({pct}%)\r\n Estimated effort: {d} person-days\r\n Clean Core ready: {pct}%\r\n\r\nNext steps:\r\n - Chain to /abap-upgrade-fix for code remediation\r\n - Distribute worksheets (if not done)\r\n - Use RETIRE list for future /abap-retire process\r\n```\r\n\r\nThen write the `step-8-done.json` checkpoint (REPORT is phase 8 —\r\nthe terminal phase). Phase complete; CCA analysis finished.\r\n\r\n## Manifest Emission (CLI integration)\r\n\r\nAfter the REPORT-phase completion summary above, emit a hidden\r\nHTML-comment manifest block as the last thing in the response.\r\nThe CSPeach CLI parses this block (via\r\n`cspeach-cli/src/projects/extract-cca.ts`) into a\r\n`cca-assessment` envelope so the file picker, `--from` chain\r\nvisibility, and team-flow status renderer all see the run.\r\n\r\nThe block must appear EXACTLY once per response, after all human\r\noutput, in this format:\r\n\r\n```\r\n<!-- cspeach:cca-manifest\r\nartefact: cca-assessment\r\ntitle: <one-line title — e.g. \"ZFI / ZSD — S/4HANA 2023 estate\">\r\ndetail_path: .cspeach/cca/projects/<slug>/project.json\r\nproject_id: <slug>\r\nscope: <scope expression — e.g. \"ZFI* + ZSD*\" or \"All Z*\">\r\natc_variant: <variant id used in CLASSIFY phase>\r\ntotal_objects: <n>\r\nkeep: <n>\r\nfix: <n>\r\nretire: <n>\r\nredesign: <n>\r\nuncategorized: <n>\r\nclassifications:\r\n item-001 | <objectName> | <objectType> | <classification> | <confidence>\r\n item-002 | <objectName> | <objectType> | <classification> | <confidence>\r\n ...up to 50 rows; full detail in detail_path...\r\n-->\r\n```\r\n\r\nField rules:\r\n- `classification` is one of: `keep`, `fix`, `retire`, `redesign`\r\n (lowercase). Map the internal CCA verdict (CLEAN, BATCH_ACTIVE,\r\n TEST → `keep`; QUICK_FIX, REFACTOR → `fix`; RETIRE_CANDIDATE →\r\n `retire`; REDESIGN → `redesign`; DORMANT, UNKNOWN, INTERFACE_VERIFY\r\n → `uncategorized` in the summary, omit from classifications table).\r\n- `confidence` is one of: `high`, `medium`, `low`. Use the per-object\r\n confidence the CLASSIFY phase already assigns.\r\n- `total_objects` is the sum across `keep + fix + retire + redesign +\r\n uncategorized`. Self-check the math before emitting.\r\n- **Counts must equal rows.** The CLI extractor cross-checks every\r\n per-class summary count (`keep`/`fix`/`retire`/`redesign`) against\r\n the table rows and REJECTS the manifest on any contradiction — a\r\n `retire: 0` next to retire rows fails the save, loudly.\r\n- The classifications table is **capped at 50 rows** to keep the\r\n envelope small. If more classified objects exist than rows shown,\r\n you MUST mark the truncation — directly above `classifications:`:\r\n ```\r\n classifications_truncated: true\r\n classifications_shown: <rows actually in the table>\r\n classifications_total: <full classified count in detail_path>\r\n ```\r\n Then emit the 50 highest-priority rows (sort: `redesign` first,\r\n then `fix`, then `retire`, then `keep`, ordered by confidence DESC\r\n within each group). The full per-object data lives in the\r\n `detail_path` project.json. An unmarked cap is rejected by the\r\n extractor (counts exceed rows → save fails).\r\n- `detail_path` is **project-relative** (resolved from the git root\r\n or current workspace), never absolute. Same convention as the\r\n upgrade manifests. Emit `detail_path` only AFTER the project.json\r\n detail file is confirmed written to disk — a manifest pointing at a\r\n file that was never saved corrupts every downstream chain step.\r\n\r\n## Post-Save Chain Prompt\r\n\r\nAfter the CLI prints the save confirmation (`Saved: ...`), ask once:\r\n\r\n> \"Assessment ready. {fix_count} objects classified for FIX or REDESIGN.\r\n> Run /abap-upgrade-scan on the in-scope packages now to baseline ATC\r\n> findings for those objects?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-scan on the FIX/REDESIGN packages (recommended)\r\n> - Distribute worksheets to functional leads first (Path B)\r\n> - End turn\r\n\r\nIf the user chooses /abap-upgrade-scan, the cross-pipeline link is a\r\nhint, not a `--from` injection (CCA classifications and ATC findings\r\nare different inputs). Use the hint pattern:\r\n\r\n> `→ Run \\`/abap-upgrade-scan\\` against {comma-separated package list},\r\n> then \\`/abap-upgrade-fix --from\\` once the scan envelope is saved.`\r\n\r\nIf the user has multiple parallel CCA assessments saved (one per\r\nconsultant slice), suggest `/abap-cca-merge` instead:\r\n\r\n> `→ Multiple cca-assessment files in workspace. Run\r\n> \\`/abap-cca-merge --inputs @<a> @<b> ...\\` to consolidate before\r\n> chaining to /abap-upgrade-scan.`\r\n",
23
+ "sha256": "f85a0597f03cb3822931826a26c4da30d53b9dc564710056b0c89977b6558b51",
17
24
  "signature": "",
18
- "signedAt": "2026-09-17T20:19:03.971Z"
25
+ "signedAt": "2026-09-28T14:37:04.153Z"
19
26
  },
20
27
  "abap-cca-merge": {
21
28
  "name": "abap-cca-merge",
22
29
  "body": "---\r\nname: abap-cca-merge\r\ndescription: >\r\n Reconcile multiple parallel /abap-cca assessment files into one\r\n consolidated cca-assessment. For multi-consultant CCA reviews where\r\n each consultant ran a scoped slice (--scope ZFI*, --scope ZSD*, etc.)\r\n in parallel — merges N assessments back into a single estate-wide\r\n picture before the team picks remediation paths.\r\nphase: ANALYZE\r\nrequires_mcp: optional\r\nforge_rules: [1, 6]\r\nversion: \"2.0\"\r\nmin_cli_version: \"0.9.0\"\r\n---\r\n\r\n# abap-cca-merge\r\n\r\n## Purpose\r\n\r\nBig-team CCA engagements split the estate into per-consultant slices\r\nvia `/abap-cca --scope ZFI*` / `--scope ZSD*` / etc. Each consultant\r\nruns their slice independently, producing their own\r\n`cca-assessment` envelope (a saved `.cspeach.json` referencing a\r\nlocal `.cspeach/cca/projects/<slug>/project.json` detail file).\r\n\r\nBefore the team can decide remediation paths or chain into\r\n`/abap-upgrade-scan`, those N assessment files need to be merged\r\nback into ONE consolidated assessment. `/abap-cca-merge` does that\r\nreconciliation — same `projectId` (or scope-compatible projectIds)\r\nlet it union per-object classifications without losing review-state\r\ninformation.\r\n\r\nThis skill produces a NEW `cca-assessment` envelope that supersedes\r\neach input. Originals are preserved unchanged on disk so the team\r\naudit trail remains intact.\r\n\r\n## When to Use\r\n\r\n- Multi-consultant team where each ran `/abap-cca --scope` on a\r\n different package slice (FI, SD, MM, ...) and now needs one\r\n consolidated assessment for the steering committee.\r\n- Cross-shift handoffs (onshore-AM / offshore-night) where each shift\r\n produced a partial assessment slice.\r\n- Reconciling an in-progress consultant slice with a peer's\r\n worksheet-corrected slice — manual overrides (status `classified`)\r\n win over auto-classifications (status `open`).\r\n\r\nDo NOT use for:\r\n- Combining assessments from DIFFERENT projects. The skill rejects\r\n inputs whose `projectId` doesn't match unless `--allow-cross-project`\r\n is set explicitly (see Cross-Project Mode below).\r\n- Full system audit (\"show me all CCA work ever done\") — that's a\r\n reporting concern, not a merge.\r\n- Re-classifying objects from scratch — merge is a fact-collection\r\n step, not a reanalysis.\r\n\r\n## Inputs Expected\r\n\r\n```\r\n/abap-cca-merge --inputs @<assessment-A> @<assessment-B> @<assessment-C>\r\n```\r\n\r\nTwo or more `--inputs @<file>` references. Each must be a\r\n`cca-assessment` envelope. The skill loads each, validates that\r\n`projectId` matches across all inputs (or that scopes are\r\nnon-overlapping if `--allow-cross-project` set), and reconciles\r\nper-object.\r\n\r\nOptional flags:\r\n- `--allow-cross-project` — accept inputs with different `projectId`.\r\n Used when consultants ran fully independent CCAs against the same\r\n system but with different `--scope` slices that don't overlap.\r\n Skill validates that the scope expressions don't intersect (warns\r\n if they do). The merged result keeps the projectId of the FIRST\r\n input; the rest are treated as additive contributions.\r\n\r\n## Required Behavior\r\n\r\n**The merge itself is DETERMINISTIC CODE, not your reasoning.** Battery\r\ntesting proved that hand-reconciling envelopes corrupts counts (counts\r\nthat contradict rows, silently truncated tables). You orchestrate and\r\npresent; the `cca_merge` tool does ALL arithmetic.\r\n\r\n### 1. Call the `cca_merge` tool\r\n\r\nOne call, passing the user's input references through verbatim:\r\n\r\n```\r\ncca_merge({\r\n inputs: [\"@ACME_CCA_FI.cspeach.json\", \"@ACME_CCA_SD.cspeach.json\"],\r\n allow_cross_project: false // true only when the user passed --allow-cross-project\r\n})\r\n```\r\n\r\nThe tool loads + validates every envelope (artefact type, projectId\r\nidentity, atcVariant match), detects scope overlaps and duplicate\r\nobjects, reconciles classification conflicts with the\r\n**most-restrictive** rule (retire > redesign > fix > keep), recomputes\r\nevery count from the merged rows, and returns:\r\n\r\n- `merged_summary`, `per_input` — the consolidated + per-slice numbers\r\n- `warnings` — scope overlaps, classification conflicts (with the\r\n resolved value), duplicate objects, truncated inputs\r\n- `merged_classifications` — full merged rows (conflicted rows carry a\r\n `conflict` record naming each slice's classification)\r\n- `manifest_block` — the EXACT `csforge:cca-manifest` block to emit\r\n\r\nIf the tool returns an error (projectId mismatch, atcVariant mismatch,\r\nunreadable file), surface the error message to the user and stop — do\r\nNOT attempt to merge by hand as a fallback. If only one input is\r\nsupplied, the merge is a no-op — print a hint that no merge is needed\r\nand end the turn.\r\n\r\n### 2. Present the consolidated result\r\n\r\nRender the merged summary from the tool output — numbers copied, never\r\nrecomputed:\r\n\r\n```\r\n## /abap-cca-merge — Consolidated Assessment\r\n\r\n**Project:** {merged_project_id} — {merged_scope}\r\n**Inputs merged:** {N} assessment files\r\n**Total objects:** {merged_summary.totalObjects}\r\n**Keep:** {merged_summary.keep}\r\n**Fix:** {merged_summary.fix}\r\n**Retire:** {merged_summary.retire}\r\n**Redesign:** {merged_summary.redesign}\r\n**Uncategorized:** {merged_summary.uncategorized}\r\n\r\n### Per-input breakdown\r\n| Input file | Slice | Total | Keep | Fix | Retire | Redesign |\r\n|---|---|---|---|---|---|---|\r\n{one row per per_input entry}\r\n\r\n### Warnings\r\n{each tool warning, one bullet each — keep the tool's resolved values}\r\n```\r\n\r\nYOUR value-add is the judgment commentary around the warnings: which\r\nconflicts need a consultant conversation before remediation planning,\r\nwhether scope overlaps suggest a slicing mistake, what a truncated\r\ninput means for coverage. Recommend concrete follow-ups (e.g. \"convene\r\nthe FI and MM consultants on ZFI_BP_SYNC — retire won by rule, verify\r\nit before planning\").\r\n\r\n### 3. Emit the manifest VERBATIM\r\n\r\nPaste `manifest_block` from the tool result — unchanged, byte for byte\r\n— as the LAST block of the response. Do not re-sort rows, do not\r\nadjust counts, do not drop the truncation fields. The CLI save hook\r\nparses this block, and the extractor re-validates counts against rows:\r\nan edited block fails the save loudly.\r\n\r\n### 4. Post-Save Chain Prompt\r\n\r\nAfter the merge envelope saves, ask once:\r\n\r\n> \"Merged {N} slices: {fix_count} fix, {retire_count} retire,\r\n> {redesign_count} redesign across {total} objects. Run\r\n> /abap-upgrade-scan on the FIX/REDESIGN packages to baseline ATC\r\n> findings?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-scan on the FIX/REDESIGN packages (recommended)\r\n> - Distribute review worksheets to the steering committee first\r\n> - End turn\r\n\r\nUse the hint pattern (the merged file is the just-saved one):\r\n\r\n> `→ Run \\`/abap-upgrade-scan\\` against the merged in-scope packages,\r\n> then \\`/abap-upgrade-fix --from\\` once the scan envelope is saved.`\r\n\r\n## Cross-Project Mode (`--allow-cross-project`)\r\n\r\nDefault mode rejects inputs with mismatched `projectId`. When\r\nconsultants ran fully independent CCAs against the same system but\r\nwith disjoint `--scope` slices that never share a `projectId`, set\r\n`--allow-cross-project` to override (pass `allow_cross_project: true`\r\nto the `cca_merge` tool).\r\n\r\nIn cross-project mode the tool:\r\n- Keeps the FIRST input's `projectId` as base, plus appends\r\n `_merged_<date>` to differentiate from the original.\r\n- Validates that scope expressions are disjoint (e.g. `ZFI*` and `ZF*`\r\n overlap; warns but does not block).\r\n- Rejects `atcVariant` mismatches — merging classifications produced\r\n under different ATC variants is meaningless. (This is enforced in\r\n every mode, not just cross-project.)\r\n\r\n## Output Structure\r\n\r\n(See step 2 above — the rendered summary IS the output.)\r\n\r\n## Guardrails\r\n\r\n- READ-ONLY against SAP. The skill only reads the input assessment\r\n files, the underlying `project.json` detail files, and the merged\r\n baseline; it does NOT touch the system, write transports, or modify\r\n code. Forge Rule 6 (read-only by default).\r\n- Reject `projectId` mismatches FIRST (or scope overlaps in\r\n cross-project mode). A merge across incompatible projects is\r\n meaningless and would silently produce a corrupt consolidated view.\r\n- Reject `atcVariant` mismatches in cross-project mode. Different\r\n variants produce different findings; merging hides the difference.\r\n- Preserve originals — input assessment files are never modified.\r\n The merged output goes to a NEW `_merged_<date>` directory.\r\n- Review statuses reset: every item in the merged envelope starts at\r\n status `open` — pre-merge viewer review decisions (reviewed /\r\n classified) on the input slices are NOT carried over (Axis-A\r\n status reconciliation is a backlog item), so re-review the merged\r\n file in the viewer.\r\n- No re-classification. The merge is a fact-collection step; do not\r\n re-run rules or invent new classifications. CLASSIFY is /abap-cca's\r\n job, not /abap-cca-merge's.\r\n- Do not chain into /abap-upgrade-fix from this skill. The merge is\r\n followed by /abap-upgrade-scan (which baselines ATC for the\r\n remediation candidates), then /abap-upgrade-fix.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-cca-merge --inputs @ACME_CCA_FI.cspeach.json @ACME_CCA_SD.cspeach.json @ACME_CCA_MM.cspeach.json\r\n```\r\n\r\n## Example Output (abridged)\r\n\r\n```\r\n## /abap-cca-merge — Consolidated Assessment\r\n\r\n**Project:** acme-s4-2026 — ZFI*, ZSD*, ZMM*\r\n**Inputs merged:** 3 assessment files\r\n**Total objects:** 1330\r\n**Keep:** 840 (63%)\r\n**Fix:** 338 (25%)\r\n**Retire:** 86 (6%)\r\n**Redesign:** 66 (5%)\r\n**Uncategorized:** 0\r\n\r\n### Per-input breakdown\r\n| Input file | Slice | Total | Keep | Fix | Retire | Redesign |\r\n|---------------------------------|-------|-------|------|-----|--------|----------|\r\n| ACME_CCA_FI.cspeach.json | ZFI* | 432 | 280 | 96 | 32 | 24 |\r\n| ACME_CCA_SD.cspeach.json | ZSD* | 510 | 320 | 140 | 28 | 22 |\r\n| ACME_CCA_MM.cspeach.json | ZMM* | 388 | 240 | 102 | 26 | 20 |\r\n\r\n### Warnings\r\n⚠ CLASSIFICATION CONFLICT: ZFI_BP_SYNC (PROG) — ACME_CCA_FI: fix [high]; ACME_CCA_MM: retire [medium]\r\n → resolved: retire (most-restrictive rule). Review with the consultants\r\n who classified before remediation planning.\r\n\r\n⚠ DUPLICATE OBJECT: ZSHARED_HELPER (CLAS) appears in 2 inputs\r\n (ACME_CCA_FI, ACME_CCA_SD) with the same classification 'fix' —\r\n overlapping slices, merged into one row.\r\n\r\n<!-- csforge:cca-manifest\r\nartefact: cca-assessment\r\ntitle: acme-s4-2026 — Merged (3 slices)\r\ndetail_path: .cspeach/cca/projects/acme-s4-2026_merged_2026-05-09/project.json\r\nproject_id: acme-s4-2026_merged_2026-05-09\r\nscope: ZFI*, ZSD*, ZMM*\r\natc_variant: S4HANA_READINESS_2023\r\ntotal_objects: 1330\r\nkeep: 840\r\nfix: 338\r\nretire: 86\r\nredesign: 66\r\nuncategorized: 0\r\nclassifications_truncated: true\r\nclassifications_shown: 50\r\nclassifications_total: 1330\r\nclassifications:\r\n item-001 | ZFI_LEGACY_DUNNING | PROG | redesign | high\r\n item-002 | ZSD_OLD_CALC | PROG | redesign | high\r\n item-018 | ZSHARED_HELPER | CLAS | fix | high\r\n item-042 | ZFI_BP_SYNC | PROG | retire | medium | conflict=ACME_CCA_FI:fix;ACME_CCA_MM:retire\r\n ...\r\n-->\r\n```\r\n\r\n(The whole `<!-- csforge:cca-manifest ... -->` block above comes from\r\nthe tool's `manifest_block` — pasted verbatim, never authored by you.\r\nConflicted rows carry a 6th `conflict=<slice>:<cls>;<slice>:<cls>`\r\ncell; truncation fields appear when the merge exceeds 50 rows.)\r\n\r\n## Forge Rules Compliance\r\n\r\n- Rule 1 (no blind generation): merge is mechanical reconciliation,\r\n no LLM creativity. Conflict resolution rules are deterministic\r\n (priority-based, most-restrictive wins) and run in CODE — the\r\n `cca_merge` tool — never in model arithmetic.\r\n- Rule 6 (read-only by default): no SAP writes, no transport mutation.\r\n- All inputs are auditable on disk; merge produces a NEW file.\r\n",
23
30
  "sha256": "2ebaca8ad020af8f8dd99b1688a57be78c73cc58a2578a094fb5e73969eb3895",
24
31
  "signature": "",
25
- "signedAt": "2026-09-17T20:19:04.172Z"
32
+ "signedAt": "2026-09-28T14:37:04.221Z"
26
33
  },
27
34
  "abap-clean-core": {
28
35
  "name": "abap-clean-core",
29
- "body": "---\nname: abap-clean-core\ndescription: >\n Clean-core decision & defense — recommend the clean-core-correct approach for a\n requirement or object, classify the clean-core level (A/B/C/D), and produce\n citable evidence to defend it; verifies against the live system when connected,\n reasons from SAP's model + citations when not. Read-only.\nphase: DESIGN\nrequires_mcp: optional\nrequired_tools:\n - sap_api_state\n - sap_atc_run\n - sap_get_source\n - sap_object_structure\n - sap_usage_references\n - mcp__sap-docs__sap_get_object_details\n - mcp__sap-docs__search\nmin_cli_version: \"0.8.0\"\nforge_rules: [1, 3, 4, 6]\nversion: \"1.0\"\n---\n\n# abap-clean-core\n\n## Purpose\n\nDecide and defend the clean-core-correct approach for a **single requirement,\nproposed approach, or SAP object/API**. Returns the recommended path, its\nclean-core level (A/B/C/D), and the citable evidence needed to hold that\nverdict in a stakeholder review or code review.\n\nThis skill does not scan the estate (use `/abap-cca` for that), does not scan a\nbody of custom code for violations (use `/abap-cloud-check` for that), and does\nnot write or activate SAP objects (it hands off to builders when connected).\n\n**Chain:** `/abap-cca` flags an item or a new requirement arrives\n→ **`/abap-clean-core`** decides and defends\n→ `/abap-rap` | `/abap-extend-model` | `/abap-generate` builds the auto-buildable parts.\n\n## When to Use\n\n### Three input modes (detected automatically)\n\n1. **Advise a requirement** — \"expose equipment user status to an external\n system.\" The skill runs the full decision tree and returns the recommended\n approach + level + defense.\n\n2. **Judge a proposed approach** — \"can we extend `API_EQUIPMENT` to add this\n operation?\" The skill delivers a verdict on that approach (feasible/clean?\n what level? why not?) and the better path if it fails.\n\n3. **Adjudicate an object or API** — \"is `STATUS_CHANGE_EXTERN` clean core?\n what level?\" The skill returns release state + level + evidence + SAP\n citation for that specific object.\n\n### What this skill is NOT\n\n- Not an estate inventory scan → `/abap-cca`\n- Not a codebase-violation scanner → `/abap-cloud-check`\n- Not a builder — it hands off to `/abap-rap`, `/abap-extend-model`, or\n `/abap-generate`; it does NOT route to `/abap-enhance` as a builder (see\n Guardrails)\n\n## Inputs Expected\n\n**Required (will be asked if missing — Forge Rule 1):**\n- The requirement, approach, or object/API name to decide on\n- Target SAP release (e.g. S/4HANA 2023, S/4HANA Cloud)\n\n**Useful:**\n- Whether the consumer is external (OData/API) or internal (ABAP call chain)\n- ATC output already available (paste it — the skill consumes it as evidence)\n- Package name / language version of the custom object in scope\n\n**Auto-provided when connected:** system release, platform, deployment model\nfrom `<session_context>`. Do not re-ask for facts already in context.\n\n---\n\n## Required Behavior\n\n### Step 1 — Intake\n\nDetect the input mode (advise / judge / adjudicate). Restate the requirement in\none sentence so the user can confirm or correct it before evidence is gathered.\n\nAsk the missing questions before judging (Forge Rule 1). The minimum needed:\n- What is the requirement or the specific object?\n- Target SAP release (if not in `<session_context>`)?\n- Is a system connection available or should this run offline?\n\nDo not begin the decision tree until the requirement is clearly stated.\n\n### Step 2 — Gather Evidence\n\n**Hard rule: if an MCP connection is present, MUST use the live system.**\nKnowledge-only is strictly the offline fallback. Never prefer embedded\nknowledge over live evidence when the system is reachable.\n\n#### When connected (tag all findings VERIFIED)\n\nRun these tools in order; collect all results before adjudicating:\n\n| Tool | What it yields |\n|------|----------------|\n| `sap_api_state` on the SAP object(s) in scope | C0/C1/C2 contract (or none); visibility; release state from the ABAP repository |\n| `sap_atc_run` with the clean-core / Usage-of-APIs variant | ATC priority (P1/P2/P3/no-finding) → directly maps to Level D/C/B/A |\n| `sap_get_source` or `sap_object_structure` on the relevant SAP object | Real behavior definition, exposed actions/fields, whether the needed capability is actually in the released surface |\n| `sap_usage_references` | Downstream consumers — useful for impact of a wrapper approach |\n| `mcp__sap-docs__sap_get_object_details` (Cloudification Repository) | Whether the object is classified as a public Classic API (Level B) or internal (Level C) |\n\nIf a tool call returns a stateful-session error (e.g. `ICMENOSESSION`): retry\nonce. If the retry also fails, switch to PER-SAP-MODEL mode for that object and\nadd an explicit note: \"Live verification failed for {object} — result is\nPER-SAP-MODEL.\" Do not silently substitute knowledge for evidence.\n\n#### Always — docs lookups (regardless of mode)\n\nUse `mcp__sap-docs__search` or `mcp__sap-docs__sap_get_object_details` to\nretrieve SAP's current level definitions, release contract documentation, and\nCloudification Repository classification for the objects in scope. These feed\nthe citations in the Defense section. See `reference/clean-core-method.md §SAP\nSources Summary` for the exact URLs and what each source covers.\n\n#### When not connected (tag all findings PER SAP MODEL)\n\nReason from the method in `reference/clean-core-method.md` — the stable level\ndefinitions, decision tree, and contract semantics. Note explicitly that this\nis \"PER SAP MODEL (unverified)\" and that connected verification may change the\nverdict. Never present a knowledge-based guess as confirmed fact.\n\nIf the `sap-docs` tools are also unavailable in this session (no MCP at all),\ndo not stall waiting on a docs call: proceed from the embedded framework in\n`reference/clean-core-method.md`, and flag in the output that the citations are\nfrom model knowledge and should be confirmed against the SAP source URLs listed\nin `reference/clean-core-method.md §SAP Sources Summary`.\n\nIf an object/API is not found in the system: state that plainly. Do not guess\na classification.\n\n### Step 3 — Adjudicate\n\nFor each SAP object or API in scope:\n\n1. **State the release status.** Always make the two-axis distinction explicit\n where relevant (see `reference/clean-core-method.md §2`):\n - **Axis 1 — C0/C1/C2 contract** (clean-core axis): governs whether the\n object is callable from a restricted ABAP language version (ABAP for Cloud\n Development). C1 = callable; none = not callable.\n - **Axis 2 — Classic SE37/Function-Builder flag**: \"Released\" (public\n Classic API ≈ Level B), \"Internally released\" (SAP-internal ≈ Level C),\n blank (not released). This flag is a stability marker from the classic era;\n it has **no bearing** on C0/C1 contracts.\n Confusing these two axes is the single most common error in clean-core\n discussions. State both whenever they are relevant.\n\n2. **Assign the clean-core level** per `reference/clean-core-method.md §1`:\n - **A** — released APIs only; ABAP Cloud Development language version; ATC: no finding\n - **B** — public Classic API (SE37 \"Released\"); ATC: Priority 3\n - **C** — internal or non-classified SAP object; ATC: Priority 2\n - **D** — modifies SAP standard or accesses non-recommended objects; ATC: Priority 1\n\n3. **Carry evidence or citation for every claim** — no bare assertions (Forge\n Rule 3). VERIFIED findings cite the tool result. PER-SAP-MODEL findings cite\n the SAP source (URL from `reference/clean-core-method.md §SAP Sources Summary`).\n\n4. **Ambiguous classification?** Say so plainly. Give the definitive check\n (from `reference/clean-core-method.md §2 — The definitive check`):\n > Place the call in an *ABAP for Cloud Development* class and compile. If\n > the compiler rejects it, the object is not clean-core released. This check\n > can be reproduced via `sap_syntax_check` on a scratch class in an ABAP\n > Cloud package.\n\n### Step 4 — Decide\n\nRun the extension-strategy decision tree from `reference/clean-core-method.md\n§3`:\n\n1. Is the required capability available via a **released API** (C1 contract)?\n - Yes, AND all dependencies are released → **Level A** (ABAP Cloud path)\n - Yes, but insufficient alone (unreleased gap remains) → treat gap as step 2\n\n2. Is the required SAP object a **public Classic API** (SE37 \"Released\", listed\n in Cloudification Repository)?\n - Yes → **Level B** (classic extensibility; do NOT call from an ABAP Cloud\n package — restricted language version cannot access it)\n\n3. Is the object **internal** (SE37 \"Internally released\" or blank, not listed\n as a Classic API)?\n - Yes → **Level C** — build a separate custom service (wrapper) that isolates\n the internal call behind a stable Z-interface. Do NOT expose the unreleased\n object directly. Do NOT extend the delivered SAP service to add this\n capability (that requires a modification = D).\n\n4. Would meeting the requirement require **modifying SAP standard**?\n - Yes → **Level D** — reject this path. State plainly: \"The only way to\n force this feature into the delivered service is a standard modification,\n which is Level D.\" Present the Level C wrapper as the alternative.\n\n**Always document options considered and why each was rejected.** This is what\nmakes the verdict defensible in a stakeholder challenge. Typical rejection\nreasons to state:\n- \"Restricted language version cannot call this FM — won't compile in ABAP\n Cloud\" (rules out Level A for an unreleased object)\n- \"Extending the standard API to host new operations requires a modification\"\n (rules out extending a delivered API = D)\n- \"Key-user extensibility scope covers UI fields, not behavioural operations\"\n (rules out key-user path)\n\n### Step 5 — Build Path\n\nSplit the recommendation honestly using `reference/clean-core-method.md §4`:\n\n**CSPeach can build this (→ skill):**\n- New RAP service / wrapper BO / OData endpoint → `/abap-rap`\n- New field or action on an existing custom model → `/abap-extend-model`\n- Net-new custom class, program, or structure → `/abap-generate`\n\n**Do this by hand (steps below):**\n- BAdI implementations, enhancement spots, implicit enhancements — ADT has no\n write API for enhancement framework objects; give step-by-step manual\n instructions (SE18/SE19 path, interface to implement, activation steps)\n- Classic/internal FM internals inside a wrapper — the FM shell and function\n group require SE37 actions not covered by `sap_set_source` alone; give manual\n steps\n\n**Critical: do not route to `/abap-enhance` as a builder.** `/abap-enhance`\nadvises on which extension point to use and generates the implementation code,\nbut it cannot write classic enhancements into the system via ADT. Classic\nenhancement steps must be manual instructions, not a skill handoff.\n\nLabel every part clearly: \"CSPeach can build this → /abap-X\" vs \"Do this by\nhand — steps:\".\n\n### Step 6 — Defense\n\nProduce three things:\n\n1. **Business summary** — a plain paragraph (no jargon) the developer can paste\n to a stakeholder, project manager, or skeptic to explain why this approach is\n the right one and is not a clean-core violation.\n\n2. **Citations** — the SAP sources that back every level/release claim in this\n output. Minimum: the level-definition source, the release-contract source,\n and the Cloudification Repository or ATC finding for each adjudicated object.\n\n3. **Rebuttal set** (optional, include when the approach is likely to be\n challenged) — structured as: \"If they say X — you say Y, here is the proof.\"\n Common challenge to anticipate:\n - \"But SE37 shows this FM as Released\" → that is the classic stability flag,\n not a C1 contract; it means Level B (Classic API) when consumed by customer\n code, not Level A; it does not make the call compile in an ABAP Cloud class.\n\n---\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly\nthis shape, and nothing before it except the skill's main heading:\n\n```markdown\n## abap-clean-core — [Requirement / Object Name]\n\n## TL;DR\n\n**Headline:** <1-sentence verdict — recommended approach and level>\n\n**Top 3 (priority order — most important first):**\n1. <key adjudication finding, decision, or constraint>\n2. <second most important — often the options-rejected summary>\n3. <build path split or defense highlight>\n\n**Verdict:** <VERIFIED | PER SAP MODEL> — <1-line conclusion and next step>\n\n---\n\n<then the full structured report below>\n```\n\n**Rules for the Top 3:**\n- Each bullet must be actionable and specific — not a generic category.\n- Include the mode label (VERIFIED / PER SAP MODEL) where relevant.\n- If fewer than 3 meaningful items exist, list only what is real.\n\n**Why this matters:** the TL;DR is what the developer sees in the chat stream\nand can paste to a stakeholder. The full structured report is saved for deeper\nreview. The TL;DR must be self-contained.\n\n---\n\n## Output Structure\n\n```\n## abap-clean-core — {requirement_or_object}\n\n## TL;DR\n\n**Headline:** {1-sentence verdict}\n\n**Top 3:**\n1. {key adjudication or constraint}\n2. {options-rejected summary}\n3. {build path or defense highlight}\n\n**Verdict:** {VERIFIED | PER SAP MODEL} — {conclusion and next step}\n\n---\n\n## Adjudication\n\n**Assessment mode:** VERIFIED | PER SAP MODEL (live verification failed / no system connected)\n\n| Object / API | Release Status (Axis 1: C0/C1/C2/none) | Classic Flag (Axis 2: SE37) | Clean-Core Level | Evidence / Citation |\n|---|---|---|---|---|\n| {object_name} | {contract} | {flag or N/A} | A / B / C / D | {ATC finding / sap_api_state result / SAP URL} |\n\n---\n\n## Decision\n\n**Recommended approach:** {description of the approach}\n**Clean-core level:** {A | B | C | D}\n**Summary:** {1-2 sentences explaining why this is the correct path}\n\n---\n\n## Options Considered\n\n| Option | Verdict | Reason rejected |\n|--------|---------|-----------------|\n| {option_1} | Rejected | {reason — cite evidence where possible} |\n| {option_2} | Rejected | {reason} |\n| {chosen_option} | Selected | {why this is the right path} |\n\n---\n\n## Build Path\n\n### CSPeach can build this\n\n| Step | What | Skill |\n|------|------|-------|\n| {step_n} | {what CSPeach builds} | /abap-rap | /abap-extend-model | /abap-generate |\n\n### Do this by hand\n\n| Step | Where | What |\n|------|-------|------|\n| {step_n} | {transaction / ADT location} | {what to do — concrete steps} |\n\n---\n\n## Defense\n\n### Business summary\n\n{Plain paragraph, no ABAP jargon, suitable for a stakeholder or project manager.}\n\n### Citations\n\n| Claim | Source | URL / Reference |\n|-------|--------|-----------------|\n| {level definition} | SAP Cloud ALM — Clean Core Level Overview | {URL from reference/clean-core-method.md} |\n| {release contract} | ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS | {URL} |\n| {object classification} | Cloudification Repository Viewer / ATC finding | {URL or tool result} |\n\n### Rebuttals (if applicable)\n\n| Challenge | Response | Proof |\n|-----------|----------|-------|\n| {what the skeptic says} | {your counter} | {cite the evidence} |\n\n---\n\n## Open Questions\n\n{Anything that requires system access not available in this session, or a\nbusiness decision outside the scope of this skill.}\n```\n\n---\n\n## Artifact — `clean-core-decision`\n\nEmit this envelope at the end of every output as a `.cspeach.json` file so the\nverdict is saved and viewable in the CSPeach viewer.\n\n```json\n{\n \"schemaVersion\": \"1.0\",\n \"id\": \"{uuid}\",\n \"createdAt\": \"{ISO-8601}\",\n \"source\": \"abap-clean-core\",\n \"artefactType\": \"clean-core-decision\",\n \"requirement\": \"{the requirement or object name as stated}\",\n \"inputMode\": \"advise | judge | adjudicate\",\n \"assessmentMode\": \"verified | per_sap_model\",\n \"adjudications\": [\n {\n \"object\": \"{object_name}\",\n \"releaseStatus\": \"released-C1 | released-C2 | classic-se37-released | internally-released | not-released\",\n \"contract\": \"C0 | C1 | C2 | none\",\n \"cleanCoreLevel\": \"A | B | C | D\",\n \"evidence\": \"{ATC finding, tool result, or citation}\",\n \"citation\": \"{SAP URL or note number}\"\n }\n ],\n \"decision\": {\n \"recommendedApproach\": \"{description}\",\n \"cleanCoreLevel\": \"A | B | C | D\",\n \"summary\": \"{1-2 sentence explanation}\"\n },\n \"optionsConsidered\": [\n {\n \"option\": \"{option description}\",\n \"verdict\": \"rejected | selected\",\n \"reason\": \"{reason}\"\n }\n ],\n \"buildPath\": {\n \"autoBuild\": [\n { \"step\": \"{step_n}\", \"description\": \"{what}\", \"handoffSkill\": \"/abap-rap | /abap-extend-model | /abap-generate\" }\n ],\n \"byHand\": [\n { \"step\": \"{step_n}\", \"where\": \"{transaction/ADT}\", \"what\": \"{instructions}\" }\n ]\n },\n \"defense\": {\n \"businessSummary\": \"{plain-language paragraph}\",\n \"citations\": [\n { \"claim\": \"{claim}\", \"source\": \"{source name}\", \"url\": \"{URL}\" }\n ],\n \"rebuttals\": [\n { \"challenge\": \"{skeptic's claim}\", \"response\": \"{counter}\", \"proof\": \"{evidence}\" }\n ]\n }\n}\n```\n\n---\n\n## Guardrails\n\nThe following rules are hard behavioral constraints, not suggestions. They must\nbe active on every run.\n\n**Connected → must verify (Rule 1 of 8)**\n- DO: call `sap_api_state`, `sap_atc_run`, `sap_get_source`/`sap_object_structure`, and\n `mcp__sap-docs__sap_get_object_details` when an MCP connection is present.\n- DON'T: rely on embedded knowledge when a live system is reachable.\n\n**Label every verdict (Rule 2 of 8)**\n- DO: tag every finding VERIFIED (live evidence) or PER SAP MODEL (knowledge, unverified).\n- DON'T: present a knowledge-based assessment as a confirmed fact.\n\n**Evidence and citation on every claim (Rule 3 of 8 / Forge Rule 3)**\n- DO: cite the tool result or SAP URL for every level/release claim.\n- DON'T: make a bare assertion (\"this is Level C\") without naming the evidence.\n\n**State the two-axis distinction (Rule 4 of 8)**\n- DO: explicitly distinguish the classic SE37 release flag (stability marker)\n from the C0/C1 clean-core release contract wherever relevant.\n- DON'T: treat \"SE37 Released\" as equivalent to C1 or as proof of clean-core\n safety.\n\n**Ambiguity → say so + give the compiler check (Rule 5 of 8)**\n- DO: when classification is unclear, state the ambiguity and give the definitive\n check (place the call in an ABAP for Cloud Development class and compile).\n- DON'T: resolve ambiguity silently by picking the more convenient answer.\n\n**No overpromise in the build path (Rule 6 of 8)**\n- DO: route auto-build handoffs only to `/abap-rap`, `/abap-extend-model`, or\n `/abap-generate` — the three skills that can write objects via ADT.\n- DON'T: route to `/abap-enhance` as a builder — it cannot write classic\n enhancements through ADT. Give manual steps instead.\n- DON'T: claim a path is auto-buildable if the target builder skill cannot\n execute it through the ADT API.\n\n**Encode method, not memorized facts (Rule 7 of 8)**\n- DO: derive volatile per-object facts (release state, current level definitions,\n cloudification classification) from live evidence and docs lookups at runtime.\n- DON'T: answer \"object X = Level C\" from training data without checking the\n live system — SAP's model and per-object classifications evolve (e.g. the Aug\n 2025 shift from the 3-tier model to A–D levels).\n\n**Session failure → retry once, then degrade explicitly (Rule 8 of 8)**\n- DO: retry a failing MCP call once; if it fails again, degrade to PER-SAP-MODEL\n with an explicit note identifying which object's live verification failed.\n- DON'T: silently fall back to knowledge without telling the user that live\n verification was unavailable.\n\n**Never endorse a Level D path**\n- DO: state plainly when the only way to achieve a requirement is a standard\n modification, and that this path is Level D and should be rejected.\n- DON'T: present Level D (modify SAP standard) as a valid option.\n\n---\n\n## Example Prompt\n\n```\n/abap-clean-core\n\nWe need to expose equipment user status (both read and change) to an external\nsystem via OData. System is S/4HANA 2023. We already looked at API_EQUIPMENT\nbut are not sure if it covers user status. Is there a clean-core path, and what\nlevel is it?\n```\n\n---\n\n## Example Output Outline\n\nThe following output is the accuracy bar for **verified mode** on this\nrequirement. The skill must reach these verdicts with this evidence and labeling.\n\n```\n## abap-clean-core — Equipment User Status Exposure (S/4HANA 2023)\n\n## TL;DR\n\n**Headline:** No released SAP API covers equipment user status — a custom RAP\nwrapper service consuming the classic internal FM behind a Z-interface is the\ncorrect path (Level C, conditional clean core).\n\n**Top 3:**\n1. VERIFIED: STATUS_CHANGE_EXTERN has no C1 contract (SE37 \"Internally released\"\n ≠ clean-core released) — calling it directly from ABAP Cloud won't compile.\n2. All three obvious Level A/B options (extend API_EQUIPMENT, I_EquipmentTP\n actions, I_EquipmentStatus) are ruled out: no user-status surface in the\n released behavior definition; standard modification would be required.\n3. Build path splits: RAP wrapper shell is auto-buildable (→ /abap-rap); the\n classic FM internals inside the wrapper must be written by hand.\n\n**Verdict:** VERIFIED — Level C (conditional clean core). Build a separate\ncustom OData/RAP wrapper service; proceed to /abap-rap for the RAP shell.\n\n---\n\n## Adjudication\n\n**Assessment mode:** VERIFIED (live sap_api_state + sap_object_structure on S/4HANA 2023)\n\n| Object / API | Release Status (Axis 1: C0/C1) | Classic Flag (Axis 2: SE37) | Clean-Core Level | Evidence / Citation |\n|---|---|---|---|---|\n| I_EquipmentTP (RAP BO) | C1 released | N/A (CDS/BDEF) | A (for its exposed surface) | sap_object_structure: exposes install/dismantle + CRUD actions; no user-status action present — VERIFIED |\n| I_EquipmentStatus (CDS) | C1 released | N/A | A (for its exposed fields) | sap_api_state: C1; exposes system-status flags only (EQUNR, STSMA, ESTAT); no user-status (OBJNR + individual status) — VERIFIED |\n| STATUS_CHANGE_EXTERN (FM) | none (no C1/C2) | \"Internally released\" (SE37) | C (when consumed by customer code) | sap_api_state: contract = none; SE37 Attributes: Internally Released — two-axis distinction applies: classic flag ≠ C1 — VERIFIED |\n\n**Two-axis note on STATUS_CHANGE_EXTERN:** SE37 shows \"Internally released\" —\nthis is the classic SE37 stability flag, not a C0/C1 contract. It means SAP\nreleased this FM for internal SAP use only. When consumed by customer code it\nis a Level C reference. It has no C1 contract and will not compile in an ABAP\nfor Cloud Development class.\n\n---\n\n## Decision\n\n**Recommended approach:** Build a separate custom RAP/OData wrapper service\n(`ZI_EquipmentUserStatus` CDS + RAP BO + OData V4 endpoint) whose behavior pool\ncalls `STATUS_CHANGE_EXTERN` in classic ABAP, behind a stable Z-interface. The\nexternal system consumes the Z-service, never the internal FM directly.\n\n**Clean-core level:** C (conditional clean core)\n\n**Summary:** SAP ships no released API for equipment user status. The wrapper\npattern isolates the internal FM behind a customer-owned stable interface — the\nsame design SAP uses internally (API_EQUIPMENT itself wraps non-released classic\nFMs). This is Level C, not a violation. No SAP standard is modified.\n\n---\n\n## Options Considered\n\n| Option | Verdict | Reason rejected |\n|--------|---------|-----------------|\n| Extend API_EQUIPMENT behavior definition to add a user-status action | Rejected (= D) | API_EQUIPMENT is a delivered RAP BO. Adding a new operation to its behavior definition requires a modification to the SAP-delivered BDEF — Level D. VERIFIED: sap_object_structure confirms no extension hook for new actions on the delivered BO. |\n| Call STATUS_CHANGE_EXTERN from an ABAP Cloud Development class (key-user / developer extensibility) | Rejected (won't compile) | STATUS_CHANGE_EXTERN has no C1 contract (VERIFIED: sap_api_state). Restricted language version can only call objects with C1 contract + correct visibility. The compiler will reject this call. Definitive check: place the CALL FUNCTION in a class with language version \"ABAP for Cloud Development\" and attempt activation — it will fail. |\n| Use I_EquipmentTP actions (Level A path) | Rejected (gap in released surface) | I_EquipmentTP is released (C1) but its behavior definition exposes only install/dismantle operations and standard CRUD. No user-status action exists on this BO. VERIFIED: sap_object_structure + Cloudification Repository. |\n| Use I_EquipmentStatus CDS view | Rejected (wrong status type) | I_EquipmentStatus exposes system-status flags (ESTAT), not user-status (individual status objects on OBJNR). VERIFIED: sap_get_source on I_EquipmentStatus. |\n| Custom RAP wrapper service (Z) calling STATUS_CHANGE_EXTERN in classic ABAP | Selected (Level C) | Wraps the internal FM behind a stable Z-interface. No SAP standard modified. Level C because the wrapper references an internal SAP object — conditional clean core with SAP's changelog applied. |\n\n---\n\n## Build Path\n\n### CSPeach can build this\n\n| Step | What | Skill |\n|------|------|-------|\n| 1 | New RAP BO (ZI_EquipmentUserStatus CDS, ZC_EquipmentUserStatus projection, ZBDEF_EquipmentUserStatus, behavior pool ZBP_EquipmentUserStatus), Service Definition + OData V4 Binding | /abap-rap |\n\n### Do this by hand\n\n| Step | Where | What |\n|------|-------|------|\n| 2 | SE37 / ADT — Function Group | Create function group ZFG_EQUIP_STATUS and function module ZFM_EQUIP_STATUS_CHANGE (the empty shell can be scaffolded via /abap-generate; only the body in step 3 is by-hand) |\n| 3 | SE37 — FM Source (or sap_set_source on the Z-FM) | Write the FM body: call STATUS_CHANGE_EXTERN with the correct parameters, handle SY-SUBRC, translate to a clean exception for the RAP behavior pool caller |\n| 4 | ADT — Behavior Pool (ZBP_EquipmentUserStatus, CCIMP include) | In the RAP behavior pool's action handler, call ZFM_EQUIP_STATUS_CHANGE (your Z-FM) — not STATUS_CHANGE_EXTERN directly. The RAP pool is in a classic ABAP package, so calling the Z-FM from classic ABAP is valid at Level C |\n\n---\n\n## Defense\n\n### Business summary\n\nSAP's own Equipment API (`API_EQUIPMENT`) wraps non-released classic function\nmodules internally — the wrapper pattern is SAP's own design for bridging\nreleased interfaces to classic implementations. Where SAP has not yet released\na user-status operation in its standard API surface, we apply the same pattern:\na new Z-OData service provides the clean, versioned interface that external\nsystems consume, while the implementation calls SAP's internal FM behind that\nstable interface. No SAP standard code is modified. ATC Priority 2 (Level C —\nconditional clean core) is the expected finding for this pattern: it means the\napproach is acceptable when applied with SAP's changelog, which is exactly what\nwrapping an internally-released FM achieves.\n\n### Citations\n\n| Claim | Source | URL |\n|-------|--------|-----|\n| Level C definition (Priority 2 ATC) | SAP Cloud ALM — Clean Core Level Overview | https://help.sap.com/docs/CloudALM/877c96cf971648b09ee0d0a64f7f4fef/eaa61d6ae6744ccdb0d18820fb8998c9.html |\n| C1 contract required for ABAP Cloud access | ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS | https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/ABENABAP_RELEASE_CONTRACTS.html |\n| Classic API vs C1 distinction | ABAP Keyword Docs — ABENABAP_API_RELEASE | https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/ABENABAP_API_RELEASE.html |\n| STATUS_CHANGE_EXTERN classification | Cloudification Repository Viewer + sap_api_state result | https://sap.github.io/abap-atc-cr-cv-s4hc/?version=objectClassifications.json |\n\n### Rebuttals\n\n| Challenge | Response | Proof |\n|-----------|----------|-------|\n| \"But SE37 shows STATUS_CHANGE_EXTERN as Released (internally released)\" | \"Internally released\" is the SE37 classic flag, not a C1 release contract. It means SAP released this FM for internal SAP use only. When customer code references it, ATC classifies the custom object at Level C (Priority 2). It has no C1 contract — placing a CALL FUNCTION statement for it in an ABAP for Cloud Development class will fail to compile. | sap_api_state on STATUS_CHANGE_EXTERN: contract = none. ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS confirms C1 is required for cloud-accessible consumption. |\n| \"Why can't we just extend API_EQUIPMENT's behavior definition?\" | Adding a new operation to a SAP-delivered behavior definition (BDEF) requires a modification to the delivered source — Level D. There is no extension hook for new actions on the standard BO surface. The clean path is a separate Z-service. | sap_object_structure on I_EquipmentTP confirms no user-status action; modifying a delivered BDEF = SMODILOG entry = Level D. |\n\n---\n\n## Open Questions\n\n- Confirm the exact parameter signature of STATUS_CHANGE_EXTERN in your system\n (verify with sap_get_source in a live session) before writing the Z-FM body.\n- If the external consumer requires OData V2 instead of V4, the RAP binding type\n changes — clarify the consumer protocol before running /abap-rap.\n```\n",
30
- "sha256": "ddb3fc4d79bcae1ff09b72bcbf102e9c99e6817e5dc6f46bfc5ac3a875e415d3",
36
+ "body": "---\r\nname: abap-clean-core\r\ndescription: >\r\n Clean-core decision & defense — recommend the clean-core-correct approach for a\r\n requirement or object, classify the clean-core level (A/B/C/D), and produce\r\n citable evidence to defend it; verifies against the live system when connected,\r\n reasons from SAP's model + citations when not. Read-only.\r\nphase: DESIGN\r\nrequires_mcp: optional\r\nrequired_tools:\r\n - sap_api_state\r\n - sap_atc_run\r\n - sap_get_source\r\n - sap_object_structure\r\n - sap_usage_references\r\n - mcp__sap-docs__sap_get_object_details\r\n - mcp__sap-docs__search\r\nmin_cli_version: \"0.8.0\"\r\nforge_rules: [1, 3, 4, 6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-clean-core\r\n\r\n## Purpose\r\n\r\nDecide and defend the clean-core-correct approach for a **single requirement,\r\nproposed approach, or SAP object/API**. Returns the recommended path, its\r\nclean-core level (A/B/C/D), and the citable evidence needed to hold that\r\nverdict in a stakeholder review or code review.\r\n\r\nThis skill does not scan the estate (use `/abap-cca` for that), does not scan a\r\nbody of custom code for violations (use `/abap-cloud-check` for that), and does\r\nnot write or activate SAP objects (it hands off to builders when connected).\r\n\r\n**Chain:** `/abap-cca` flags an item or a new requirement arrives\r\n→ **`/abap-clean-core`** decides and defends\r\n→ `/abap-rap` | `/abap-extend-model` | `/abap-generate` builds the auto-buildable parts.\r\n\r\n## When to Use\r\n\r\n### Three input modes (detected automatically)\r\n\r\n1. **Advise a requirement** — \"expose equipment user status to an external\r\n system.\" The skill runs the full decision tree and returns the recommended\r\n approach + level + defense.\r\n\r\n2. **Judge a proposed approach** — \"can we extend `API_EQUIPMENT` to add this\r\n operation?\" The skill delivers a verdict on that approach (feasible/clean?\r\n what level? why not?) and the better path if it fails.\r\n\r\n3. **Adjudicate an object or API** — \"is `STATUS_CHANGE_EXTERN` clean core?\r\n what level?\" The skill returns release state + level + evidence + SAP\r\n citation for that specific object.\r\n\r\n### What this skill is NOT\r\n\r\n- Not an estate inventory scan → `/abap-cca`\r\n- Not a codebase-violation scanner → `/abap-cloud-check`\r\n- Not a builder — it hands off to `/abap-rap`, `/abap-extend-model`, or\r\n `/abap-generate`; it does NOT route to `/abap-enhance` as a builder (see\r\n Guardrails)\r\n\r\n## Inputs Expected\r\n\r\n**Required (will be asked if missing — Forge Rule 1):**\r\n- The requirement, approach, or object/API name to decide on\r\n- Target SAP release (e.g. S/4HANA 2023, S/4HANA Cloud)\r\n\r\n**Useful:**\r\n- Whether the consumer is external (OData/API) or internal (ABAP call chain)\r\n- ATC output already available (paste it — the skill consumes it as evidence)\r\n- Package name / language version of the custom object in scope\r\n\r\n**Auto-provided when connected:** system release, platform, deployment model\r\nfrom `<session_context>`. Do not re-ask for facts already in context.\r\n\r\n---\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Intake\r\n\r\nDetect the input mode (advise / judge / adjudicate). Restate the requirement in\r\none sentence so the user can confirm or correct it before evidence is gathered.\r\n\r\nAsk the missing questions before judging (Forge Rule 1). The minimum needed:\r\n- What is the requirement or the specific object?\r\n- Target SAP release (if not in `<session_context>`)?\r\n- Is a system connection available or should this run offline?\r\n\r\nDo not begin the decision tree until the requirement is clearly stated.\r\n\r\n### Step 2 — Gather Evidence\r\n\r\n**Hard rule: if an MCP connection is present, MUST use the live system.**\r\nKnowledge-only is strictly the offline fallback. Never prefer embedded\r\nknowledge over live evidence when the system is reachable.\r\n\r\n#### When connected (tag all findings VERIFIED)\r\n\r\nRun these tools in order; collect all results before adjudicating:\r\n\r\n| Tool | What it yields |\r\n|------|----------------|\r\n| `sap_api_state` on the SAP object(s) in scope | C0/C1/C2 contract (or none); visibility; release state from the ABAP repository |\r\n| `sap_atc_run` with the clean-core / Usage-of-APIs variant | ATC priority (P1/P2/P3/no-finding) → directly maps to Level D/C/B/A |\r\n| `sap_get_source` or `sap_object_structure` on the relevant SAP object | Real behavior definition, exposed actions/fields, whether the needed capability is actually in the released surface |\r\n| `sap_usage_references` | Downstream consumers — useful for impact of a wrapper approach |\r\n| `mcp__sap-docs__sap_get_object_details` (Cloudification Repository) | Whether the object is classified as a public Classic API (Level B) or internal (Level C) |\r\n\r\nIf a tool call returns a stateful-session error (e.g. `ICMENOSESSION`): retry\r\nonce. If the retry also fails, switch to PER-SAP-MODEL mode for that object and\r\nadd an explicit note: \"Live verification failed for {object} — result is\r\nPER-SAP-MODEL.\" Do not silently substitute knowledge for evidence.\r\n\r\n#### Always — docs lookups (regardless of mode)\r\n\r\nUse `mcp__sap-docs__search` or `mcp__sap-docs__sap_get_object_details` to\r\nretrieve SAP's current level definitions, release contract documentation, and\r\nCloudification Repository classification for the objects in scope. These feed\r\nthe citations in the Defense section. See `reference/clean-core-method.md §SAP\r\nSources Summary` for the exact URLs and what each source covers.\r\n\r\n#### When not connected (tag all findings PER SAP MODEL)\r\n\r\nReason from the method in `reference/clean-core-method.md` — the stable level\r\ndefinitions, decision tree, and contract semantics. Note explicitly that this\r\nis \"PER SAP MODEL (unverified)\" and that connected verification may change the\r\nverdict. Never present a knowledge-based guess as confirmed fact.\r\n\r\nIf the `sap-docs` tools are also unavailable in this session (no MCP at all),\r\ndo not stall waiting on a docs call: proceed from the embedded framework in\r\n`reference/clean-core-method.md`, and flag in the output that the citations are\r\nfrom model knowledge and should be confirmed against the SAP source URLs listed\r\nin `reference/clean-core-method.md §SAP Sources Summary`.\r\n\r\nIf an object/API is not found in the system: state that plainly. Do not guess\r\na classification.\r\n\r\n### Step 3 — Adjudicate\r\n\r\nFor each SAP object or API in scope:\r\n\r\n1. **State the release status.** Always make the two-axis distinction explicit\r\n where relevant (see `reference/clean-core-method.md §2`):\r\n - **Axis 1 — C0/C1/C2 contract** (clean-core axis): governs whether the\r\n object is callable from a restricted ABAP language version (ABAP for Cloud\r\n Development). C1 = callable; none = not callable.\r\n - **Axis 2 — Classic SE37/Function-Builder flag**: \"Released\" (public\r\n Classic API ≈ Level B), \"Internally released\" (SAP-internal ≈ Level C),\r\n blank (not released). This flag is a stability marker from the classic era;\r\n it has **no bearing** on C0/C1 contracts.\r\n Confusing these two axes is the single most common error in clean-core\r\n discussions. State both whenever they are relevant.\r\n\r\n2. **Assign the clean-core level** per `reference/clean-core-method.md §1`:\r\n - **A** — released APIs only; ABAP Cloud Development language version; ATC: no finding\r\n - **B** — public Classic API (SE37 \"Released\"); ATC: Priority 3\r\n - **C** — internal or non-classified SAP object; ATC: Priority 2\r\n - **D** — modifies SAP standard or accesses non-recommended objects; ATC: Priority 1\r\n\r\n3. **Carry evidence or citation for every claim** — no bare assertions (Forge\r\n Rule 3). VERIFIED findings cite the tool result. PER-SAP-MODEL findings cite\r\n the SAP source (URL from `reference/clean-core-method.md §SAP Sources Summary`).\r\n\r\n4. **Ambiguous classification?** Say so plainly. Give the definitive check\r\n (from `reference/clean-core-method.md §2 — The definitive check`):\r\n > Place the call in an *ABAP for Cloud Development* class and compile. If\r\n > the compiler rejects it, the object is not clean-core released. This check\r\n > can be reproduced via `sap_syntax_check` on a scratch class in an ABAP\r\n > Cloud package.\r\n\r\n### Step 4 — Decide\r\n\r\nRun the extension-strategy decision tree from `reference/clean-core-method.md\r\n§3`:\r\n\r\n1. Is the required capability available via a **released API** (C1 contract)?\r\n - Yes, AND all dependencies are released → **Level A** (ABAP Cloud path)\r\n - Yes, but insufficient alone (unreleased gap remains) → treat gap as step 2\r\n\r\n2. Is the required SAP object a **public Classic API** (SE37 \"Released\", listed\r\n in Cloudification Repository)?\r\n - Yes → **Level B** (classic extensibility; do NOT call from an ABAP Cloud\r\n package — restricted language version cannot access it)\r\n\r\n3. Is the object **internal** (SE37 \"Internally released\" or blank, not listed\r\n as a Classic API)?\r\n - Yes → **Level C** — build a separate custom service (wrapper) that isolates\r\n the internal call behind a stable Z-interface. Do NOT expose the unreleased\r\n object directly. Do NOT extend the delivered SAP service to add this\r\n capability (that requires a modification = D).\r\n\r\n4. Would meeting the requirement require **modifying SAP standard**?\r\n - Yes → **Level D** — reject this path. State plainly: \"The only way to\r\n force this feature into the delivered service is a standard modification,\r\n which is Level D.\" Present the Level C wrapper as the alternative.\r\n\r\n**Always document options considered and why each was rejected.** This is what\r\nmakes the verdict defensible in a stakeholder challenge. Typical rejection\r\nreasons to state:\r\n- \"Restricted language version cannot call this FM — won't compile in ABAP\r\n Cloud\" (rules out Level A for an unreleased object)\r\n- \"Extending the standard API to host new operations requires a modification\"\r\n (rules out extending a delivered API = D)\r\n- \"Key-user extensibility scope covers UI fields, not behavioural operations\"\r\n (rules out key-user path)\r\n\r\n### Step 5 — Build Path\r\n\r\nSplit the recommendation honestly using `reference/clean-core-method.md §4`:\r\n\r\n**CSPeach can build this (→ skill):**\r\n- New RAP service / wrapper BO / OData endpoint → `/abap-rap`\r\n- New field or action on an existing custom model → `/abap-extend-model`\r\n- Net-new custom class, program, or structure → `/abap-generate`\r\n\r\n**Do this by hand (steps below):**\r\n- BAdI implementations, enhancement spots, implicit enhancements — ADT has no\r\n write API for enhancement framework objects; give step-by-step manual\r\n instructions (SE18/SE19 path, interface to implement, activation steps)\r\n- Classic/internal FM internals inside a wrapper — the FM shell and function\r\n group require SE37 actions not covered by `sap_set_source` alone; give manual\r\n steps\r\n\r\n**Critical: do not route to `/abap-enhance` as a builder.** `/abap-enhance`\r\nadvises on which extension point to use and generates the implementation code,\r\nbut it cannot write classic enhancements into the system via ADT. Classic\r\nenhancement steps must be manual instructions, not a skill handoff.\r\n\r\nLabel every part clearly: \"CSPeach can build this → /abap-X\" vs \"Do this by\r\nhand — steps:\".\r\n\r\n### Step 6 — Defense\r\n\r\nProduce three things:\r\n\r\n1. **Business summary** — a plain paragraph (no jargon) the developer can paste\r\n to a stakeholder, project manager, or skeptic to explain why this approach is\r\n the right one and is not a clean-core violation.\r\n\r\n2. **Citations** — the SAP sources that back every level/release claim in this\r\n output. Minimum: the level-definition source, the release-contract source,\r\n and the Cloudification Repository or ATC finding for each adjudicated object.\r\n\r\n3. **Rebuttal set** (optional, include when the approach is likely to be\r\n challenged) — structured as: \"If they say X — you say Y, here is the proof.\"\r\n Common challenge to anticipate:\r\n - \"But SE37 shows this FM as Released\" → that is the classic stability flag,\r\n not a C1 contract; it means Level B (Classic API) when consumed by customer\r\n code, not Level A; it does not make the call compile in an ABAP Cloud class.\r\n\r\n---\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly\r\nthis shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## abap-clean-core — [Requirement / Object Name]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence verdict — recommended approach and level>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <key adjudication finding, decision, or constraint>\r\n2. <second most important — often the options-rejected summary>\r\n3. <build path split or defense highlight>\r\n\r\n**Verdict:** <VERIFIED | PER SAP MODEL> — <1-line conclusion and next step>\r\n\r\n---\r\n\r\n<then the full structured report below>\r\n```\r\n\r\n**Rules for the Top 3:**\r\n- Each bullet must be actionable and specific — not a generic category.\r\n- Include the mode label (VERIFIED / PER SAP MODEL) where relevant.\r\n- If fewer than 3 meaningful items exist, list only what is real.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in the chat stream\r\nand can paste to a stakeholder. The full structured report is saved for deeper\r\nreview. The TL;DR must be self-contained.\r\n\r\n---\r\n\r\n## Output Structure\r\n\r\n```\r\n## abap-clean-core — {requirement_or_object}\r\n\r\n## TL;DR\r\n\r\n**Headline:** {1-sentence verdict}\r\n\r\n**Top 3:**\r\n1. {key adjudication or constraint}\r\n2. {options-rejected summary}\r\n3. {build path or defense highlight}\r\n\r\n**Verdict:** {VERIFIED | PER SAP MODEL} — {conclusion and next step}\r\n\r\n---\r\n\r\n## Adjudication\r\n\r\n**Assessment mode:** VERIFIED | PER SAP MODEL (live verification failed / no system connected)\r\n\r\n| Object / API | Release Status (Axis 1: C0/C1/C2/none) | Classic Flag (Axis 2: SE37) | Clean-Core Level | Evidence / Citation |\r\n|---|---|---|---|---|\r\n| {object_name} | {contract} | {flag or N/A} | A / B / C / D | {ATC finding / sap_api_state result / SAP URL} |\r\n\r\n---\r\n\r\n## Decision\r\n\r\n**Recommended approach:** {description of the approach}\r\n**Clean-core level:** {A | B | C | D}\r\n**Summary:** {1-2 sentences explaining why this is the correct path}\r\n\r\n---\r\n\r\n## Options Considered\r\n\r\n| Option | Verdict | Reason rejected |\r\n|--------|---------|-----------------|\r\n| {option_1} | Rejected | {reason — cite evidence where possible} |\r\n| {option_2} | Rejected | {reason} |\r\n| {chosen_option} | Selected | {why this is the right path} |\r\n\r\n---\r\n\r\n## Build Path\r\n\r\n### CSPeach can build this\r\n\r\n| Step | What | Skill |\r\n|------|------|-------|\r\n| {step_n} | {what CSPeach builds} | /abap-rap | /abap-extend-model | /abap-generate |\r\n\r\n### Do this by hand\r\n\r\n| Step | Where | What |\r\n|------|-------|------|\r\n| {step_n} | {transaction / ADT location} | {what to do — concrete steps} |\r\n\r\n---\r\n\r\n## Defense\r\n\r\n### Business summary\r\n\r\n{Plain paragraph, no ABAP jargon, suitable for a stakeholder or project manager.}\r\n\r\n### Citations\r\n\r\n| Claim | Source | URL / Reference |\r\n|-------|--------|-----------------|\r\n| {level definition} | SAP Cloud ALM — Clean Core Level Overview | {URL from reference/clean-core-method.md} |\r\n| {release contract} | ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS | {URL} |\r\n| {object classification} | Cloudification Repository Viewer / ATC finding | {URL or tool result} |\r\n\r\n### Rebuttals (if applicable)\r\n\r\n| Challenge | Response | Proof |\r\n|-----------|----------|-------|\r\n| {what the skeptic says} | {your counter} | {cite the evidence} |\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n{Anything that requires system access not available in this session, or a\r\nbusiness decision outside the scope of this skill.}\r\n```\r\n\r\n---\r\n\r\n## Artifact — `clean-core-decision`\r\n\r\nEmit this envelope at the end of every output as a `.cspeach.json` file so the\r\nverdict is saved and viewable in the CSPeach viewer.\r\n\r\n```json\r\n{\r\n \"schemaVersion\": \"1.0\",\r\n \"id\": \"{uuid}\",\r\n \"createdAt\": \"{ISO-8601}\",\r\n \"source\": \"abap-clean-core\",\r\n \"artefactType\": \"clean-core-decision\",\r\n \"requirement\": \"{the requirement or object name as stated}\",\r\n \"inputMode\": \"advise | judge | adjudicate\",\r\n \"assessmentMode\": \"verified | per_sap_model\",\r\n \"adjudications\": [\r\n {\r\n \"object\": \"{object_name}\",\r\n \"releaseStatus\": \"released-C1 | released-C2 | classic-se37-released | internally-released | not-released\",\r\n \"contract\": \"C0 | C1 | C2 | none\",\r\n \"cleanCoreLevel\": \"A | B | C | D\",\r\n \"evidence\": \"{ATC finding, tool result, or citation}\",\r\n \"citation\": \"{SAP URL or note number}\"\r\n }\r\n ],\r\n \"decision\": {\r\n \"recommendedApproach\": \"{description}\",\r\n \"cleanCoreLevel\": \"A | B | C | D\",\r\n \"summary\": \"{1-2 sentence explanation}\"\r\n },\r\n \"optionsConsidered\": [\r\n {\r\n \"option\": \"{option description}\",\r\n \"verdict\": \"rejected | selected\",\r\n \"reason\": \"{reason}\"\r\n }\r\n ],\r\n \"buildPath\": {\r\n \"autoBuild\": [\r\n { \"step\": \"{step_n}\", \"description\": \"{what}\", \"handoffSkill\": \"/abap-rap | /abap-extend-model | /abap-generate\" }\r\n ],\r\n \"byHand\": [\r\n { \"step\": \"{step_n}\", \"where\": \"{transaction/ADT}\", \"what\": \"{instructions}\" }\r\n ]\r\n },\r\n \"defense\": {\r\n \"businessSummary\": \"{plain-language paragraph}\",\r\n \"citations\": [\r\n { \"claim\": \"{claim}\", \"source\": \"{source name}\", \"url\": \"{URL}\" }\r\n ],\r\n \"rebuttals\": [\r\n { \"challenge\": \"{skeptic's claim}\", \"response\": \"{counter}\", \"proof\": \"{evidence}\" }\r\n ]\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Guardrails\r\n\r\nThe following rules are hard behavioral constraints, not suggestions. They must\r\nbe active on every run.\r\n\r\n**Connected → must verify (Rule 1 of 8)**\r\n- DO: call `sap_api_state`, `sap_atc_run`, `sap_get_source`/`sap_object_structure`, and\r\n `mcp__sap-docs__sap_get_object_details` when an MCP connection is present.\r\n- DON'T: rely on embedded knowledge when a live system is reachable.\r\n\r\n**Label every verdict (Rule 2 of 8)**\r\n- DO: tag every finding VERIFIED (live evidence) or PER SAP MODEL (knowledge, unverified).\r\n- DON'T: present a knowledge-based assessment as a confirmed fact.\r\n\r\n**Evidence and citation on every claim (Rule 3 of 8 / Forge Rule 3)**\r\n- DO: cite the tool result or SAP URL for every level/release claim.\r\n- DON'T: make a bare assertion (\"this is Level C\") without naming the evidence.\r\n\r\n**State the two-axis distinction (Rule 4 of 8)**\r\n- DO: explicitly distinguish the classic SE37 release flag (stability marker)\r\n from the C0/C1 clean-core release contract wherever relevant.\r\n- DON'T: treat \"SE37 Released\" as equivalent to C1 or as proof of clean-core\r\n safety.\r\n\r\n**Ambiguity → say so + give the compiler check (Rule 5 of 8)**\r\n- DO: when classification is unclear, state the ambiguity and give the definitive\r\n check (place the call in an ABAP for Cloud Development class and compile).\r\n- DON'T: resolve ambiguity silently by picking the more convenient answer.\r\n\r\n**No overpromise in the build path (Rule 6 of 8)**\r\n- DO: route auto-build handoffs only to `/abap-rap`, `/abap-extend-model`, or\r\n `/abap-generate` — the three skills that can write objects via ADT.\r\n- DON'T: route to `/abap-enhance` as a builder — it cannot write classic\r\n enhancements through ADT. Give manual steps instead.\r\n- DON'T: claim a path is auto-buildable if the target builder skill cannot\r\n execute it through the ADT API.\r\n\r\n**Encode method, not memorized facts (Rule 7 of 8)**\r\n- DO: derive volatile per-object facts (release state, current level definitions,\r\n cloudification classification) from live evidence and docs lookups at runtime.\r\n- DON'T: answer \"object X = Level C\" from training data without checking the\r\n live system — SAP's model and per-object classifications evolve (e.g. the Aug\r\n 2025 shift from the 3-tier model to A–D levels).\r\n\r\n**Session failure → retry once, then degrade explicitly (Rule 8 of 8)**\r\n- DO: retry a failing MCP call once; if it fails again, degrade to PER-SAP-MODEL\r\n with an explicit note identifying which object's live verification failed.\r\n- DON'T: silently fall back to knowledge without telling the user that live\r\n verification was unavailable.\r\n\r\n**Never endorse a Level D path**\r\n- DO: state plainly when the only way to achieve a requirement is a standard\r\n modification, and that this path is Level D and should be rejected.\r\n- DON'T: present Level D (modify SAP standard) as a valid option.\r\n\r\n---\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-clean-core\r\n\r\nWe need to expose equipment user status (both read and change) to an external\r\nsystem via OData. System is S/4HANA 2023. We already looked at API_EQUIPMENT\r\nbut are not sure if it covers user status. Is there a clean-core path, and what\r\nlevel is it?\r\n```\r\n\r\n---\r\n\r\n## Example Output Outline\r\n\r\nThe following output is the accuracy bar for **verified mode** on this\r\nrequirement. The skill must reach these verdicts with this evidence and labeling.\r\n\r\n```\r\n## abap-clean-core — Equipment User Status Exposure (S/4HANA 2023)\r\n\r\n## TL;DR\r\n\r\n**Headline:** No released SAP API covers equipment user status — a custom RAP\r\nwrapper service consuming the classic internal FM behind a Z-interface is the\r\ncorrect path (Level C, conditional clean core).\r\n\r\n**Top 3:**\r\n1. VERIFIED: STATUS_CHANGE_EXTERN has no C1 contract (SE37 \"Internally released\"\r\n ≠ clean-core released) — calling it directly from ABAP Cloud won't compile.\r\n2. All three obvious Level A/B options (extend API_EQUIPMENT, I_EquipmentTP\r\n actions, I_EquipmentStatus) are ruled out: no user-status surface in the\r\n released behavior definition; standard modification would be required.\r\n3. Build path splits: RAP wrapper shell is auto-buildable (→ /abap-rap); the\r\n classic FM internals inside the wrapper must be written by hand.\r\n\r\n**Verdict:** VERIFIED — Level C (conditional clean core). Build a separate\r\ncustom OData/RAP wrapper service; proceed to /abap-rap for the RAP shell.\r\n\r\n---\r\n\r\n## Adjudication\r\n\r\n**Assessment mode:** VERIFIED (live sap_api_state + sap_object_structure on S/4HANA 2023)\r\n\r\n| Object / API | Release Status (Axis 1: C0/C1) | Classic Flag (Axis 2: SE37) | Clean-Core Level | Evidence / Citation |\r\n|---|---|---|---|---|\r\n| I_EquipmentTP (RAP BO) | C1 released | N/A (CDS/BDEF) | A (for its exposed surface) | sap_object_structure: exposes install/dismantle + CRUD actions; no user-status action present — VERIFIED |\r\n| I_EquipmentStatus (CDS) | C1 released | N/A | A (for its exposed fields) | sap_api_state: C1; exposes system-status flags only (EQUNR, STSMA, ESTAT); no user-status (OBJNR + individual status) — VERIFIED |\r\n| STATUS_CHANGE_EXTERN (FM) | none (no C1/C2) | \"Internally released\" (SE37) | C (when consumed by customer code) | sap_api_state: contract = none; SE37 Attributes: Internally Released — two-axis distinction applies: classic flag ≠ C1 — VERIFIED |\r\n\r\n**Two-axis note on STATUS_CHANGE_EXTERN:** SE37 shows \"Internally released\" —\r\nthis is the classic SE37 stability flag, not a C0/C1 contract. It means SAP\r\nreleased this FM for internal SAP use only. When consumed by customer code it\r\nis a Level C reference. It has no C1 contract and will not compile in an ABAP\r\nfor Cloud Development class.\r\n\r\n---\r\n\r\n## Decision\r\n\r\n**Recommended approach:** Build a separate custom RAP/OData wrapper service\r\n(`ZI_EquipmentUserStatus` CDS + RAP BO + OData V4 endpoint) whose behavior pool\r\ncalls `STATUS_CHANGE_EXTERN` in classic ABAP, behind a stable Z-interface. The\r\nexternal system consumes the Z-service, never the internal FM directly.\r\n\r\n**Clean-core level:** C (conditional clean core)\r\n\r\n**Summary:** SAP ships no released API for equipment user status. The wrapper\r\npattern isolates the internal FM behind a customer-owned stable interface — the\r\nsame design SAP uses internally (API_EQUIPMENT itself wraps non-released classic\r\nFMs). This is Level C, not a violation. No SAP standard is modified.\r\n\r\n---\r\n\r\n## Options Considered\r\n\r\n| Option | Verdict | Reason rejected |\r\n|--------|---------|-----------------|\r\n| Extend API_EQUIPMENT behavior definition to add a user-status action | Rejected (= D) | API_EQUIPMENT is a delivered RAP BO. Adding a new operation to its behavior definition requires a modification to the SAP-delivered BDEF — Level D. VERIFIED: sap_object_structure confirms no extension hook for new actions on the delivered BO. |\r\n| Call STATUS_CHANGE_EXTERN from an ABAP Cloud Development class (key-user / developer extensibility) | Rejected (won't compile) | STATUS_CHANGE_EXTERN has no C1 contract (VERIFIED: sap_api_state). Restricted language version can only call objects with C1 contract + correct visibility. The compiler will reject this call. Definitive check: place the CALL FUNCTION in a class with language version \"ABAP for Cloud Development\" and attempt activation — it will fail. |\r\n| Use I_EquipmentTP actions (Level A path) | Rejected (gap in released surface) | I_EquipmentTP is released (C1) but its behavior definition exposes only install/dismantle operations and standard CRUD. No user-status action exists on this BO. VERIFIED: sap_object_structure + Cloudification Repository. |\r\n| Use I_EquipmentStatus CDS view | Rejected (wrong status type) | I_EquipmentStatus exposes system-status flags (ESTAT), not user-status (individual status objects on OBJNR). VERIFIED: sap_get_source on I_EquipmentStatus. |\r\n| Custom RAP wrapper service (Z) calling STATUS_CHANGE_EXTERN in classic ABAP | Selected (Level C) | Wraps the internal FM behind a stable Z-interface. No SAP standard modified. Level C because the wrapper references an internal SAP object — conditional clean core with SAP's changelog applied. |\r\n\r\n---\r\n\r\n## Build Path\r\n\r\n### CSPeach can build this\r\n\r\n| Step | What | Skill |\r\n|------|------|-------|\r\n| 1 | New RAP BO (ZI_EquipmentUserStatus CDS, ZC_EquipmentUserStatus projection, ZBDEF_EquipmentUserStatus, behavior pool ZBP_EquipmentUserStatus), Service Definition + OData V4 Binding | /abap-rap |\r\n\r\n### Do this by hand\r\n\r\n| Step | Where | What |\r\n|------|-------|------|\r\n| 2 | SE37 / ADT — Function Group | Create function group ZFG_EQUIP_STATUS and function module ZFM_EQUIP_STATUS_CHANGE (the empty shell can be scaffolded via /abap-generate; only the body in step 3 is by-hand) |\r\n| 3 | SE37 — FM Source (or sap_set_source on the Z-FM) | Write the FM body: call STATUS_CHANGE_EXTERN with the correct parameters, handle SY-SUBRC, translate to a clean exception for the RAP behavior pool caller |\r\n| 4 | ADT — Behavior Pool (ZBP_EquipmentUserStatus, CCIMP include) | In the RAP behavior pool's action handler, call ZFM_EQUIP_STATUS_CHANGE (your Z-FM) — not STATUS_CHANGE_EXTERN directly. The RAP pool is in a classic ABAP package, so calling the Z-FM from classic ABAP is valid at Level C |\r\n\r\n---\r\n\r\n## Defense\r\n\r\n### Business summary\r\n\r\nSAP's own Equipment API (`API_EQUIPMENT`) wraps non-released classic function\r\nmodules internally — the wrapper pattern is SAP's own design for bridging\r\nreleased interfaces to classic implementations. Where SAP has not yet released\r\na user-status operation in its standard API surface, we apply the same pattern:\r\na new Z-OData service provides the clean, versioned interface that external\r\nsystems consume, while the implementation calls SAP's internal FM behind that\r\nstable interface. No SAP standard code is modified. ATC Priority 2 (Level C —\r\nconditional clean core) is the expected finding for this pattern: it means the\r\napproach is acceptable when applied with SAP's changelog, which is exactly what\r\nwrapping an internally-released FM achieves.\r\n\r\n### Citations\r\n\r\n| Claim | Source | URL |\r\n|-------|--------|-----|\r\n| Level C definition (Priority 2 ATC) | SAP Cloud ALM — Clean Core Level Overview | https://help.sap.com/docs/CloudALM/877c96cf971648b09ee0d0a64f7f4fef/eaa61d6ae6744ccdb0d18820fb8998c9.html |\r\n| C1 contract required for ABAP Cloud access | ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS | https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/ABENABAP_RELEASE_CONTRACTS.html |\r\n| Classic API vs C1 distinction | ABAP Keyword Docs — ABENABAP_API_RELEASE | https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/ABENABAP_API_RELEASE.html |\r\n| STATUS_CHANGE_EXTERN classification | Cloudification Repository Viewer + sap_api_state result | https://sap.github.io/abap-atc-cr-cv-s4hc/?version=objectClassifications.json |\r\n\r\n### Rebuttals\r\n\r\n| Challenge | Response | Proof |\r\n|-----------|----------|-------|\r\n| \"But SE37 shows STATUS_CHANGE_EXTERN as Released (internally released)\" | \"Internally released\" is the SE37 classic flag, not a C1 release contract. It means SAP released this FM for internal SAP use only. When customer code references it, ATC classifies the custom object at Level C (Priority 2). It has no C1 contract — placing a CALL FUNCTION statement for it in an ABAP for Cloud Development class will fail to compile. | sap_api_state on STATUS_CHANGE_EXTERN: contract = none. ABAP Keyword Docs — ABENABAP_RELEASE_CONTRACTS confirms C1 is required for cloud-accessible consumption. |\r\n| \"Why can't we just extend API_EQUIPMENT's behavior definition?\" | Adding a new operation to a SAP-delivered behavior definition (BDEF) requires a modification to the delivered source — Level D. There is no extension hook for new actions on the standard BO surface. The clean path is a separate Z-service. | sap_object_structure on I_EquipmentTP confirms no user-status action; modifying a delivered BDEF = SMODILOG entry = Level D. |\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n- Confirm the exact parameter signature of STATUS_CHANGE_EXTERN in your system\r\n (verify with sap_get_source in a live session) before writing the Z-FM body.\r\n- If the external consumer requires OData V2 instead of V4, the RAP binding type\r\n changes — clarify the consumer protocol before running /abap-rap.\r\n```\r\n",
37
+ "sha256": "04faf4312ae8a51a2405f6876a4cfc7782db9fbbc4c15960348a741cef9676c4",
31
38
  "signature": "",
32
- "signedAt": "2026-09-17T20:19:04.224Z"
39
+ "signedAt": "2026-09-28T14:37:04.248Z"
33
40
  },
34
41
  "abap-cloud-check": {
35
42
  "name": "abap-cloud-check",
36
43
  "body": "---\r\nname: abap-cloud-check\r\ndescription: >\r\n Check ABAP code for clean core and S/4HANA cloud readiness — identify\r\n unreleased APIs, classic statements, direct access to SAP standard tables,\r\n and statements not allowed in ABAP Cloud. Produces an Overall Assessment\r\n with concrete modernization guidance. Read-only.\r\nphase: VERIFY\r\nrequires_mcp: false\r\nforge_rules: [3, 4, 6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-cloud-check\r\n\r\n## Purpose\r\n\r\nEvaluate ABAP source code against the SAP Clean Core and ABAP Cloud programming\r\nmodel requirements. Identify every API, function module, table access, and\r\nlanguage statement that is either unreleased, deprecated, or restricted in\r\nS/4HANA cloud and BTP ABAP environments.\r\n\r\nThis skill does not modify code. It produces a gap analysis with a modernization\r\ndirection. For automated ATC remediation use `/abap-atc-fix`. For actual\r\nrefactoring use `/abap-refactor`.\r\n\r\n## When to Use\r\n\r\n- Before migrating a Z-program from ECC to S/4HANA\r\n- When moving on-prem custom code to BTP ABAP\r\n- When ATC ABAP Cloud Readiness check fails\r\n- As a pre-check before `/abap-migrate` analysis\r\n- When claiming a solution is \"cloud-ready\" — Forge Rule 3 forbids unverified claims\r\n\r\nWhen NOT to use: whole-estate inventory + classification — `/abap-cca`; actually fixing the findings — `/abap-upgrade-fix` or `/abap-modernize`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- ABAP source code (paste) or class/program name with MCP connection\r\n\r\nOptional (will be asked if helpful):\r\n- Target environment: S/4HANA on-prem (which release), BTP ABAP, or cloud\r\n- ATC check variant name if available\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Scan the Code\r\n\r\nIdentify all:\r\n- CALL FUNCTION statements → check release status category\r\n- Class instantiation with NEW / CREATE OBJECT → check if class is released\r\n- SELECT FROM {table} → check if table is an SAP standard table and if it has\r\n a released CDS view alternative\r\n- Dynamic SQL (EXECUTE PROGRAM, dynamic WHERE)\r\n- AUTHORITY-CHECK patterns (released in cloud)\r\n- ABAP statements not allowed in cloud context:\r\n - CALL TRANSACTION\r\n - SUBMIT\r\n - MESSAGE (type I/E/S/W without ABAP messaging framework)\r\n - WAIT UP TO\r\n - RFC function module calls\r\n - WRITE (not relevant in OO context)\r\n\r\n### Step 2 — Categorize Each Finding\r\n\r\nFor each finding, assign:\r\n- **API Status:** Released C1 / Released C2 (partner) / Not Released / Unknown\r\n- **Impact:** Blocker (cloud deployment fails) / Warning (deprecated, may be removed)\r\n / Info (works but better alternative exists)\r\n- **Successor:** What to use instead (if known with confidence)\r\n\r\nDo NOT invent release status. If uncertain, say \"Unable to confirm release status\r\nwithout system access — verify in SAP transaction SNOTE or released API catalog.\"\r\n\r\nReference `reference/released-api-patterns.md` for guidance on common patterns.\r\n\r\n### Step 3 — SAP Standard Table Access Assessment\r\n\r\nFor each SAP standard table accessed directly:\r\n- Name the table\r\n- State whether a released CDS Virtual Data Model (VDM) view exists as successor\r\n- Note that direct table access is a clean core violation if a VDM exists\r\n\r\n### Step 4 — Produce Overall Assessment\r\n\r\nRate the code: Cloud-Ready / Requires Moderate Refactoring / Significant Blocker.\r\n\r\n### Step 5 — Modernization Direction\r\n\r\nFor each blocker, give a concrete modernization direction.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Cloud Readiness Assessment — {object_name}\r\n\r\n**Target Environment:** {S/4HANA version | BTP ABAP}\r\n**Assessment Date:** {date}\r\n\r\n---\r\n\r\n## Overall Assessment\r\n\r\n**Rating:** {Cloud-Ready | Requires Moderate Refactoring | Significant Blocker}\r\n\r\n{2-3 sentence summary}\r\n\r\n---\r\n\r\n## Classic API Concerns\r\n\r\n| Item | Type | Issue | Impact | Successor |\r\n|------|------|-------|--------|-----------|\r\n| {api_name} | FM / Class / Statement | {issue} | Blocker / Warning | {successor or \"None identified\"} |\r\n\r\n---\r\n\r\n## Released vs Unreleased APIs\r\n\r\n| API | Release Status | Use Allowed | Notes |\r\n|-----|----------------|-------------|-------|\r\n| {api_name} | C1 Released / Not Released / Unknown | Yes / No | {notes} |\r\n\r\n---\r\n\r\n## SAP Standard Table Access\r\n\r\n| Table | Direct Access | Released VDM View | Clean Core Status |\r\n|-------|---------------|-------------------|-------------------|\r\n| {table} | Yes | {vdm_view or N/A} | Violation / Acceptable |\r\n\r\n---\r\n\r\n## Modernization Direction\r\n\r\n### {blocker_1}\r\n**Current:** {what the code does now}\r\n**Target:** {what to use instead}\r\n**Effort:** {Low | Medium | High}\r\n**Reference:** {SAP note, blog, or released API name}\r\n\r\n---\r\n\r\n## Clean Core Guidance\r\n\r\n{3-5 bullet points of specific guidance for this code's migration path}\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n{list of things that could not be assessed without system access or additional context}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER claim an API is released without evidence or reference\r\n- NEVER claim an API is unreleased if you are not certain — say \"Unknown —\r\n verify in system\" instead\r\n- Do not assess release status of Z-objects — they are custom, not SAP standard\r\n- If the code is already using released APIs consistently, say so explicitly\r\n- Forge Rule 3: no cloud readiness claim without evidence\r\n- Direct standard table access findings are valid only if the table name is a\r\n known SAP-delivered table (e.g. KNA1, LFA1, EKKO, VBAK) — do not flag Z-tables\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-cloud-check\r\n\r\nCheck this customer order processing class for cloud readiness. We plan to\r\nmove it to BTP ABAP eventually. S/4HANA 2023 on-prem currently.\r\n\r\nMETHOD process_customer_orders.\r\n DATA: lt_orders TYPE TABLE OF vbak,\r\n ls_order TYPE vbak,\r\n lt_items TYPE TABLE OF vbap.\r\n\r\n CALL FUNCTION 'SD_SALESDOCUMENT_GETLIST'\r\n EXPORTING\r\n sales_org = iv_vkorg\r\n TABLES\r\n sales_documents = lt_orders\r\n EXCEPTIONS\r\n OTHERS = 1.\r\n\r\n IF sy-subrc <> 0.\r\n MESSAGE 'Error reading orders' TYPE 'E'.\r\n ENDIF.\r\n\r\n LOOP AT lt_orders INTO ls_order.\r\n SELECT * FROM vbap INTO TABLE lt_items\r\n WHERE vbeln = ls_order-vbeln.\r\n\r\n CALL FUNCTION 'BAPI_CUSTOMERRETURN_CREATE'\r\n EXPORTING header = VALUE bapiret2( ).\r\n\r\n CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.\r\n ENDLOOP.\r\nENDMETHOD.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Cloud Readiness Assessment — process_customer_orders (method)\r\n\r\n**Target Environment:** BTP ABAP (current: S/4HANA 2023 on-prem)\r\n**Assessment Date:** 2026-03-29\r\n\r\n---\r\n\r\n## Overall Assessment\r\n\r\n**Rating:** Significant Blocker\r\n\r\nThis method uses multiple unreleased function modules, direct access to SAP\r\nstandard tables (VBAK, VBAP), and statements (MESSAGE type E) that are not\r\ncompatible with BTP ABAP ABAP Cloud restrictions. All four API calls require\r\nreplacement before cloud deployment.\r\n\r\n---\r\n\r\n## Classic API Concerns\r\n\r\n| Item | Type | Issue | Impact | Successor |\r\n|------------------------------|------|-------------------------------------|---------|-------------------------------------|\r\n| SD_SALESDOCUMENT_GETLIST | FM | Not released for C1 use | Blocker | CDS View I_SalesOrder (VDM) |\r\n| BAPI_CUSTOMERRETURN_CREATE | FM | Release status: Unknown — verify | Warning | Investigate released API in SD area |\r\n| BAPI_TRANSACTION_COMMIT | FM | Not released in ABAP Cloud | Blocker | Implicit commit by framework |\r\n| MESSAGE ... TYPE 'E' | Stmt | Not allowed in ABAP Cloud classes | Blocker | RAISE EXCEPTION TYPE cx_... |\r\n\r\n---\r\n\r\n## Released vs Unreleased APIs\r\n\r\n| API | Release Status | Use Allowed in Cloud | Notes |\r\n|---------------------------|----------------------|----------------------|----------------------------------------|\r\n| SD_SALESDOCUMENT_GETLIST | Not Released | No | Use I_SalesOrder CDS view SELECT |\r\n| BAPI_CUSTOMERRETURN_CREATE| Unknown | Unverifiable offline | Check in system via transaction SAP_ATC|\r\n| BAPI_TRANSACTION_COMMIT | Not Released (Cloud) | No | ABAP Cloud manages commits implicitly |\r\n\r\n---\r\n\r\n## SAP Standard Table Access\r\n\r\n| Table | Direct Access | Released VDM View | Clean Core Status |\r\n|-------|---------------|---------------------------|------------------------|\r\n| VBAK | SELECT * | I_SalesOrder | Violation |\r\n| VBAP | SELECT * | I_SalesOrderItem | Violation |\r\n\r\n---\r\n\r\n## Modernization Direction\r\n\r\n### SD_SALESDOCUMENT_GETLIST\r\n**Current:** FM call returning VBAK rows into an internal table\r\n**Target:** SELECT from I_SalesOrder CDS view with ABAP SQL\r\n**Effort:** Low\r\n**Reference:** I_SalesOrder is a released C1 view in S/4HANA SD VDM\r\n\r\n### Direct VBAP access\r\n**Current:** SELECT * FROM vbap in a loop (N+1 SQL)\r\n**Target:** SELECT from I_SalesOrderItem with a join or FOR ALL ENTRIES;\r\nuse released VDM view\r\n**Effort:** Low\r\n\r\n### BAPI_TRANSACTION_COMMIT\r\n**Current:** Explicit commit after each loop iteration\r\n**Target:** Remove — ABAP Cloud framework commits at LUW boundary;\r\nusing explicit COMMIT in cloud context causes ATC error\r\n**Effort:** Low\r\n\r\n### MESSAGE TYPE 'E'\r\n**Current:** Classic MESSAGE statement used in method body\r\n**Target:** Raise a typed exception class (CX_) with message text;\r\ncaller handles display\r\n**Effort:** Low\r\n\r\n---\r\n\r\n## Clean Core Guidance\r\n\r\n- Replace all direct SAP standard table SELECTs with released CDS VDM views\r\n- Remove BAPI_TRANSACTION_COMMIT — not needed in ABAP Cloud\r\n- Replace MESSAGE statements with exception class raises\r\n- Verify BAPI_CUSTOMERRETURN_CREATE release status in your system before investing\r\n in replacement — if not released, investigate C/4HANA SDK or ABAP SDK alternative\r\n- Run SAP_ATC with the \"ABAP Cloud\" check variant to get authoritative findings\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n- Is BAPI_CUSTOMERRETURN_CREATE released in your S/4HANA 2023 system?\r\n Check in SE80 → Function Module → API Release Status.\r\n- What is the expected transaction commit strategy? The loop structure suggests\r\n a one-commit-per-order approach — this needs redesign for ABAP Cloud.\r\n- Are there authorization requirements on the I_SalesOrder CDS view in your system?\r\n```\r\n",
37
44
  "sha256": "968812337682a8865e4fb3b8cb75e5aee3fd7c2ced8b1487337ded1ec435ec01",
38
45
  "signature": "",
39
- "signedAt": "2026-09-17T20:19:04.299Z"
46
+ "signedAt": "2026-09-28T14:37:04.277Z"
40
47
  },
41
48
  "abap-data-model": {
42
49
  "name": "abap-data-model",
43
- "body": "---\r\nname: abap-data-model\r\ndescription: \"Design and create DDIC objects — domains, data elements, tables, structures. Paste mode designs, MCP mode creates in SAP.\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-data-model\r\n\r\n## Purpose\r\n\r\nDesign and optionally create SAP ABAP Data Dictionary (DDIC) objects: domains, data elements, database tables, structures, and table types. This skill operates in two modes:\r\n\r\n- **Paste mode** (default, no MCP) — produces a complete design document with all object definitions ready for manual creation in SAP or handoff to another developer\r\n- **MCP mode** (when connected to SAP via MCP tools) — executes the design by creating objects in the connected SAP system using CDS `define table` syntax, strictly following Forge Rules 7–10\r\n\r\nIn both modes, the design phase is identical. In MCP mode, execution requires a plan review and explicit confirmation before any object is created.\r\n\r\n> **MCP Note:** In MCP mode, database tables are created via CDS `define table` syntax — NOT classic SE11 DDIC. Fields are typed by one of three routes, chosen by the **type strategy** the skill asks once per data-model phase (see \"Type Strategy — Ask Once\" below): a **custom Z-domain + Z-data-element** (via `sap_create_domain` / `sap_create_data_element`), a **standard SAP data element** referenced by name (equnr, werks_d, etc.), or a **built-in ABAP type** (abap.char, abap.numc, etc.) used directly in the table source.\r\n>\r\n> **Custom DOMA/DTEL creation is now supported** via the `sap_create_domain` and `sap_create_data_element` tools — they create real custom domains (with fixed-value lists or value tables) and data elements (with short/medium/long/heading labels) through ADT. The earlier limitation (no custom DOMA/DTEL via MCP) is **lifted on systems where the ADT DDIC write path works**. Some systems strip that path; when a custom create genuinely FAILS, fall back to the standard-type path and record the simplification (see \"Fallback Rule\" below). The fallback is now the **exception**, not the default.\r\n\r\n## When to Use\r\n\r\n- After `/abap-design` has identified DDIC objects in the object list\r\n- When a new custom database table, structure, or domain is needed for a feature\r\n- When reviewing or documenting an existing data model\r\n- When extending an existing table with an append structure (S/4HANA compatible)\r\n- Before any RAP business object is built (DDIC is the foundation)\r\n\r\nWhen NOT to use: a full RAP stack (table + CDS + BDEF + service) — `/abap-rap`; a new class or program — `/abap-generate`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE or MORE of the following:\r\n\r\n1. **Business requirement description** — what data needs to be stored and why\r\n2. **Design output from `/abap-design`** — the Objects to Create table with DDIC entries\r\n3. **Field list** — field names, proposed types, lengths, descriptions\r\n4. **Existing table name** — if designing an append structure to an existing table\r\n5. **Mode indicator** — `mode: paste` (default) or `mode: mcp` (requires MCP connection)\r\n6. **Package name** — the SAP package to assign objects to (required for MCP mode)\r\n7. **Transport number** — the transport to assign objects to (required for MCP mode; must be an ABAPForge session transport per Rule 9)\r\n\r\nIf mode is not specified, **paste mode** is assumed.\r\n\r\n## Required Behavior — Paste Mode\r\n\r\n1. **Analyze the requirement.** Identify all entities, their fields, data types, and relationships.\r\n\r\n2. **Design domains.** For each reusable fixed-value type (status codes, category indicators, yes/no flags), define a domain with: name, data type, length, fixed values list (value, description).\r\n\r\n3. **Design data elements.** For each meaningful field concept (not just technical type), define a data element with: name, domain or built-in type, field labels (short 10 chars, medium 20 chars, long 40 chars, heading 55 chars).\r\n\r\n4. **Design tables and structures.** For each table: name, delivery class, table category, buffering setting, all fields with type references, primary key fields marked. Include technical fields: MANDT (client) as first key field for client-dependent tables, CREATED_BY/CREATED_AT/CHANGED_BY/CHANGED_AT administrative fields as appropriate.\r\n\r\n5. **Design relationships.** Note foreign key relationships between tables (even if not enforcing FK constraints — documentation matters). Note which fields join to standard SAP tables (VBELN → VBAK, MATNR → MARA, etc.).\r\n\r\n6. **Produce creation sequence.** Domains before data elements before tables. State dependencies explicitly.\r\n\r\n7. **Label all assumptions.** If a field length is assumed (e.g., status code assumed 1 character), mark `[ASSUMED — confirm with business]`.\r\n\r\n8. **Forge Rules compliance.** Paste mode is read-only (Rule 6). No SAP system calls made.\r\n\r\n## Type Strategy — Ask Once\r\n\r\nBefore designing fields in MCP mode (and before the first table is built), ask the **type strategy** question **exactly once per data-model phase** — NOT per field (per-field prompting is death by prompts). Present three choices:\r\n\r\n```\r\nHow should I type new fields in this data model?\r\n\r\n 1. Custom — create custom Z-domains + Z-data-elements for every new\r\n semantic type (full DDIC control, more objects to maintain).\r\n 2. Reuse standard — reuse standard SAP data elements / built-in types wherever\r\n they fit; no custom DOMA/DTEL.\r\n 3. Hybrid (recommended) — custom Z-domain/DE for genuinely NEW business semantics\r\n (a downtime reason code, a status code, an approval state —\r\n anything with a fixed-value list or domain-specific meaning);\r\n reuse standard for plumbing (plant WERKS, timestamps,\r\n currency WAERS, client, language). This is what a good ABAP\r\n architect does.\r\n\r\nPick 1 / 2 / 3:\r\n```\r\n\r\n**Record the answer as a project convention.** Capture the chosen strategy in `work.notes` (when invoked by `/abap-plan`, this is the persisted phase state) so it is asked **once and remembered across sessions** — do NOT re-ask every time. On resume, re-state the recorded strategy (\"Type strategy: Hybrid — confirmed [date]\") instead of prompting again. There is no separate convention store; `work.notes` IS the convention record.\r\n\r\nIf no MCP connection is present (paste mode), this question is not asked — paste mode always designs full custom domains + data elements as a design document.\r\n\r\n## Type Strategy — Routing Rule\r\n\r\nOnce the strategy is recorded, route each field as follows:\r\n\r\n| Field is… | Strategy `custom` | Strategy `hybrid` | Strategy `reuse-standard` |\r\n|-----------|-------------------|-------------------|---------------------------|\r\n| **NEW business-semantic type** (reason code, status, approval state — fixed-value list or domain-specific meaning) | custom DOMA/DTEL | **custom DOMA/DTEL** | standard DE / built-in |\r\n| **Plumbing** (plant WERKS, currency WAERS, timestamps, client, language, material, company code) | custom DOMA/DTEL | **standard DE / built-in** | standard DE / built-in |\r\n\r\n### Routing → Custom DOMA/DTEL (new business semantics)\r\n\r\nFor a NEW business-semantic type under `custom` or `hybrid`:\r\n\r\n1. **Create the domain** — `sap_create_domain { name, description, dataType (CHAR/NUMC/DEC/CURR/QUAN/INT4/DATS/TIMS/…), length, decimals?, outputLength?, lowercase?, signExists?, valueTable?, fixedValues?: [{value, text}], transport?, approval_id }`.\r\n - Use `fixedValues: [{value, text}, …]` for a code list (reason codes, status codes, approval states).\r\n - Use `valueTable: 'ZCHECK_TABLE'` when values come from a check table instead of a fixed list.\r\n2. **Create the data element** — `sap_create_data_element { name, description, domainName, labels: { short, medium, long, heading }, searchHelp?, transport?, approval_id }` referencing the domain just created.\r\n3. **Use the data element in the table field** — type the CDS `define table` field with the new data element name in lowercase (e.g. `reason_code : zde_dt_reason_code;`).\r\n\r\nBoth tools require an `approval_id` (like every write tool) and go on the session transport (Rule 9). Creation order: **domain → data element → table field**. Domains before data elements before the table that uses them.\r\n\r\n### Routing → Standard DE / Built-in (plumbing, or strategy reuse-standard)\r\n\r\nFor plumbing under any strategy, and for ALL fields under `reuse-standard`, use the existing path: reference a **standard SAP data element** by name (see \"Referencing Standard Data Elements Directly\") or a **built-in** `abap.*` type. No custom DOMA/DTEL is created.\r\n\r\n## Required Behavior — MCP Mode\r\n\r\nMCP mode follows Paste Mode steps 1–7 first (design phase), asks the **Type Strategy** question once (above), then proceeds to execution:\r\n\r\n8. **Check for existing transport (Rule 9).** Call appropriate MCP tool to check if an ABAPForge session transport exists. If not, state that one must be created and ask the user to confirm transport creation before proceeding.\r\n\r\n9. **Present execution plan (Rule 8).** Before creating any object, produce a numbered plan listing:\r\n - Each object name, type, and operation (create) — including any custom domains and data elements the type strategy routed to `sap_create_domain` / `sap_create_data_element`\r\n - The package and transport assignment for each\r\n - The activation sequence (domains → data elements → tables)\r\n - Any irreversible steps (all DDIC creation is irreversible without manual SE11 deletion)\r\n End the plan with: `Plan complete. This will create [N] DDIC objects. Proceed? [y/n]`\r\n\r\n10. **Wait for explicit confirmation.** Do not proceed until the user responds `y` or equivalent.\r\n\r\n11. **Request approval ONCE per object, with op `create` (Rule 8).** In a single `request_approval` batch, list one `create` change per new object. **A `create` approval covers that object's whole lifecycle — the shell `sap_create_object`, the source write `sap_set_source` (op `modify`), and `sap_activate`.** Reuse the same `approval_id` the batch returned for that object across all three calls. Do NOT request separate `modify`/`activate` approvals for an object you are creating — that produces the approval-after-approval churn. (Approvals last 30 minutes, so a multi-object DDIC batch will not expire mid-build.)\r\n\r\n12. **Execute in dependency order: domains → data elements → tables.** Issue write-class tools sequentially, not in parallel.\r\n\r\n **a. Custom domains first** (those routed by the type strategy). For each:\r\n - Call `sap_create_domain` with `{ name, description, dataType, length, decimals?, fixedValues?, valueTable?, transport, approval_id }` — `fixedValues` for code lists, `valueTable` for check-table-backed values.\r\n - Verify activation via `sap_inactive_objects` (Rule 10).\r\n - If the create FAILS (system strips the ADT DDIC write path), stop and apply the Fallback Rule below; record the simplification.\r\n\r\n **b. Custom data elements next.** For each:\r\n - Call `sap_create_data_element` with `{ name, description, domainName, labels: { short, medium, long, heading }, searchHelp?, transport, approval_id }`, referencing the domain created in (a).\r\n - Verify activation via `sap_inactive_objects` (Rule 10).\r\n - On failure, apply the Fallback Rule and record.\r\n\r\n **c. Tables last, using CDS `define table` syntax.** For each database table:\r\n - Call `sap_create_object` with objectType `TABL`, subObjectType `DT`, name, package, and transport — this creates the TABL/DT shell (pass the object's `create` approval_id)\r\n - Call `sap_set_source` with objectType `TABL` to write the CDS `define table` source (this writes to `/ddic/tables/{name}/source/main`) — reuse the SAME approval_id. Type each field per the routing rule: custom data element name (lowercase), standard DE name (lowercase), or `abap.*` built-in.\r\n - Call `sap_activate` to activate the table — reuse the SAME approval_id\r\n - Verify activation via `sap_inactive_objects` (Rule 10)\r\n - Report result before moving to the next object\r\n - If the design includes structures (STRU), follow the same create-shell → set-source → activate pattern\r\n\r\n13. **Handle failures immediately.** If any step fails, stop the workflow, report the current system state (what was created, what was not), and ask the user for instructions. Do not attempt to create remaining objects after a failure.\r\n\r\n14. **Note: No snapshot rule for fresh creates.** Rule 7 (snapshot before write) applies to overwriting or deleting existing objects. For `sap_create_object` creating a brand-new object, there is nothing to snapshot. However, if the plan includes `sap_set_source` to set the DDIC source of an existing object, Rule 7 applies.\r\n\r\n## CDS Table Syntax Reference\r\n\r\nDatabase tables in MCP mode are written as CDS source using `define table`. This is the syntax used by `sap_set_source` with objectType `TABL`.\r\n\r\n### Annotations\r\n\r\nEvery table must have these five annotations:\r\n\r\n```\r\n@EndUserText.label : 'Short description of the table'\r\n@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE\r\n@AbapCatalog.tableCategory : #TRANSPARENT\r\n@AbapCatalog.deliveryClass : #A\r\n@AbapCatalog.dataMaintenance : #RESTRICTED\r\n```\r\n\r\nAdjust `deliveryClass` and `dataMaintenance` as appropriate:\r\n- `#A` = application data, `#C` = customizing, `#E` = system/program, `#L` = temporary\r\n- `dataMaintenance`: `#RESTRICTED` (standard), `#ALLOWED` (SM30 view), `#READ_ONLY`\r\n\r\n### Example CDS Table\r\n\r\n```sql\r\n@EndUserText.label : 'Employee Master'\r\n@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE\r\n@AbapCatalog.tableCategory : #TRANSPARENT\r\n@AbapCatalog.deliveryClass : #A\r\n@AbapCatalog.dataMaintenance : #RESTRICTED\r\ndefine table ztab_employee {\r\n key client : abap.clnt not null;\r\n key emp_id : abap.numc(10) not null;\r\n emp_name : abap.char(40);\r\n email : abap.char(100);\r\n department : abap.char(20);\r\n hire_date : abap.dats;\r\n @Semantics.amount.currencyCode: 'currency'\r\n salary : abap.curr(15,2);\r\n currency : abap.cuky;\r\n}\r\n```\r\n\r\n### Built-in ABAP Types for CDS Tables\r\n\r\n| Type | Description | Example |\r\n|------|-------------|---------|\r\n| `abap.clnt` | Client (always first key field for client-dependent tables) | `key client : abap.clnt not null;` |\r\n| `abap.char(n)` | Character string of length n | `abap.char(40)` |\r\n| `abap.numc(n)` | Numeric character string of length n | `abap.numc(10)` |\r\n| `abap.int4` | 4-byte integer | `abap.int4` |\r\n| `abap.int8` | 8-byte integer | `abap.int8` |\r\n| `abap.dats` | Date (YYYYMMDD, 8 chars) | `abap.dats` |\r\n| `abap.tims` | Time (HHMMSS, 6 chars) | `abap.tims` |\r\n| `abap.curr(n,d)` | Currency amount — must pair with `abap.cuky` reference field | `abap.curr(15,2)` |\r\n| `abap.cuky` | Currency key (reference field for CURR amounts) | `abap.cuky` |\r\n| `abap.dec(n,d)` | Packed decimal with n digits and d decimal places | `abap.dec(9,2)` |\r\n| `abap.quan(n,d)` | Quantity — must pair with `abap.unit` reference field | `abap.quan(13,3)` |\r\n| `abap.unit` | Unit of measure (reference field for QUAN quantities) | `abap.unit` |\r\n| `abap.lang` | Language key | `abap.lang` |\r\n| `abap.string` | Variable-length string (not allowed as key field) | `abap.string` |\r\n| `abap.rawstring` | Variable-length raw bytes | `abap.rawstring` |\r\n\r\n**CURR/QUAN pairing rule:** Any `abap.curr` field must be preceded by a `@Semantics.amount.currencyCode: 'fieldname'` annotation pointing to the `abap.cuky` field. Any `abap.quan` field must be preceded by `@Semantics.quantity.unitOfMeasure: 'fieldname'` pointing to the `abap.unit` field.\r\n\r\n### Referencing Standard Data Elements Directly\r\n\r\nA CDS `define table` field may be typed with a **standard SAP data element name** instead of an `abap.*` built-in. Write the data element name in lowercase as the field type — no `abap.` prefix:\r\n\r\n```sql\r\ndefine table zpm_dt_log {\r\n key client : abap.clnt not null;\r\n key downtime_id : abap.numc(12) not null;\r\n equipment : equnr; -- standard DE: Equipment number\r\n plant : werks_d; -- standard DE: Plant\r\n reason_code : abap.char(4); -- see fallback note below\r\n start_ts : timestampl; -- standard DE: UTC timestamp (long)\r\n ...\r\n}\r\n```\r\n\r\nThis is the **preferred** way to type a field whose meaning matches an existing SAP concept (equipment `EQUNR`, plant `WERKS_D`, material `MATNR`, company code `BUKRS`, user `XUBNAME`, dates/times, etc.): you inherit the standard DE's labels, conversion exits, and — crucially — its **check table / value help** for free, without creating any custom DDIC.\r\n\r\n### Fallback Rule — Custom Domain/Data Element Create FAILS (the exception)\r\n\r\nCustom DOMA/DTEL creation is the supported path now (`sap_create_domain` / `sap_create_data_element`). This fallback applies **only as the exception** — when a custom create genuinely FAILS on the target system (some systems strip the ADT DDIC write path). It is NOT the default; do not skip the create and jump straight here.\r\n\r\nWhen a `sap_create_domain` or `sap_create_data_element` call fails, apply this fallback **in order**:\r\n\r\n1. **Is there a standard DE that fits?** Use it directly (section above). E.g. equipment → `equnr`, not a new `ZDE_EQUIPMENT`.\r\n2. **No standard DE fits?** Type the field with the matching **built-in** (`abap.char(4)`, `abap.numc(10)`, …) sized to the intended domain. The field still works; it just lacks the custom domain's fixed-value list at the DDIC layer.\r\n3. **Where did the fixed values / value help go?** For a code field that would have drawn its values from a custom domain, get the value help from a **check-table foreign key** instead: the code column references a check table (its own Z check table, or a standard one), and the FK supplies the F4 help. Record this as the design intent even though the FK relationship itself is declared at the CDS/consumption layer, not in the raw `define table`.\r\n4. **Record the simplification — always.** Whatever you substitute, state it explicitly in the output (and, when invoked by `/abap-plan`, in `work.notes`): *\"Intended custom domain `ZPM_DT_REASON_CODE` (CHAR 4, fixed values OPER/MECH/ELEC/EXT) → `sap_create_domain` FAILED on this system → substituted built-in `abap.char(4)`; value help to come from check-table FK.\"* A reviewer must be able to see exactly what was traded and why.\r\n\r\nThis fallback is correct and supported when it triggers — it is not a workaround to apologise for. The data model is complete; only the optional DDIC-layer value list is deferred to a check table.\r\n\r\n### Table Maintenance (SM30/SE54) — Not Available via ADT\r\n\r\nThe SE54 **table-maintenance generator** — which builds the generated SM30 maintenance dialog (screens, function group, events) — is a **SAP GUI-only** transaction. There is no ADT REST endpoint for it. So in MCP mode you can create and activate a configuration table, but you CANNOT generate its SM30 dialog. Same shape as the custom DOMA/DTEL gap above; apply the same fallback-and-record discipline.\r\n\r\nFor a configuration/checktable that the design wants SM30-maintainable:\r\n\r\n1. **Make the table editable without a generated dialog.** Create it with `@AbapCatalog.dataMaintenance: #ALLOWED` (and delivery class `#C`). With `#ALLOWED` the table is directly maintainable in **SE16N** — sufficient for low-volume config (reason codes, type lists) from day one, no generated dialog required.\r\n2. **Seed initial values via an initial-load report** (an `abap-generate` report doing `MODIFY <table> FROM TABLE`) when the design needs starter rows (e.g. a seeded `OTHER` reason code). This path is fully ADT-drivable.\r\n3. **Flag SM30/SE54 generation as a manual GUI step** — record in the output (and `work.notes` when called from `/abap-plan`) the exact manual recipe: *\"SM30 dialog NOT generated (SE54 is GUI-only, not in ADT). Table is `#ALLOWED` → SE16N-maintainable now. To add the generated SM30 dialog: SE11 → table → Utilities → Table Maintenance Generator (SE54), authorization group + function group + maintenance type, then transport.\"* Do not claim \"SM30 view generated\" when it was not.\r\n4. **Value help is unaffected** — F4 on a code field comes from the check-table foreign key, not from SM30.\r\n\r\nNever report a maintenance dialog as generated/tested when the generation can only happen in the GUI. Like the DOMA fallback, this is a clean, traceable deferral — the table and its data are complete via ADT; only the optional generated dialog is a manual step.\r\n\r\n### Administrative Timestamp Fields (RAP-managed)\r\n\r\nFor RAP-enabled tables, use these released types instead of raw DATS/TIMS for the admin fields:\r\n\r\n```\r\ncreated_by : abp_creation_user;\r\ncreated_at : abp_creation_tstmpl;\r\nlocal_last_changed_by : abp_locinst_lastchange_user;\r\nlocal_last_changed_at : abp_locinst_lastchange_tstmpl;\r\nlast_changed_at : abp_lastchange_tstmpl;\r\n```\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure — Paste Mode\r\n\r\n```\r\n## Data Model Design — [FEATURE NAME]\r\n\r\n**Mode:** Paste (design document only — create objects manually in SE11)\r\n**Objects:** [N domains, N data elements, N tables/structures]\r\n\r\n---\r\n\r\n### Domains\r\n\r\n#### [DOMAIN NAME]\r\n- **Data Type:** [CHAR / NUMC / DEC / DATS / TIMS / etc.]\r\n- **Length:** [N]\r\n- **Description:** [What this domain represents]\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| [value] | [description] |\r\n\r\n---\r\n\r\n### Data Elements\r\n\r\n#### [DATA ELEMENT NAME]\r\n- **Domain / Type:** [Domain name, or built-in type if no domain]\r\n- **Description:** [What this field means]\r\n- **Labels:**\r\n - Short (10): [text]\r\n - Medium (20): [text]\r\n - Long (40): [text]\r\n - Heading (55): [text]\r\n- **Search Help:** [SE field name if applicable, or NONE]\r\n\r\n---\r\n\r\n### Tables\r\n\r\n#### [TABLE NAME]\r\n- **Description:** [What this table stores]\r\n- **Delivery Class:** [A = application, C = customizing, E = system, L = temporary, etc.]\r\n- **Table Category:** [Transparent]\r\n- **Buffering:** [Not buffered / Fully buffered / Single record buffered]\r\n\r\n**Fields:**\r\n\r\n| Field | Key | Data Element | Type | Length | Null | Description |\r\n|-------|-----|-------------|------|--------|------|-------------|\r\n| MANDT | ✓ | MANDT | CLNT | 3 | No | Client |\r\n| [FIELD] | [✓/—] | [DE name] | [type] | [len] | [No/Yes] | [description] |\r\n\r\n**Technical Notes:**\r\n- [Any indexing recommendations]\r\n- [Foreign key relationships]\r\n- [Archiving object if known]\r\n\r\n---\r\n\r\n### Structures\r\n\r\n#### [STRUCTURE NAME]\r\n- **Description:** [What this structure represents]\r\n- **Use:** [Transfer structure for FM / result structure for CDS / etc.]\r\n\r\n**Fields:**\r\n\r\n| Field | Data Element | Type | Length | Description |\r\n|-------|-------------|------|--------|-------------|\r\n| [FIELD] | [DE name] | [type] | [len] | [description] |\r\n\r\n---\r\n\r\n### Relationships\r\n\r\n| From Table | Field | To Table/View | Field | Type |\r\n|-----------|-------|--------------|-------|------|\r\n| [table] | [field] | [table] | [field] | Foreign Key / Association / Documented reference |\r\n\r\n---\r\n\r\n### Creation Sequence\r\n1. [Object name] (Domain) — create first, no dependencies\r\n2. [Object name] (Domain) — create first, no dependencies\r\n3. [Object name] (Data Element) — after domains\r\n4. [Object name] (Data Element) — after domains\r\n5. [Object name] (DB Table) — after data elements\r\n```\r\n\r\n## Output Structure — MCP Mode\r\n\r\nThe design section above is produced first, then:\r\n\r\n```\r\n## Execution Plan — MCP Mode\r\n\r\n**Transport:** [transport number] (ABAPForge session transport)\r\n**Package:** [package name]\r\n**Type strategy:** Hybrid (recorded in work.notes) — custom DOMA/DTEL for new semantics, standard/built-in for plumbing\r\n**Method:** sap_create_domain / sap_create_data_element for new semantic types, CDS define table syntax for tables\r\n\r\n### Steps\r\n\r\n1. Create domain ZDOM_DT_REASON → sap_create_domain (CHAR 4, fixedValues OPER/MECH/ELEC/EXT) → Transport [TR] → verify via sap_inactive_objects\r\n2. Create data element ZDE_DT_REASON → sap_create_data_element (domainName ZDOM_DT_REASON, labels) → Transport [TR] → verify\r\n3. Create table ZAPPR_REQUEST shell → sap_create_object (TABL/DT) → Package [X] → Transport [TR]\r\n4. Write ZAPPR_REQUEST source → sap_set_source (objectType: TABL) with CDS define table source (reason_code : zde_dt_reason; plant : werks_d)\r\n5. Activate ZAPPR_REQUEST → sap_activate → verify via sap_inactive_objects\r\n\r\nOrder: domains → data elements → tables. Each table: create shell → write CDS source → activate → verify. Stop on any failure.\r\n\r\nPlan complete. This will create 1 domain, 1 data element, 1 database table. Proceed? [y/n]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Never invent SAP built-in types.** In paste mode, use classic DDIC types: CHAR, NUMC, DEC, CURR, QUAN, DATS, TIMS, INT4, INT8, CLNT, LANG, UNIT, CUKY, ACCP, RAWSTRING, STRING, RAW, FLTP, DECFLOAT16, DECFLOAT34. In MCP mode, type each field with ONE of: a valid CDS built-in (abap.char(n), abap.numc(n), abap.int4, abap.int8, abap.dats, abap.tims, abap.curr(n,d), abap.cuky, abap.dec(n,d), abap.quan(n,d), abap.unit, abap.clnt, abap.lang, abap.string, abap.rawstring); a real **standard** SAP data element name referenced directly in lowercase (equnr, werks_d, matnr, bukrs, timestampl, …) — only reference DEs you know exist in the target release, never invent one; or a **custom Z-data-element you created in this run** via `sap_create_data_element`.\r\n- **Ask the type strategy ONCE, not per field.** Ask the Type Strategy question once per data-model phase, record the answer in `work.notes`, and route every field by the recorded strategy (see \"Type Strategy — Routing Rule\"). Do NOT re-ask on resume — re-state the recorded strategy. Under `custom`/`hybrid`, create custom domains + data elements for new business-semantic types via `sap_create_domain` / `sap_create_data_element` (domain → data element → table, on the session transport, each with an approval_id). Only fall back to standard/built-in types when a custom create genuinely FAILS — and **record the substitution** in the output (and `work.notes` when called from `/abap-plan`) per the Fallback Rule.\r\n- **Never reference SAP-delivered tables without confirming they exist in the target release.** If a table is only available in S/4HANA (e.g., ACDOCA), mark it `[S/4HANA only — confirm release]`.\r\n- **Label all assumptions about field lengths and types.** Business requirements rarely specify technical data types — every type decision is an assumption unless explicitly stated.\r\n- **MANDT as first key field** for all client-dependent transparent tables. Never omit it. Flag if the user's design omits it.\r\n- **For MCP mode: follow Rules 7–10 without exception.** No object creation without transport. No proceeding after a syntax check failure. No skipping the activation verification.\r\n- **Do not design modifications to SAP-delivered tables.** Use append structures or customer include CI_ structures. If the user asks to add a field directly to a standard table (e.g., VBAK), flag this as a modification that violates clean core and propose the append structure alternative.\r\n- **Follow the 10 Forge Rules.**\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-data-model mode: paste\r\n\r\nDesign the DDIC objects for a customer credit approval rules table. Business requirements:\r\n\r\n- Customers can have custom credit approval thresholds per company code\r\n- Three approval levels: automatic (no approval needed), single approver, dual approver\r\n- Thresholds are amount-based in customer's credit currency\r\n- Records can be active or inactive (soft delete)\r\n- Must track who created and last changed the record\r\n- Package: ZCREDIT_MGMT\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Data Model Design — Customer Credit Approval Rules\r\n\r\n**Mode:** Paste (design document only — create objects manually in SE11)\r\n**Objects:** 3 domains, 5 data elements, 1 table\r\n\r\n---\r\n\r\n### Domains\r\n\r\n#### ZDOM_CREDIT_APPR_LEVEL\r\n- **Data Type:** CHAR\r\n- **Length:** 1\r\n- **Description:** Credit approval level: automatic, single, or dual approver required\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| A | Automatic — no approval required |\r\n| S | Single approver required |\r\n| D | Dual approver required |\r\n\r\n[ASSUMED — 1-character code is assumed; confirm with business if longer codes are used]\r\n\r\n#### ZDOM_ACTIVE_INACTIVE\r\n- **Data Type:** CHAR\r\n- **Length:** 1\r\n- **Description:** Active/Inactive flag for soft-delete pattern\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| X | Active |\r\n| | Inactive (space = inactive) |\r\n\r\n[ASSUMED — space-as-inactive is a common SAP pattern; confirm this convention is acceptable]\r\n\r\n#### ZDOM_CREDIT_AMOUNT\r\n- **Data Type:** CURR\r\n- **Length:** 15\r\n- **Decimal Places:** 2\r\n- **Description:** Credit threshold amount — must be paired with a currency key field (CUKY reference)\r\n- **Fixed Values:** None (amount domain, no fixed values)\r\n\r\n---\r\n\r\n### Data Elements\r\n\r\n#### ZDE_CREDIT_APPR_LEVEL\r\n- **Domain:** ZDOM_CREDIT_APPR_LEVEL\r\n- **Description:** Credit approval level required for a transaction\r\n- **Labels:**\r\n - Short (10): Appr.Level\r\n - Medium (20): Approval Level\r\n - Long (40): Credit Approval Level\r\n - Heading (55): Credit Approval Level Required\r\n- **Search Help:** NONE (fixed values come from domain)\r\n\r\n#### ZDE_CREDIT_APPR_THRESHOLD\r\n- **Domain:** ZDOM_CREDIT_AMOUNT\r\n- **Description:** Minimum order amount that triggers this approval level\r\n- **Labels:**\r\n - Short (10): Threshold\r\n - Medium (20): Approval Threshold\r\n - Long (40): Credit Approval Threshold Amount\r\n - Heading (55): Approval Threshold Amount (Credit Currency)\r\n- **Search Help:** NONE\r\n\r\n#### ZDE_CREDIT_RULE_ACTIVE\r\n- **Domain:** ZDOM_ACTIVE_INACTIVE\r\n- **Description:** Whether this approval rule is currently in effect\r\n- **Labels:**\r\n - Short (10): Active\r\n - Medium (20): Rule Active\r\n - Long (40): Approval Rule Active Indicator\r\n - Heading (55): Active\r\n- **Search Help:** NONE\r\n\r\n#### ZDE_CREDIT_RULE_ID\r\n- **Data Type:** NUMC (no domain — technical key)\r\n- **Length:** 10\r\n- **Description:** Internal numeric key for a credit approval rule record\r\n- **Labels:**\r\n - Short (10): Rule ID\r\n - Medium (20): Credit Rule ID\r\n - Long (40): Credit Approval Rule ID\r\n - Heading (55): Credit Approval Rule ID\r\n- **Search Help:** NONE\r\n\r\n[ASSUMED — numeric surrogate key assumed; confirm if business key (KUNNR + BUKRS + APPR_LEVEL) is preferred as sole key]\r\n\r\n#### ZDE_CREDIT_BUKRS\r\n- **Domain:** Reuse standard SAP data element BUKRS (Company Code)\r\n- **Note:** Do NOT create a new data element — reuse BUKRS directly. This provides automatic search help, label, and foreign key to T001.\r\n\r\n---\r\n\r\n### Tables\r\n\r\n#### ZCREDIT_APPR_RULES\r\n- **Description:** Customer credit approval thresholds per company code and approval level\r\n- **Delivery Class:** C (Customizing — maintained by business configuration, not application code)\r\n- **Table Category:** Transparent\r\n- **Buffering:** Fully buffered (customizing data, read frequently, changes infrequent)\r\n\r\n**Fields:**\r\n\r\n| Field | Key | Data Element | Type | Length | Null | Description |\r\n|-------|-----|-------------|------|--------|------|-------------|\r\n| MANDT | ✓ | MANDT | CLNT | 3 | No | Client |\r\n| RULE_ID | ✓ | ZDE_CREDIT_RULE_ID | NUMC | 10 | No | Credit rule internal ID |\r\n| BUKRS | — | BUKRS | CHAR | 4 | No | Company code |\r\n| KUNNR | — | KUNNR | CHAR | 10 | No | Customer number |\r\n| APPR_LEVEL | — | ZDE_CREDIT_APPR_LEVEL | CHAR | 1 | No | Approval level (A/S/D) |\r\n| THRESHOLD_AMT | — | ZDE_CREDIT_APPR_THRESHOLD | CURR | 15,2 | No | Threshold amount |\r\n| THRESHOLD_CURR | — | WAERS | CUKY | 5 | No | Currency key for threshold (reference field for THRESHOLD_AMT) |\r\n| VALID_FROM | — | DATS | DATS | 8 | No | Rule valid from date |\r\n| VALID_TO | — | DATS | DATS | 8 | No | Rule valid to date (9999/12/31 = no expiry) |\r\n| ACTIVE | — | ZDE_CREDIT_RULE_ACTIVE | CHAR | 1 | No | Active indicator |\r\n| CREATED_BY | — | ERNAM | CHAR | 12 | No | Created by (user ID) |\r\n| CREATED_AT | — | ERDAT | DATS | 8 | No | Created on date |\r\n| CREATED_TM | — | ERZET | TIMS | 6 | No | Created at time |\r\n| CHANGED_BY | — | AENAM | CHAR | 12 | No | Last changed by |\r\n| CHANGED_AT | — | AEDAT | DATS | 8 | No | Last changed date |\r\n\r\n**Technical Notes:**\r\n- THRESHOLD_AMT is type CURR and MUST reference THRESHOLD_CURR as its currency reference field. Set this in SE11 via the \"Currency/Quantity Fields\" tab.\r\n- Consider a secondary index on (MANDT, BUKRS, KUNNR, ACTIVE) to support the most common query pattern: \"all active rules for a customer in a company code.\"\r\n- VALID_FROM / VALID_TO enables time-dependent rules. Application code must filter WHERE VALID_FROM <= sy-datum AND VALID_TO >= sy-datum.\r\n- Delivery class C means this table appears in customizing transport requests (via SM30/view cluster) rather than in workbench requests. A maintenance view (SE54) should be created for business users to maintain rules — this is out of scope for this data model design but must be planned.\r\n- [ASSUMED] RULE_ID as numeric surrogate key. If business prefers a natural key of MANDT + BUKRS + KUNNR + APPR_LEVEL, the field RULE_ID is unnecessary and those four fields become the composite primary key. Confirm before creating.\r\n\r\n---\r\n\r\n### Structures\r\n\r\nNo standalone structures required for this data model. The table ZCREDIT_APPR_RULES can be used directly as a structure type where needed.\r\n\r\nIf a transfer structure is needed for a function module or class method, define it inline using `LIKE LINE OF` or create a structure with the relevant subset of fields. Do not create a structure that duplicates the full table definition.\r\n\r\n---\r\n\r\n### Relationships\r\n\r\n| From Table | Field | To Table | Field | Type |\r\n|-----------|-------|---------|-------|------|\r\n| ZCREDIT_APPR_RULES | MANDT+BUKRS | T001 | MANDT+BUKRS | Foreign Key — company code must exist |\r\n| ZCREDIT_APPR_RULES | MANDT+KUNNR | KNA1 | MANDT+KUNNR | Foreign Key — customer must exist |\r\n| ZCREDIT_APPR_RULES | THRESHOLD_CURR | TCURC | WAERS | Foreign Key — currency must be valid |\r\n\r\n---\r\n\r\n### Creation Sequence\r\n1. ZDOM_CREDIT_APPR_LEVEL (Domain) — no dependencies; create and activate first\r\n2. ZDOM_ACTIVE_INACTIVE (Domain) — no dependencies; create and activate\r\n3. ZDOM_CREDIT_AMOUNT (Domain) — no dependencies; create and activate\r\n4. ZDE_CREDIT_APPR_LEVEL (Data Element) — depends on domain 1\r\n5. ZDE_CREDIT_APPR_THRESHOLD (Data Element) — depends on domain 3\r\n6. ZDE_CREDIT_RULE_ACTIVE (Data Element) — depends on domain 2\r\n7. ZDE_CREDIT_RULE_ID (Data Element) — no domain dependency; create after other DEs for clarity\r\n8. ZCREDIT_APPR_RULES (DB Table) — depends on all data elements (steps 4–7); create last; activate and verify; create secondary index after table activation\r\n```\r\n",
44
- "sha256": "b84a47b55f138945c965c6df1b87c229c722dd1b720f66d7ac77f1156fd34a2f",
50
+ "body": "---\r\nname: abap-data-model\r\ndescription: \"Design and create DDIC objects — domains, data elements, tables, structures. Paste mode designs, MCP mode creates in SAP.\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-data-model\r\n\r\n## Purpose\r\n\r\nDesign and optionally create SAP ABAP Data Dictionary (DDIC) objects: domains, data elements, database tables, structures, and table types. This skill operates in two modes:\r\n\r\n- **Paste mode** (default, no MCP) — produces a complete design document with all object definitions ready for manual creation in SAP or handoff to another developer\r\n- **MCP mode** (when connected to SAP via MCP tools) — executes the design by creating objects in the connected SAP system using CDS `define table` syntax, strictly following Forge Rules 7–10\r\n\r\nIn both modes, the design phase is identical. In MCP mode, execution requires a plan review and explicit confirmation before any object is created.\r\n\r\n> **MCP Note:** In MCP mode, database tables are created via CDS `define table` syntax — NOT classic SE11 DDIC. Fields are typed by one of three routes, chosen by the **type strategy** the skill asks once per data-model phase (see \"Type Strategy — Ask Once\" below): a **custom Z-domain + Z-data-element** (via `sap_create_domain` / `sap_create_data_element`), a **standard SAP data element** referenced by name (equnr, werks_d, etc.), or a **built-in ABAP type** (abap.char, abap.numc, etc.) used directly in the table source.\r\n>\r\n> **Custom DOMA/DTEL creation is now supported** via the `sap_create_domain` and `sap_create_data_element` tools — they create real custom domains (with fixed-value lists or value tables) and data elements (with short/medium/long/heading labels) through ADT. The earlier limitation (no custom DOMA/DTEL via MCP) is **lifted on systems where the ADT DDIC write path works**. Some systems strip that path; when a custom create genuinely FAILS, fall back to the standard-type path and record the simplification (see \"Fallback Rule\" below). The fallback is now the **exception**, not the default.\r\n\r\n## When to Use\r\n\r\n- After `/abap-design` has identified DDIC objects in the object list\r\n- When a new custom database table, structure, or domain is needed for a feature\r\n- When reviewing or documenting an existing data model\r\n- When extending an existing table with an append structure (S/4HANA compatible)\r\n- Before any RAP business object is built (DDIC is the foundation)\r\n\r\nWhen NOT to use: a full RAP stack (table + CDS + BDEF + service) — `/abap-rap`; a new class or program — `/abap-generate`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE or MORE of the following:\r\n\r\n1. **Business requirement description** — what data needs to be stored and why\r\n2. **Design output from `/abap-design`** — the Objects to Create table with DDIC entries\r\n3. **Field list** — field names, proposed types, lengths, descriptions\r\n4. **Existing table name** — if designing an append structure to an existing table\r\n5. **Mode indicator** — `mode: paste` (default) or `mode: mcp` (requires MCP connection)\r\n6. **Package name** — the SAP package to assign objects to (required for MCP mode)\r\n7. **Transport number** — the transport to assign objects to (required for MCP mode; must be a dedicated session transport per Rule 9)\r\n\r\nIf mode is not specified, **paste mode** is assumed.\r\n\r\n## Required Behavior — Paste Mode\r\n\r\n1. **Analyze the requirement.** Identify all entities, their fields, data types, and relationships.\r\n\r\n2. **Design domains.** For each reusable fixed-value type (status codes, category indicators, yes/no flags), define a domain with: name, data type, length, fixed values list (value, description).\r\n\r\n3. **Design data elements.** For each meaningful field concept (not just technical type), define a data element with: name, domain or built-in type, field labels (short 10 chars, medium 20 chars, long 40 chars, heading 55 chars).\r\n\r\n4. **Design tables and structures.** For each table: name, delivery class, table category, buffering setting, all fields with type references, primary key fields marked. Include technical fields: MANDT (client) as first key field for client-dependent tables, CREATED_BY/CREATED_AT/CHANGED_BY/CHANGED_AT administrative fields as appropriate.\r\n\r\n5. **Design relationships.** Note foreign key relationships between tables (even if not enforcing FK constraints — documentation matters). Note which fields join to standard SAP tables (VBELN → VBAK, MATNR → MARA, etc.).\r\n\r\n6. **Produce creation sequence.** Domains before data elements before tables. State dependencies explicitly.\r\n\r\n7. **Label all assumptions.** If a field length is assumed (e.g., status code assumed 1 character), mark `[ASSUMED — confirm with business]`.\r\n\r\n8. **Forge Rules compliance.** Paste mode is read-only (Rule 6). No SAP system calls made.\r\n\r\n## Type Strategy — Ask Once\r\n\r\nBefore designing fields in MCP mode (and before the first table is built), ask the **type strategy** question **exactly once per data-model phase** — NOT per field (per-field prompting is death by prompts). Present three choices:\r\n\r\n```\r\nHow should I type new fields in this data model?\r\n\r\n 1. Custom — create custom Z-domains + Z-data-elements for every new\r\n semantic type (full DDIC control, more objects to maintain).\r\n 2. Reuse standard — reuse standard SAP data elements / built-in types wherever\r\n they fit; no custom DOMA/DTEL.\r\n 3. Hybrid (recommended) — custom Z-domain/DE for genuinely NEW business semantics\r\n (a downtime reason code, a status code, an approval state —\r\n anything with a fixed-value list or domain-specific meaning);\r\n reuse standard for plumbing (plant WERKS, timestamps,\r\n currency WAERS, client, language). This is what a good ABAP\r\n architect does.\r\n\r\nPick 1 / 2 / 3:\r\n```\r\n\r\n**Record the answer as a project convention.** Capture the chosen strategy in `work.notes` (when invoked by `/abap-plan`, this is the persisted phase state) so it is asked **once and remembered across sessions** — do NOT re-ask every time. On resume, re-state the recorded strategy (\"Type strategy: Hybrid — confirmed [date]\") instead of prompting again. There is no separate convention store; `work.notes` IS the convention record.\r\n\r\nIf no MCP connection is present (paste mode), this question is not asked — paste mode always designs full custom domains + data elements as a design document.\r\n\r\n## Type Strategy — Routing Rule\r\n\r\nOnce the strategy is recorded, route each field as follows:\r\n\r\n| Field is… | Strategy `custom` | Strategy `hybrid` | Strategy `reuse-standard` |\r\n|-----------|-------------------|-------------------|---------------------------|\r\n| **NEW business-semantic type** (reason code, status, approval state — fixed-value list or domain-specific meaning) | custom DOMA/DTEL | **custom DOMA/DTEL** | standard DE / built-in |\r\n| **Plumbing** (plant WERKS, currency WAERS, timestamps, client, language, material, company code) | custom DOMA/DTEL | **standard DE / built-in** | standard DE / built-in |\r\n\r\n### Routing → Custom DOMA/DTEL (new business semantics)\r\n\r\nFor a NEW business-semantic type under `custom` or `hybrid`:\r\n\r\n1. **Create the domain** — `sap_create_domain { name, description, dataType (CHAR/NUMC/DEC/CURR/QUAN/INT4/DATS/TIMS/…), length, decimals?, outputLength?, lowercase?, signExists?, valueTable?, fixedValues?: [{value, text}], transport?, approval_id }`.\r\n - Use `fixedValues: [{value, text}, …]` for a code list (reason codes, status codes, approval states).\r\n - Use `valueTable: 'ZCHECK_TABLE'` when values come from a check table instead of a fixed list.\r\n2. **Create the data element** — `sap_create_data_element { name, description, domainName, labels: { short, medium, long, heading }, searchHelp?, transport?, approval_id }` referencing the domain just created.\r\n3. **Use the data element in the table field** — type the CDS `define table` field with the new data element name in lowercase (e.g. `reason_code : zde_dt_reason_code;`).\r\n\r\nBoth tools require an `approval_id` (like every write tool) and go on the session transport (Rule 9). Creation order: **domain → data element → table field**. Domains before data elements before the table that uses them.\r\n\r\n### Routing → Standard DE / Built-in (plumbing, or strategy reuse-standard)\r\n\r\nFor plumbing under any strategy, and for ALL fields under `reuse-standard`, use the existing path: reference a **standard SAP data element** by name (see \"Referencing Standard Data Elements Directly\") or a **built-in** `abap.*` type. No custom DOMA/DTEL is created.\r\n\r\n## Required Behavior — MCP Mode\r\n\r\nMCP mode follows Paste Mode steps 1–7 first (design phase), asks the **Type Strategy** question once (above), then proceeds to execution:\r\n\r\n8. **Check for existing transport (Rule 9).** Call appropriate MCP tool to check if a session transport for this work exists. If not, state that one must be created and ask the user to confirm transport creation before proceeding.\r\n\r\n9. **Present execution plan (Rule 8).** Before creating any object, produce a numbered plan listing:\r\n - Each object name, type, and operation (create) — including any custom domains and data elements the type strategy routed to `sap_create_domain` / `sap_create_data_element`\r\n - The package and transport assignment for each\r\n - The activation sequence (domains → data elements → tables)\r\n - Any irreversible steps (all DDIC creation is irreversible without manual SE11 deletion)\r\n End the plan with: `Plan complete. This will create [N] DDIC objects. Proceed? [y/n]`\r\n\r\n10. **Wait for explicit confirmation.** Do not proceed until the user responds `y` or equivalent.\r\n\r\n11. **Request approval ONCE per object, with op `create` (Rule 8).** In a single `request_approval` batch, list one `create` change per new object. **A `create` approval covers that object's whole lifecycle — the shell `sap_create_object`, the source write `sap_set_source` (op `modify`), and `sap_activate`.** Reuse the same `approval_id` the batch returned for that object across all three calls. Do NOT request separate `modify`/`activate` approvals for an object you are creating — that produces the approval-after-approval churn. (Approvals last 30 minutes, so a multi-object DDIC batch will not expire mid-build.)\r\n\r\n12. **Execute in dependency order: domains → data elements → tables.** Issue write-class tools sequentially, not in parallel.\r\n\r\n **a. Custom domains first** (those routed by the type strategy). For each:\r\n - Call `sap_create_domain` with `{ name, description, dataType, length, decimals?, fixedValues?, valueTable?, transport, approval_id }` — `fixedValues` for code lists, `valueTable` for check-table-backed values.\r\n - Verify activation via `sap_inactive_objects` (Rule 10).\r\n - If the create FAILS (system strips the ADT DDIC write path), stop and apply the Fallback Rule below; record the simplification.\r\n\r\n **b. Custom data elements next.** For each:\r\n - Call `sap_create_data_element` with `{ name, description, domainName, labels: { short, medium, long, heading }, searchHelp?, transport, approval_id }`, referencing the domain created in (a).\r\n - Verify activation via `sap_inactive_objects` (Rule 10).\r\n - On failure, apply the Fallback Rule and record.\r\n\r\n **c. Tables last, using CDS `define table` syntax.** For each database table:\r\n - Call `sap_create_object` with objectType `TABL`, subObjectType `DT`, name, package, and transport — this creates the TABL/DT shell (pass the object's `create` approval_id)\r\n - Call `sap_set_source` with objectType `TABL` to write the CDS `define table` source (this writes to `/ddic/tables/{name}/source/main`) — reuse the SAME approval_id. Type each field per the routing rule: custom data element name (lowercase), standard DE name (lowercase), or `abap.*` built-in.\r\n - Call `sap_activate` to activate the table — reuse the SAME approval_id\r\n - Verify activation via `sap_inactive_objects` (Rule 10)\r\n - Report result before moving to the next object\r\n - If the design includes structures (STRU), follow the same create-shell → set-source → activate pattern\r\n\r\n13. **Handle failures immediately.** If any step fails, stop the workflow, report the current system state (what was created, what was not), and ask the user for instructions. Do not attempt to create remaining objects after a failure.\r\n\r\n14. **Note: No snapshot rule for fresh creates.** Rule 7 (snapshot before write) applies to overwriting or deleting existing objects. For `sap_create_object` creating a brand-new object, there is nothing to snapshot. However, if the plan includes `sap_set_source` to set the DDIC source of an existing object, Rule 7 applies.\r\n\r\n## CDS Table Syntax Reference\r\n\r\nDatabase tables in MCP mode are written as CDS source using `define table`. This is the syntax used by `sap_set_source` with objectType `TABL`.\r\n\r\n### Annotations\r\n\r\nEvery table must have these five annotations:\r\n\r\n```\r\n@EndUserText.label : 'Short description of the table'\r\n@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE\r\n@AbapCatalog.tableCategory : #TRANSPARENT\r\n@AbapCatalog.deliveryClass : #A\r\n@AbapCatalog.dataMaintenance : #RESTRICTED\r\n```\r\n\r\nAdjust `deliveryClass` and `dataMaintenance` as appropriate:\r\n- `#A` = application data, `#C` = customizing, `#E` = system/program, `#L` = temporary\r\n- `dataMaintenance`: `#RESTRICTED` (standard), `#ALLOWED` (SM30 view), `#READ_ONLY`\r\n\r\n### Example CDS Table\r\n\r\n```sql\r\n@EndUserText.label : 'Employee Master'\r\n@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE\r\n@AbapCatalog.tableCategory : #TRANSPARENT\r\n@AbapCatalog.deliveryClass : #A\r\n@AbapCatalog.dataMaintenance : #RESTRICTED\r\ndefine table ztab_employee {\r\n key client : abap.clnt not null;\r\n key emp_id : abap.numc(10) not null;\r\n emp_name : abap.char(40);\r\n email : abap.char(100);\r\n department : abap.char(20);\r\n hire_date : abap.dats;\r\n @Semantics.amount.currencyCode: 'currency'\r\n salary : abap.curr(15,2);\r\n currency : abap.cuky;\r\n}\r\n```\r\n\r\n### Built-in ABAP Types for CDS Tables\r\n\r\n| Type | Description | Example |\r\n|------|-------------|---------|\r\n| `abap.clnt` | Client (always first key field for client-dependent tables) | `key client : abap.clnt not null;` |\r\n| `abap.char(n)` | Character string of length n | `abap.char(40)` |\r\n| `abap.numc(n)` | Numeric character string of length n | `abap.numc(10)` |\r\n| `abap.int4` | 4-byte integer | `abap.int4` |\r\n| `abap.int8` | 8-byte integer | `abap.int8` |\r\n| `abap.dats` | Date (YYYYMMDD, 8 chars) | `abap.dats` |\r\n| `abap.tims` | Time (HHMMSS, 6 chars) | `abap.tims` |\r\n| `abap.curr(n,d)` | Currency amount — must pair with `abap.cuky` reference field | `abap.curr(15,2)` |\r\n| `abap.cuky` | Currency key (reference field for CURR amounts) | `abap.cuky` |\r\n| `abap.dec(n,d)` | Packed decimal with n digits and d decimal places | `abap.dec(9,2)` |\r\n| `abap.quan(n,d)` | Quantity — must pair with `abap.unit` reference field | `abap.quan(13,3)` |\r\n| `abap.unit` | Unit of measure (reference field for QUAN quantities) | `abap.unit` |\r\n| `abap.lang` | Language key | `abap.lang` |\r\n| `abap.string` | Variable-length string (not allowed as key field) | `abap.string` |\r\n| `abap.rawstring` | Variable-length raw bytes | `abap.rawstring` |\r\n\r\n**CURR/QUAN pairing rule:** Any `abap.curr` field must be preceded by a `@Semantics.amount.currencyCode: 'fieldname'` annotation pointing to the `abap.cuky` field. Any `abap.quan` field must be preceded by `@Semantics.quantity.unitOfMeasure: 'fieldname'` pointing to the `abap.unit` field.\r\n\r\n### Referencing Standard Data Elements Directly\r\n\r\nA CDS `define table` field may be typed with a **standard SAP data element name** instead of an `abap.*` built-in. Write the data element name in lowercase as the field type — no `abap.` prefix:\r\n\r\n```sql\r\ndefine table zpm_dt_log {\r\n key client : abap.clnt not null;\r\n key downtime_id : abap.numc(12) not null;\r\n equipment : equnr; -- standard DE: Equipment number\r\n plant : werks_d; -- standard DE: Plant\r\n reason_code : abap.char(4); -- see fallback note below\r\n start_ts : timestampl; -- standard DE: UTC timestamp (long)\r\n ...\r\n}\r\n```\r\n\r\nThis is the **preferred** way to type a field whose meaning matches an existing SAP concept (equipment `EQUNR`, plant `WERKS_D`, material `MATNR`, company code `BUKRS`, user `XUBNAME`, dates/times, etc.): you inherit the standard DE's labels, conversion exits, and — crucially — its **check table / value help** for free, without creating any custom DDIC.\r\n\r\n### Fallback Rule — Custom Domain/Data Element Create FAILS (the exception)\r\n\r\nCustom DOMA/DTEL creation is the supported path now (`sap_create_domain` / `sap_create_data_element`). This fallback applies **only as the exception** — when a custom create genuinely FAILS on the target system (some systems strip the ADT DDIC write path). It is NOT the default; do not skip the create and jump straight here.\r\n\r\nWhen a `sap_create_domain` or `sap_create_data_element` call fails, apply this fallback **in order**:\r\n\r\n1. **Is there a standard DE that fits?** Use it directly (section above). E.g. equipment → `equnr`, not a new `ZDE_EQUIPMENT`.\r\n2. **No standard DE fits?** Type the field with the matching **built-in** (`abap.char(4)`, `abap.numc(10)`, …) sized to the intended domain. The field still works; it just lacks the custom domain's fixed-value list at the DDIC layer.\r\n3. **Where did the fixed values / value help go?** For a code field that would have drawn its values from a custom domain, get the value help from a **check-table foreign key** instead: the code column references a check table (its own Z check table, or a standard one), and the FK supplies the F4 help. Record this as the design intent even though the FK relationship itself is declared at the CDS/consumption layer, not in the raw `define table`.\r\n4. **Record the simplification — always.** Whatever you substitute, state it explicitly in the output (and, when invoked by `/abap-plan`, in `work.notes`): *\"Intended custom domain `ZPM_DT_REASON_CODE` (CHAR 4, fixed values OPER/MECH/ELEC/EXT) → `sap_create_domain` FAILED on this system → substituted built-in `abap.char(4)`; value help to come from check-table FK.\"* A reviewer must be able to see exactly what was traded and why.\r\n\r\nThis fallback is correct and supported when it triggers — it is not a workaround to apologise for. The data model is complete; only the optional DDIC-layer value list is deferred to a check table.\r\n\r\n### Table Maintenance (SM30/SE54) — Not Available via ADT\r\n\r\nThe SE54 **table-maintenance generator** — which builds the generated SM30 maintenance dialog (screens, function group, events) — is a **SAP GUI-only** transaction. There is no ADT REST endpoint for it. So in MCP mode you can create and activate a configuration table, but you CANNOT generate its SM30 dialog. Same shape as the custom DOMA/DTEL gap above; apply the same fallback-and-record discipline.\r\n\r\nFor a configuration/checktable that the design wants SM30-maintainable:\r\n\r\n1. **Make the table editable without a generated dialog.** Create it with `@AbapCatalog.dataMaintenance: #ALLOWED` (and delivery class `#C`). With `#ALLOWED` the table is directly maintainable in **SE16N** — sufficient for low-volume config (reason codes, type lists) from day one, no generated dialog required.\r\n2. **Seed initial values via an initial-load report** (an `abap-generate` report doing `MODIFY <table> FROM TABLE`) when the design needs starter rows (e.g. a seeded `OTHER` reason code). This path is fully ADT-drivable.\r\n3. **Flag SM30/SE54 generation as a manual GUI step** — record in the output (and `work.notes` when called from `/abap-plan`) the exact manual recipe: *\"SM30 dialog NOT generated (SE54 is GUI-only, not in ADT). Table is `#ALLOWED` → SE16N-maintainable now. To add the generated SM30 dialog: SE11 → table → Utilities → Table Maintenance Generator (SE54), authorization group + function group + maintenance type, then transport.\"* Do not claim \"SM30 view generated\" when it was not.\r\n4. **Value help is unaffected** — F4 on a code field comes from the check-table foreign key, not from SM30.\r\n\r\nNever report a maintenance dialog as generated/tested when the generation can only happen in the GUI. Like the DOMA fallback, this is a clean, traceable deferral — the table and its data are complete via ADT; only the optional generated dialog is a manual step.\r\n\r\n### Administrative Timestamp Fields (RAP-managed)\r\n\r\nFor RAP-enabled tables, use these released types instead of raw DATS/TIMS for the admin fields:\r\n\r\n```\r\ncreated_by : abp_creation_user;\r\ncreated_at : abp_creation_tstmpl;\r\nlocal_last_changed_by : abp_locinst_lastchange_user;\r\nlocal_last_changed_at : abp_locinst_lastchange_tstmpl;\r\nlast_changed_at : abp_lastchange_tstmpl;\r\n```\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure — Paste Mode\r\n\r\n```\r\n## Data Model Design — [FEATURE NAME]\r\n\r\n**Mode:** Paste (design document only — create objects manually in SE11)\r\n**Objects:** [N domains, N data elements, N tables/structures]\r\n\r\n---\r\n\r\n### Domains\r\n\r\n#### [DOMAIN NAME]\r\n- **Data Type:** [CHAR / NUMC / DEC / DATS / TIMS / etc.]\r\n- **Length:** [N]\r\n- **Description:** [What this domain represents]\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| [value] | [description] |\r\n\r\n---\r\n\r\n### Data Elements\r\n\r\n#### [DATA ELEMENT NAME]\r\n- **Domain / Type:** [Domain name, or built-in type if no domain]\r\n- **Description:** [What this field means]\r\n- **Labels:**\r\n - Short (10): [text]\r\n - Medium (20): [text]\r\n - Long (40): [text]\r\n - Heading (55): [text]\r\n- **Search Help:** [SE field name if applicable, or NONE]\r\n\r\n---\r\n\r\n### Tables\r\n\r\n#### [TABLE NAME]\r\n- **Description:** [What this table stores]\r\n- **Delivery Class:** [A = application, C = customizing, E = system, L = temporary, etc.]\r\n- **Table Category:** [Transparent]\r\n- **Buffering:** [Not buffered / Fully buffered / Single record buffered]\r\n\r\n**Fields:**\r\n\r\n| Field | Key | Data Element | Type | Length | Null | Description |\r\n|-------|-----|-------------|------|--------|------|-------------|\r\n| MANDT | ✓ | MANDT | CLNT | 3 | No | Client |\r\n| [FIELD] | [✓/—] | [DE name] | [type] | [len] | [No/Yes] | [description] |\r\n\r\n**Technical Notes:**\r\n- [Any indexing recommendations]\r\n- [Foreign key relationships]\r\n- [Archiving object if known]\r\n\r\n---\r\n\r\n### Structures\r\n\r\n#### [STRUCTURE NAME]\r\n- **Description:** [What this structure represents]\r\n- **Use:** [Transfer structure for FM / result structure for CDS / etc.]\r\n\r\n**Fields:**\r\n\r\n| Field | Data Element | Type | Length | Description |\r\n|-------|-------------|------|--------|-------------|\r\n| [FIELD] | [DE name] | [type] | [len] | [description] |\r\n\r\n---\r\n\r\n### Relationships\r\n\r\n| From Table | Field | To Table/View | Field | Type |\r\n|-----------|-------|--------------|-------|------|\r\n| [table] | [field] | [table] | [field] | Foreign Key / Association / Documented reference |\r\n\r\n---\r\n\r\n### Creation Sequence\r\n1. [Object name] (Domain) — create first, no dependencies\r\n2. [Object name] (Domain) — create first, no dependencies\r\n3. [Object name] (Data Element) — after domains\r\n4. [Object name] (Data Element) — after domains\r\n5. [Object name] (DB Table) — after data elements\r\n```\r\n\r\n## Output Structure — MCP Mode\r\n\r\nThe design section above is produced first, then:\r\n\r\n```\r\n## Execution Plan — MCP Mode\r\n\r\n**Transport:** [transport number] (session transport)\r\n**Package:** [package name]\r\n**Type strategy:** Hybrid (recorded in work.notes) — custom DOMA/DTEL for new semantics, standard/built-in for plumbing\r\n**Method:** sap_create_domain / sap_create_data_element for new semantic types, CDS define table syntax for tables\r\n\r\n### Steps\r\n\r\n1. Create domain ZDOM_DT_REASON → sap_create_domain (CHAR 4, fixedValues OPER/MECH/ELEC/EXT) → Transport [TR] → verify via sap_inactive_objects\r\n2. Create data element ZDE_DT_REASON → sap_create_data_element (domainName ZDOM_DT_REASON, labels) → Transport [TR] → verify\r\n3. Create table ZAPPR_REQUEST shell → sap_create_object (TABL/DT) → Package [X] → Transport [TR]\r\n4. Write ZAPPR_REQUEST source → sap_set_source (objectType: TABL) with CDS define table source (reason_code : zde_dt_reason; plant : werks_d)\r\n5. Activate ZAPPR_REQUEST → sap_activate → verify via sap_inactive_objects\r\n\r\nOrder: domains → data elements → tables. Each table: create shell → write CDS source → activate → verify. Stop on any failure.\r\n\r\nPlan complete. This will create 1 domain, 1 data element, 1 database table. Proceed? [y/n]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Never invent SAP built-in types.** In paste mode, use classic DDIC types: CHAR, NUMC, DEC, CURR, QUAN, DATS, TIMS, INT4, INT8, CLNT, LANG, UNIT, CUKY, ACCP, RAWSTRING, STRING, RAW, FLTP, DECFLOAT16, DECFLOAT34. In MCP mode, type each field with ONE of: a valid CDS built-in (abap.char(n), abap.numc(n), abap.int4, abap.int8, abap.dats, abap.tims, abap.curr(n,d), abap.cuky, abap.dec(n,d), abap.quan(n,d), abap.unit, abap.clnt, abap.lang, abap.string, abap.rawstring); a real **standard** SAP data element name referenced directly in lowercase (equnr, werks_d, matnr, bukrs, timestampl, …) — only reference DEs you know exist in the target release, never invent one; or a **custom Z-data-element you created in this run** via `sap_create_data_element`.\r\n- **Ask the type strategy ONCE, not per field.** Ask the Type Strategy question once per data-model phase, record the answer in `work.notes`, and route every field by the recorded strategy (see \"Type Strategy — Routing Rule\"). Do NOT re-ask on resume — re-state the recorded strategy. Under `custom`/`hybrid`, create custom domains + data elements for new business-semantic types via `sap_create_domain` / `sap_create_data_element` (domain → data element → table, on the session transport, each with an approval_id). Only fall back to standard/built-in types when a custom create genuinely FAILS — and **record the substitution** in the output (and `work.notes` when called from `/abap-plan`) per the Fallback Rule.\r\n- **Never reference SAP-delivered tables without confirming they exist in the target release.** If a table is only available in S/4HANA (e.g., ACDOCA), mark it `[S/4HANA only — confirm release]`.\r\n- **Label all assumptions about field lengths and types.** Business requirements rarely specify technical data types — every type decision is an assumption unless explicitly stated.\r\n- **MANDT as first key field** for all client-dependent transparent tables. Never omit it. Flag if the user's design omits it.\r\n- **For MCP mode: follow Rules 7–10 without exception.** No object creation without transport. No proceeding after a syntax check failure. No skipping the activation verification.\r\n- **Do not design modifications to SAP-delivered tables.** Use append structures or customer include CI_ structures. If the user asks to add a field directly to a standard table (e.g., VBAK), flag this as a modification that violates clean core and propose the append structure alternative.\r\n- **Follow the 10 Forge Rules.**\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-data-model mode: paste\r\n\r\nDesign the DDIC objects for a customer credit approval rules table. Business requirements:\r\n\r\n- Customers can have custom credit approval thresholds per company code\r\n- Three approval levels: automatic (no approval needed), single approver, dual approver\r\n- Thresholds are amount-based in customer's credit currency\r\n- Records can be active or inactive (soft delete)\r\n- Must track who created and last changed the record\r\n- Package: ZCREDIT_MGMT\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Data Model Design — Customer Credit Approval Rules\r\n\r\n**Mode:** Paste (design document only — create objects manually in SE11)\r\n**Objects:** 3 domains, 5 data elements, 1 table\r\n\r\n---\r\n\r\n### Domains\r\n\r\n#### ZDOM_CREDIT_APPR_LEVEL\r\n- **Data Type:** CHAR\r\n- **Length:** 1\r\n- **Description:** Credit approval level: automatic, single, or dual approver required\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| A | Automatic — no approval required |\r\n| S | Single approver required |\r\n| D | Dual approver required |\r\n\r\n[ASSUMED — 1-character code is assumed; confirm with business if longer codes are used]\r\n\r\n#### ZDOM_ACTIVE_INACTIVE\r\n- **Data Type:** CHAR\r\n- **Length:** 1\r\n- **Description:** Active/Inactive flag for soft-delete pattern\r\n- **Fixed Values:**\r\n\r\n| Value | Description |\r\n|-------|-------------|\r\n| X | Active |\r\n| | Inactive (space = inactive) |\r\n\r\n[ASSUMED — space-as-inactive is a common SAP pattern; confirm this convention is acceptable]\r\n\r\n#### ZDOM_CREDIT_AMOUNT\r\n- **Data Type:** CURR\r\n- **Length:** 15\r\n- **Decimal Places:** 2\r\n- **Description:** Credit threshold amount — must be paired with a currency key field (CUKY reference)\r\n- **Fixed Values:** None (amount domain, no fixed values)\r\n\r\n---\r\n\r\n### Data Elements\r\n\r\n#### ZDE_CREDIT_APPR_LEVEL\r\n- **Domain:** ZDOM_CREDIT_APPR_LEVEL\r\n- **Description:** Credit approval level required for a transaction\r\n- **Labels:**\r\n - Short (10): Appr.Level\r\n - Medium (20): Approval Level\r\n - Long (40): Credit Approval Level\r\n - Heading (55): Credit Approval Level Required\r\n- **Search Help:** NONE (fixed values come from domain)\r\n\r\n#### ZDE_CREDIT_APPR_THRESHOLD\r\n- **Domain:** ZDOM_CREDIT_AMOUNT\r\n- **Description:** Minimum order amount that triggers this approval level\r\n- **Labels:**\r\n - Short (10): Threshold\r\n - Medium (20): Approval Threshold\r\n - Long (40): Credit Approval Threshold Amount\r\n - Heading (55): Approval Threshold Amount (Credit Currency)\r\n- **Search Help:** NONE\r\n\r\n#### ZDE_CREDIT_RULE_ACTIVE\r\n- **Domain:** ZDOM_ACTIVE_INACTIVE\r\n- **Description:** Whether this approval rule is currently in effect\r\n- **Labels:**\r\n - Short (10): Active\r\n - Medium (20): Rule Active\r\n - Long (40): Approval Rule Active Indicator\r\n - Heading (55): Active\r\n- **Search Help:** NONE\r\n\r\n#### ZDE_CREDIT_RULE_ID\r\n- **Data Type:** NUMC (no domain — technical key)\r\n- **Length:** 10\r\n- **Description:** Internal numeric key for a credit approval rule record\r\n- **Labels:**\r\n - Short (10): Rule ID\r\n - Medium (20): Credit Rule ID\r\n - Long (40): Credit Approval Rule ID\r\n - Heading (55): Credit Approval Rule ID\r\n- **Search Help:** NONE\r\n\r\n[ASSUMED — numeric surrogate key assumed; confirm if business key (KUNNR + BUKRS + APPR_LEVEL) is preferred as sole key]\r\n\r\n#### ZDE_CREDIT_BUKRS\r\n- **Domain:** Reuse standard SAP data element BUKRS (Company Code)\r\n- **Note:** Do NOT create a new data element — reuse BUKRS directly. This provides automatic search help, label, and foreign key to T001.\r\n\r\n---\r\n\r\n### Tables\r\n\r\n#### ZCREDIT_APPR_RULES\r\n- **Description:** Customer credit approval thresholds per company code and approval level\r\n- **Delivery Class:** C (Customizing — maintained by business configuration, not application code)\r\n- **Table Category:** Transparent\r\n- **Buffering:** Fully buffered (customizing data, read frequently, changes infrequent)\r\n\r\n**Fields:**\r\n\r\n| Field | Key | Data Element | Type | Length | Null | Description |\r\n|-------|-----|-------------|------|--------|------|-------------|\r\n| MANDT | ✓ | MANDT | CLNT | 3 | No | Client |\r\n| RULE_ID | ✓ | ZDE_CREDIT_RULE_ID | NUMC | 10 | No | Credit rule internal ID |\r\n| BUKRS | — | BUKRS | CHAR | 4 | No | Company code |\r\n| KUNNR | — | KUNNR | CHAR | 10 | No | Customer number |\r\n| APPR_LEVEL | — | ZDE_CREDIT_APPR_LEVEL | CHAR | 1 | No | Approval level (A/S/D) |\r\n| THRESHOLD_AMT | — | ZDE_CREDIT_APPR_THRESHOLD | CURR | 15,2 | No | Threshold amount |\r\n| THRESHOLD_CURR | — | WAERS | CUKY | 5 | No | Currency key for threshold (reference field for THRESHOLD_AMT) |\r\n| VALID_FROM | — | DATS | DATS | 8 | No | Rule valid from date |\r\n| VALID_TO | — | DATS | DATS | 8 | No | Rule valid to date (9999/12/31 = no expiry) |\r\n| ACTIVE | — | ZDE_CREDIT_RULE_ACTIVE | CHAR | 1 | No | Active indicator |\r\n| CREATED_BY | — | ERNAM | CHAR | 12 | No | Created by (user ID) |\r\n| CREATED_AT | — | ERDAT | DATS | 8 | No | Created on date |\r\n| CREATED_TM | — | ERZET | TIMS | 6 | No | Created at time |\r\n| CHANGED_BY | — | AENAM | CHAR | 12 | No | Last changed by |\r\n| CHANGED_AT | — | AEDAT | DATS | 8 | No | Last changed date |\r\n\r\n**Technical Notes:**\r\n- THRESHOLD_AMT is type CURR and MUST reference THRESHOLD_CURR as its currency reference field. Set this in SE11 via the \"Currency/Quantity Fields\" tab.\r\n- Consider a secondary index on (MANDT, BUKRS, KUNNR, ACTIVE) to support the most common query pattern: \"all active rules for a customer in a company code.\"\r\n- VALID_FROM / VALID_TO enables time-dependent rules. Application code must filter WHERE VALID_FROM <= sy-datum AND VALID_TO >= sy-datum.\r\n- Delivery class C means this table appears in customizing transport requests (via SM30/view cluster) rather than in workbench requests. A maintenance view (SE54) should be created for business users to maintain rules — this is out of scope for this data model design but must be planned.\r\n- [ASSUMED] RULE_ID as numeric surrogate key. If business prefers a natural key of MANDT + BUKRS + KUNNR + APPR_LEVEL, the field RULE_ID is unnecessary and those four fields become the composite primary key. Confirm before creating.\r\n\r\n---\r\n\r\n### Structures\r\n\r\nNo standalone structures required for this data model. The table ZCREDIT_APPR_RULES can be used directly as a structure type where needed.\r\n\r\nIf a transfer structure is needed for a function module or class method, define it inline using `LIKE LINE OF` or create a structure with the relevant subset of fields. Do not create a structure that duplicates the full table definition.\r\n\r\n---\r\n\r\n### Relationships\r\n\r\n| From Table | Field | To Table | Field | Type |\r\n|-----------|-------|---------|-------|------|\r\n| ZCREDIT_APPR_RULES | MANDT+BUKRS | T001 | MANDT+BUKRS | Foreign Key — company code must exist |\r\n| ZCREDIT_APPR_RULES | MANDT+KUNNR | KNA1 | MANDT+KUNNR | Foreign Key — customer must exist |\r\n| ZCREDIT_APPR_RULES | THRESHOLD_CURR | TCURC | WAERS | Foreign Key — currency must be valid |\r\n\r\n---\r\n\r\n### Creation Sequence\r\n1. ZDOM_CREDIT_APPR_LEVEL (Domain) — no dependencies; create and activate first\r\n2. ZDOM_ACTIVE_INACTIVE (Domain) — no dependencies; create and activate\r\n3. ZDOM_CREDIT_AMOUNT (Domain) — no dependencies; create and activate\r\n4. ZDE_CREDIT_APPR_LEVEL (Data Element) — depends on domain 1\r\n5. ZDE_CREDIT_APPR_THRESHOLD (Data Element) — depends on domain 3\r\n6. ZDE_CREDIT_RULE_ACTIVE (Data Element) — depends on domain 2\r\n7. ZDE_CREDIT_RULE_ID (Data Element) — no domain dependency; create after other DEs for clarity\r\n8. ZCREDIT_APPR_RULES (DB Table) — depends on all data elements (steps 4–7); create last; activate and verify; create secondary index after table activation\r\n```\r\n",
51
+ "sha256": "364cc50f6478fca20205d20845fa540f0bf79c042b7b7c5a7ccb13890444d0db",
45
52
  "signature": "",
46
- "signedAt": "2026-09-17T20:19:04.347Z"
53
+ "signedAt": "2026-09-28T14:37:04.305Z"
47
54
  },
48
55
  "abap-design": {
49
56
  "name": "abap-design",
50
- "body": "---\nname: abap-design\ndescription: \"Design the solution architecture — object list, dependency sequence, pattern selection. Use after spec-gap, before coding.\"\nuser-invocable: true\nversion: \"1.2\"\n---\n\n# Skill: abap-design\n\n## Purpose\n\nProduce a concrete, implementable architecture design for an ABAP development task. Given a requirement (ideally after `/abap-spec-gap` has been run), produce the full object list, dependency sequence, pattern decisions, and design rationale. This skill designs — it does not generate code. Its output is the blueprint that `/abap-generate`, `/abap-rap`, and other BUILD skills will execute.\n\n## When to Use\n\n- After `/abap-spec-gap` has been run and blockers resolved (or documented)\n- When a new feature, report, interface, or enhancement needs a technical design\n- When reviewing or validating an existing design before implementation begins\n- When the team needs a design document to review before coding starts\n- Before estimating (the object list from this skill feeds `/abap-estimate`)\n\nWhen NOT to use: requirements still vague — run `/abap-spec-gap` first; multi-session project planning — `/abap-plan`; generating the code — `/abap-generate`.\n\n## Inputs Expected\n\nProvide ONE or MORE of the following:\n\n1. **Requirement description** — user story, functional spec, or informal description\n2. **Gap analysis output** — from `/abap-spec-gap`, with answers filled in\n3. **System context** — SAP release (ECC 6.0, S/4HANA 2023, BTP ABAP), module, existing objects to consider\n4. **Constraints** — clean core required, no new Z tables, must use existing RAP stack, etc.\n5. **Object name hints** — if the customer has naming conventions, state them\n- If the source requirement is a `.pdf`/`.docx`, read it with `read_document(path)` first.\n\nIf no system context is given, design for S/4HANA ABAP with clean core principles as the default.\n\n**System context is auto-provided when CSPeach is connected to a SAP system.** A `<session_context>` block at the top of the user message exposes a `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>` element. **Do not ask the user about any field already present there.** Treat that block as ground truth — the `Target System:` line in your output, the platform-conditional defaults in Pattern Decisions, and any clean-core recommendations must be derived from it.\n\n## Required Behavior\n\n0. **Read `<session_context>` FIRST.** Before drafting the design, scan the prompt for a `<session_context>` block. If `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>` is present, those facts are KNOWN — never ask about them, never list them as Open Design Questions. Use them to set the design defaults:\n - `platform=\"S/4HANA\"` → default architecture is **RAP managed BO + CDS interface/projection + Fiori Elements**. Released APIs / CDS over direct table access. Frame classic patterns as \"OR if you have a documented reason for direct VBAK access, say so.\"\n - `platform=\"ECC\"` → default is **classic ALV report + global class + FOR ALL ENTRIES on tables**. Don't propose RAP unless ECC ≥ 7.50. CDS only on ECC ≥ 7.40 SP08.\n - `abap_version=\"7.55\"+` → use modern syntax in any ABAP examples (FILTER, REDUCE, FOR..., inline DATA). Don't write TYPES BEGIN OF when DATA inline works.\n - `deployment=\"public-cloud\"` → no SE38, no classic GUI, no direct DDIC writes. Everything must be RAP / Fiori Elements / released APIs only.\n When `<sap_system>` is missing or `platform=\"unknown\"`, the existing default (\"S/4HANA + clean core\") still applies, and platform questions are fair game in Open Design Questions.\n\n0a. **Resolve BLOCKERS before drafting — but ask the developer HOW to resolve them first.** Before producing any design, scan the requirement for SHOWSTOPPER ambiguities — interpretations of key terms that would produce *different object lists* or *different pattern choices*. Examples:\n - Definition of \"open\" / \"overdue\" / \"active\" — multiple plausible date/status interpretations\n - Trigger event — manual button vs. automatic on save (BAdI) vs. scheduled job\n - Authorization scope — by user, by role, by org unit, by plant\n - Data scope — single sales org vs. company-wide; current period vs. historical\n\n **Critical UX rule:** many of these are FUNCTIONAL / BUSINESS questions, not developer questions. A developer often does not own \"what does *open* mean to this customer's sales process\" — that's a business analyst / product owner call. Forcing a developer to guess is wrong; locking them out of their own design tool when they don't have the answers is also wrong.\n\n When 1–3 such blockers exist, **first ask ONE meta-question via `ask_question`** with this framing:\n\n > \"I found N blocker question(s) before I can draft this design. Many are functional/business decisions, not technical defaults. How do you want to handle them?\n > (a) **Answer interactively** — I'll ask each one and produce a tight, tailored design when you're done.\n > (b) **Create a handoff file** — I'll write the questions to a shareable doc you can send to the functional analyst / business owner, then you re-run `/abap-design` once they've replied.\"\n\n **Branch on the answer:**\n\n - **(a) Interactive** — Use `ask_question` for each blocker, ONE AT A TIME, with named options + a free-text fallback. Wait for each answer. Then produce the full design tailored to the resolved interpretation.\n\n - **(b) Handoff file** — Produce a structured handoff document (NOT a design). Format:\n ```\n ## Design Handoff — [Requirement Title]\n\n **Requested by:** <developer name from session if available>\n **For:** Functional analyst / business owner\n **Why this exists:** I need these decisions before I can build the design. Please answer below and return.\n\n ### Question 1 — <topic>\n **Why it matters:** <1 line on what differs between options>\n **Options:**\n a) <option A> — <implication>\n b) <option B> — <implication>\n c) <option C> — <implication>\n **Your answer:**\n\n ### Question 2 — ...\n\n ### Once answered\n The developer should re-run `/abap-design` and paste this file's answers in the prompt, OR run `/abap-spec-gap` for a fuller question set.\n ```\n End the turn. **Do NOT produce a design** — that's the entire point of path (b). The harness's \"Save as project file?\" prompt then offers to persist this handoff doc.\n\n Cap inline blockers at 3 in path (a). If more than 3 truly-blocking ambiguities exist, the requirement is too vague for `/abap-design` regardless of path — recommend the user run `/abap-spec-gap` first.\n\n The \"Open Design Questions\" section in the design output is reserved for NICE-TO-KNOW refinements (rejected items? plant filter? tile vs. semantic object?) — questions that refine but don't change the architecture. Blockers go through this 0a flow; refinements go to that output section.\n\n1. **Parse the requirement and context.** Identify the business process, the users, the data involved, and the integration points. Note any constraints explicitly.\n\n2. **Select the appropriate architecture pattern.** For each major category, make a deliberate choice. **When `<sap_system>` is provided, default to the modern clean-core option for that platform — don't list it as a question.** Document the alternative considered and why it was rejected (or why it's offered as an opt-out).\n - **Data access layer**: CDS view (default on S/4HANA) / direct SELECT (default on ECC < 7.40 SP08) / RAP entity / existing released BAPI\n - **Business logic layer**: RAP behavior (default on S/4HANA for transactional flows) / global class / procedural report / function module\n - **UI layer**: Fiori Elements via RAP (default on S/4HANA) / classic ALV (default on ECC) / WebDynpro / RFC consumer / batch report\n - **Output/integration**: ALV native spreadsheet export (default — no `cl_gui_frontend_services`) / IDoc / RFC / REST API / file / email\n For each chosen layer, document the alternative considered and the reason — even if the alternative is \"the legacy path, which the user can opt into by request.\"\n\n3. **Produce the object list.** For every SAP object that must be created or extended, produce a row in the table: object name, type, purpose, and transport assignment.\n\n4. **Sequence the dependencies.** DDIC objects before CDS views before classes before reports. Activation dependencies must be respected. Produce a numbered sequence showing which objects must exist before others can be created.\n\n5. **Identify reuse opportunities.** What standard SAP objects (BAPIs, CDS views, function modules, authority objects) can be used as-is? What custom objects already exist in the system that should be extended rather than duplicated?\n\n6. **Flag open design questions.** Decisions that require business or technical input before implementation: field mapping uncertainties, authorization object selection, integration protocol choice. **Do not list as \"open\" anything `<session_context>` already answered** — platform, release, ABAP version, deployment model are facts, not questions.\n\n7. **Label all inferences.** If a naming convention is assumed from context rather than stated by the user, mark it `[ASSUMED]`. If an existing object is assumed to exist in the system but not confirmed, mark it `[UNCONFIRMED]`.\n\n8. **State what is out of scope.** Explicitly name things the design does not cover that a reader might expect to be there.\n\n9. **Forge Rules compliance.** This skill is read-only (Rule 6). Design documents reference but do not create SAP objects. No write tools are called. Rule 2 (no change without impact thinking) is fulfilled by the Dependency Sequence and Reuse Opportunities sections.\n\n10. **Offer revision before ending the turn.** After the complete design is rendered, before yielding control, use `ask_question` ONE more time with these options:\n - **Save as-is** — design is good as drafted\n - **Rename objects** — different prefix/suffix/convention; user types the rename rule in custom answer (e.g. \"use ZI_SO_* not ZI_SalesOrder_*\")\n - **Add or drop objects** — e.g. \"drop the metadata extension\", \"add a custom auth object ZAOBJ_...\"\n - **Change a Pattern Decision** — e.g. \"use Z table not CDS\", \"V2 binding not V4\", \"managed → unmanaged\", \"draft enabled\"\n - **Other change** — free-text describes the override\n\n Wait for the answer.\n\n If \"Save as-is\" — end the turn. The harness's file-save prompt persists the design.\n\n For ANY other answer — apply the change(s) the user described, then **re-render the COMPLETE updated design** (full document, not a diff — the user is saving the file, they need everything coherent). Update the object list, dependency sequence, pattern decisions, reuse list, and any cross-references touched by the change. Then re-ask the same Save/Revise question. Loop.\n\n Cap at 5 revision rounds. Beyond 5, the requirement is too unsettled for incremental edits — recommend the user run `/abap-spec-gap` and re-run `/abap-design` fresh.\n\n **Why this matters:** the developer is the one saving the file and owning the design output. They must be able to override naming conventions, swap pattern choices, drop suggestions they disagree with — without abandoning the whole turn and starting from scratch. One-shot output without a revision loop forces \"accept or restart\"; the revision loop offers \"accept, refine, or restart\" — the right granularity for a design artifact.\n\n11. **After save, offer the next-chain skill — design-adaptive.** Once \"Save as-is\" is selected in step 10 and the design persists, use `ask_question` ONE more time before ending the turn. **Read your own design's object list to pick the right recommended build skill:**\n\n - If the object list contains any of: **Behavior Definition (BDEF)**, **Behavior Implementation / Behavior Pool**, **Service Binding (SRVB)**, or **Service Definition (SRVD)** — the design is a RAP stack. Recommend **/abap-rap next** as the FIRST option.\n When the design's UI Layer Decision says an app is needed, add to the recommendation text: \"The plan will carry ui.build/ui.deploy phases for the [flavor] app — /abap-plan from this design covers backend AND frontend.\"\n - Otherwise — the design is a classic build (ALV report, function module, utility class, etc.). Recommend **/abap-generate next** as the FIRST option.\n - Always offer the OTHER build skill as the second option (override).\n - **/abap-estimate is NOT a default next step.** It's optional. List it AFTER both build options, labelled as \"effort estimate (optional)\".\n\n Prompt:\n\n > \"Design saved. Want to continue the chain in this session? The full context will carry over — no re-paste needed.\"\n\n Options for a **RAP design**:\n > - Run **/abap-rap** next — build the RAP stack (recommended for this design)\n > - Run **/abap-generate** next — build as classic ABAP (override the recommendation)\n > - Run **/abap-estimate** next — effort estimate (optional, can be done anytime)\n > - Run **/abap-impact** next — assess impact of the new objects on existing custom code\n > - End turn — I'll continue manually\n > - (Other — type the next skill name in the custom answer)\n\n Options for a **classic design**:\n > - Run **/abap-generate** next — build the objects (recommended for this design)\n > - Run **/abap-rap** next — re-design as a RAP stack (override — this design is classic)\n > - Run **/abap-estimate** next — effort estimate (optional, can be done anytime)\n > - Run **/abap-impact** next — assess impact of the new objects on existing custom code\n > - End turn — I'll continue manually\n > - (Other — type the next skill name in the custom answer)\n\n On any \"Run /abap-X next\" answer — print a one-line continuation hint as the final line before ending the turn:\n\n > `→ Run \\`/files\\` and pick the just-saved design, then choose /abap-X. Or type \\`/abap-X --from @\\` and tab-complete the filename.`\n\n Then end the turn. **Why not auto-dispatch via dispatch_skill?** This turn's design file does NOT exist yet — the harness's save hook fires AFTER step 11 ends. We don't know the saved filename to put in the dispatch command. The /files picker is the cleanest user-side bridge: one keypress to find the just-saved file, one keypress to pick the right BUILD skill (which is already shape-recommended by the picker post the design-shape work). Auto-dispatch from this step is achievable but requires a placeholder + post-save substitution mechanism — separate session.\n\n On \"End turn\" / \"(Other)\" — end the turn cleanly, no hint line. Standard prompt resumes.\n\n On \"End turn\" / \"(Other)\" — end the turn cleanly, no hint line, no dispatch_skill. Standard prompt resumes.\n\n **Why this matters:** estimate is an optional, secondary path — not the canonical next step. The canonical next step is BUILD. Whether that BUILD is /abap-rap or /abap-generate depends on what the design actually contains. Reading the design and recommending the right next skill prevents the common error of running /abap-generate on a RAP design (which then has to re-route to /abap-rap mid-flow). Auto-routing via `dispatch_skill` collapses the whole hand-off into one picker click — no retype, no Enter — because the picker pick IS the user's consent and re-asking for it is friction.\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\n```\n## ABAP Design — [FEATURE/TICKET TITLE]\n\n**Pattern:** [e.g., RAP Business Object + Fiori Elements / ALV Report + CDS Data Layer / RFC-enabled FM + IDoc]\n**Target System:** [e.g., S/4HANA 2023 / ECC 6.0 / BTP ABAP Environment]\n**Approach:** [1-sentence summary of the architectural decision]\n\n---\n\n### Objects to Create\n\n| # | Name | Type | Purpose | Transport |\n|---|------|------|---------|-----------|\n| 1 | ZDOM_APPROVAL_STATUS | Domain | Status values: OPEN, APPROVED, REJECTED, PENDING | [transport] |\n| 2 | ZDE_APPROVAL_STATUS | Data Element | Reusable type for approval status fields | [transport] |\n| 3 | ZAPPROVAL_HEADER | DB Table | Custom table: approval request header | [transport] |\n...\n\n---\n\n### Architecture Notes\n\n**Data Access Layer:**\n[Chosen approach and reason. E.g., \"CDS view ZI_ApprovalRequest on top of ZAPPROVAL_HEADER. Direct table access avoided to allow future extension without changing consumers.\"]\n\n**Business Logic Layer:**\n[Chosen approach and reason.]\n\n**UI Layer:**\n[Chosen approach and reason.]\n\n**Output / Integration:**\n[Chosen approach and reason.]\n\n---\n\n### UI Layer Decision\n\n| Decision | Value |\n|---|---|\n| App needed | [yes / no — if no, one line why] |\n| Flavor | [fiori-elements / freestyle] |\n| Floorplan (FE only) | [listReport / worklist / ovp / alp] |\n| Editability | [editable (draft) / display-only — REQUIRED whenever the app is transactional (create/update/delete). An **editable** answer seeds a draft-table backend phase (draft table + BDEF `with draft` + `draft determine action Prepare` + projection `use … with draft`), giving Create and an editable Object Page. **display-only** for read-only reports/analytics (alp, pure list output) — no draft. Never leave this blank and silently default to non-draft.] |\n| Reasoning | [1-2 sentences. Default: fiori-elements when the requirement maps to a standard floorplan; freestyle when it needs layouts/controls/flows annotations cannot express; alp when the requirement is analytical (KPIs, charts over aggregates, drill-down) — an alp decision seeds analytical backend phases (cube + query views, see abap-rap analytical-layer template).] |\n| App id / title | [reverse-domain id, human title — proposed, user confirms] |\n\n**Editability is a forced decision — ask it like DCL / package / transport.** When the app is transactional and the requirement does not state editable-vs-display, ask the developer via `ask_question` (\"Should users create/edit records (draft) or is this display-only?\") and RECORD the answer in the row above — do not silently default to non-draft. An **editable** answer must be reflected everywhere the design derives from it: add the draft table to the object list, add a `Draft | Enabled` row to Pattern Decisions, and (for an editable app) the recommendation in step 11 notes the plan seeds the draft-table backend phase alongside ui.build/ui.deploy. A display-only report has no draft and no draft table.\n\n---\n\n### Dependency Sequence\n\nObjects must be created and activated in this order:\n\n1. [Object name] ([type]) — foundation, no dependencies\n2. [Object name] ([type]) — depends on step 1\n3. [Object name] ([type]) — depends on steps 1–2\n...\n\n---\n\n### Pattern Decisions\n\n| Decision | Choice | Alternatives Considered | Reason |\n|----------|--------|------------------------|--------|\n| Authorization | New auth object ZAPPROVAL_AA | Reuse V_VBAK_AAT | Domain-specific data requires dedicated object |\n| Data storage | New Z table | Custom fields on VBAK | Approval workflow has own lifecycle, not order lifecycle |\n\n---\n\n### Reuse Opportunities\n\n| Object | Type | How Used |\n|--------|------|---------|\n| BAPI_SALESORDER_GETLIST | BAPI | Fetch sales order context for approval requests [UNCONFIRMED — must verify it exists in target system] |\n| I_BusinessUser | CDS View | Look up approver name from business user ID |\n| CL_ABAP_SENDMAIL | Class | Send approval notification emails |\n\n---\n\n### Open Design Questions\n\n1. [Design decision that needs input before implementation]\n2. [Field mapping question]\n3. [Integration protocol choice pending]\n\n---\n\n### Out of Scope\n- [Thing explicitly not included]\n- [Thing that might be expected but is not in this design]\n```\n\n## Guardrails\n\n- **Do not generate code.** This skill produces a design document, not source code. Use `/abap-generate` or `/abap-rap` to generate code from this design.\n- **Do not invent SAP objects.** If a standard SAP BAPI, CDS view, or FM is listed under Reuse, it must be a real, known SAP-delivered object. Mark anything unverified as `[UNCONFIRMED]`.\n- **Honor clean core.** By default, prefer BAdI/RAP extensions, CDS extensions, and official APIs over table modifications or implicit enhancements. If the system context requires a modification, flag it explicitly and explain why no extension point covers the need.\n- **Never restate known facts as questions.** If `<sap_system platform=\"S/4HANA\"/>` is in context, \"Target system?\" is not an Open Design Question — it's already answered. Padding the output with answered-by-context items is a quality regression.\n- **Quality over count.** Aim for a complete object list and a tight Pattern Decisions table. When `<sap_system>` is known, fewer Pattern Decisions (4–6 high-impact rows) beat a long table that re-litigates platform-level defaults.\n- **Name objects following CSPeach conventions.** Z prefix for all custom objects. ZCL_ for classes, ZIF_ for interfaces, ZI_ for interface CDS views, ZC_ for consumption CDS views, ZDE_ for data elements, ZDOM_ for domains. If the customer uses a different prefix, follow theirs.\n- **Do not skip the dependency sequence.** Activation failures from incorrect creation order are a common source of wasted time. The sequence must be explicit.\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind code generation — this design must be reviewed before BUILD skills are invoked).\n\n## Example Prompt\n\n*(In a live CSPeach session connected to SAP, `<session_context><sap_system .../></session_context>` would be auto-prepended; the prompt below is what the user typed.)*\n\n```\n/abap-design\n\nRequirement: Order Approval System\n\nSales orders above EUR 50,000 net value must go through a two-step approval process before they can be released for delivery:\n1. First approval by the sales manager (role: ZSM_SALES_MANAGER)\n2. Second approval by the finance controller (role: ZFI_CONTROLLER)\n\nThe approver should see a Fiori tile showing pending approvals. They can approve or reject with a mandatory comment. The sales order is blocked from delivery (delivery block ZLCK) until both approvals are given. Rejections trigger an email notification to the order creator.\n\nSystem: S/4HANA 2023 on-premise. Clean core preferred. Use existing sales order infrastructure (VBAK/VBAP). Custom namespace: Z.\n```\n\n## Example Output Outline\n\n```\n## ABAP Design — Order Approval System\n\n**Pattern:** RAP Business Object (managed) + Fiori Elements List Report + OData V4\n**Target System:** S/4HANA 2023 on-premise\n**Approach:** A dedicated RAP business object manages approval requests as a custom entity linked to sales orders; approval actions are RAP actions; delivery block is set/lifted via BAPI_SALESORDER_CHANGE as a side-effect.\n\n---\n\n### Objects to Create\n\n| # | Name | Type | Purpose | Transport |\n|---|------|------|---------|-----------|\n| 1 | ZDOM_APPROVAL_STATUS | Domain | Fixed values: PENDING, APPROVED, REJECTED | ZDEV_TR001 |\n| 2 | ZDOM_APPROVAL_STEP | Domain | Fixed values: STEP1_MANAGER, STEP2_CONTROLLER | ZDEV_TR001 |\n| 3 | ZDE_APPROVAL_STATUS | Data Element | Reusable type for approval status | ZDEV_TR001 |\n| 4 | ZDE_APPROVAL_STEP | Data Element | Reusable type for approval step | ZDEV_TR001 |\n| 5 | ZAPPR_REQUEST | DB Table | Approval request header: VBELN, step, status, requestor, created_on, approved_by, approved_on, comment | ZDEV_TR001 |\n| 6 | ZI_ApprovalRequest | CDS Interface View | Read access to ZAPPR_REQUEST with VBAK join for order value | ZDEV_TR001 |\n| 7 | ZC_ApprovalRequest | CDS Projection View | Consumption view with @UI annotations for Fiori Elements | ZDEV_TR001 |\n| 8 | ZBDEF_APPROVAL | Behavior Definition | RAP behavior: managed, draft-disabled; actions: approve, reject | ZDEV_TR001 |\n| 9 | ZBDEF_APPROVAL_PROJ | Behavior Definition | Projection behavior definition | ZDEV_TR001 |\n| 10 | ZCL_APPROVAL_BEHAVIOR | Class (ABAP Behavior Pool) | Implements approve/reject actions, delivery block logic, email notification | ZDEV_TR001 |\n| 11 | ZIF_APPROVAL_BEHAVIOR | Interface | Behavior pool interface for testability | ZDEV_TR001 |\n| 12 | ZCL_APPROVAL_MAILER | Class | Sends rejection email via CL_ABAP_SENDMAIL | ZDEV_TR001 |\n| 13 | ZIF_MAILER | Interface | Mailer interface for unit test double | ZDEV_TR001 |\n| 14 | ZAOBJ_APPROVAL_AA | Auth Object | Custom auth object: VBELN (order) + ACTVT (approve=Z1, reject=Z2, display=03) | ZDEV_TR001 |\n| 15 | ZSRV_APPROVAL | Service Definition | OData V4 service definition | ZDEV_TR001 |\n| 16 | ZBND_APPROVAL | Service Binding | OData V4 UI service binding for Fiori Elements | ZDEV_TR001 |\n| 17 | ZCL_APPROVAL_TEST | Test Class | ABAP Unit tests for behavior pool | ZDEV_TR001 |\n\n---\n\n### Architecture Notes\n\n**Data Access Layer:**\nCDS views ZI_ApprovalRequest and ZC_ApprovalRequest sit on top of custom table ZAPPR_REQUEST with a LEFT OUTER JOIN to VBAK for order net value and KUNNR. This avoids direct table access in the behavior pool and allows the UI to consume a single structured entity. The CDS views are annotated for OData exposure.\n\n**Business Logic Layer:**\nRAP managed scenario. The behavior definition declares two actions — `approve` and `reject` — both requiring a mandatory `comment` import parameter. The behavior pool class ZCL_APPROVAL_BEHAVIOR implements: validation that the action is performed by a user in the correct role for the current step, state transition logic (PENDING → APPROVED → second PENDING → final APPROVED, or any step → REJECTED), and a side-effect call to BAPI_SALESORDER_CHANGE to set or remove delivery block ZLCK. After a rejection, ZCL_APPROVAL_MAILER is called to send notification email.\n\n**UI Layer:**\nFiori Elements List Report (OData V4). ZC_ApprovalRequest carries @UI.lineItem and @UI.identification annotations so that no hand-coded UI is needed. The approve and reject actions are exposed as Fiori action buttons. The approver list is filtered by user's pending step using a @Consumption.filter annotation on the status and step fields.\n\n- UI Layer Decision: fiori-elements, listReport, display-only (no draft — records are read + acted on via approve/reject actions, never created/edited in the UI; see the `Draft | Disabled` Pattern Decision) — standard list+object page over ZC_MaintReq; app id zmaintreq.app\n\n**Output / Integration:**\nRejection notification via CL_ABAP_SENDMAIL (available in S/4HANA 2023). Email recipient is ERNAM from VBAK (order creator). Subject and body assembled via string template. No IDoc or RFC required for email. Sales order delivery block set via BAPI_SALESORDER_CHANGE — standard BAPI, no modification of VBAK directly.\n\n---\n\n### Dependency Sequence\n\nObjects must be created and activated in this order:\n\n1. ZDOM_APPROVAL_STATUS (Domain) — no dependencies\n2. ZDOM_APPROVAL_STEP (Domain) — no dependencies\n3. ZDE_APPROVAL_STATUS (Data Element) — depends on domain 1\n4. ZDE_APPROVAL_STEP (Data Element) — depends on domain 2\n5. ZAPPR_REQUEST (DB Table) — depends on data elements 3 and 4\n6. ZAOBJ_APPROVAL_AA (Authorization Object) — no ABAP dependencies; must exist before auth check in behavior pool\n7. ZIF_MAILER (Interface) — no dependencies\n8. ZIF_APPROVAL_BEHAVIOR (Interface) — no dependencies\n9. ZI_ApprovalRequest (CDS Interface View) — depends on ZAPPR_REQUEST (step 5)\n10. ZC_ApprovalRequest (CDS Projection View) — depends on ZI_ApprovalRequest (step 9)\n11. ZBDEF_APPROVAL (Behavior Definition) — depends on ZI_ApprovalRequest (step 9)\n12. ZBDEF_APPROVAL_PROJ (Behavior Definition Projection) — depends on ZC_ApprovalRequest (step 10) and ZBDEF_APPROVAL (step 11)\n13. ZCL_APPROVAL_MAILER (Class) — depends on ZIF_MAILER (step 7)\n14. ZCL_APPROVAL_BEHAVIOR (ABAP Behavior Pool) — depends on ZBDEF_APPROVAL (step 11), ZIF_APPROVAL_BEHAVIOR (step 8), ZCL_APPROVAL_MAILER (step 13)\n15. ZCL_APPROVAL_TEST (Test Class) — depends on ZCL_APPROVAL_BEHAVIOR (step 14)\n16. ZSRV_APPROVAL (Service Definition) — depends on ZC_ApprovalRequest (step 10)\n17. ZBND_APPROVAL (Service Binding) — depends on ZSRV_APPROVAL (step 16)\n\n---\n\n### Pattern Decisions\n\n| Decision | Choice | Alternatives Considered | Reason |\n|----------|--------|------------------------|--------|\n| Business object storage | New custom table ZAPPR_REQUEST | Append structure on VBAK | Approval lifecycle is separate from order lifecycle; storing in VBAK couples two domains and risks transport contamination |\n| Behavior management | RAP managed (framework handles CRUD) | RAP unmanaged | No custom persistence logic needed; framework handles locking and buffering cleanly |\n| Draft | Disabled (no draft) | Draft-enabled | Approval is a committed action, not a staged one — no draft needed; simpler implementation |\n| Delivery block mechanism | BAPI_SALESORDER_CHANGE | Direct UPDATE on VBAK-LIFSK | Direct table update bypasses SAP update logic and is disallowed by clean core; BAPI is the safe API |\n| Email notification | CL_ABAP_SENDMAIL | SCOT / workflow email | CL_ABAP_SENDMAIL is available in S/4HANA 2023 and does not require workflow configuration |\n| Authorization | New auth object ZAOBJ_APPROVAL_AA | Reuse V_VBAK_AAT | Approval actions (approve/reject) are not standard SD activities; dedicated object gives precise control |\n| First approval trigger | On-demand: approver opens Fiori tile | Automatic: BAdI on order save | On-demand avoids build complexity in the first version; BAdI trigger can be added in a follow-up |\n\n---\n\n### Reuse Opportunities\n\n| Object | Type | How Used |\n|--------|------|---------|\n| BAPI_SALESORDER_CHANGE | BAPI | Set/remove delivery block ZLCK on the sales order. Caller must handle RETURN table for errors. [UNCONFIRMED — verify that BAPI supports delivery block field in this S/4HANA release] |\n| I_SalesOrder | CDS View (SAP) | Join to ZI_ApprovalRequest to expose VBELN, NETWR, KUNNR without direct VBAK access |\n| CL_ABAP_SENDMAIL | Class (SAP) | Send rejection notification email. Available in S/4HANA 2022+. |\n| I_BusinessUser | CDS View (SAP) | Resolve approver user ID to full name for display in Fiori |\n\n---\n\n### Open Design Questions\n\n1. **Approval trigger**: Should the approval request be created automatically when a sales order is saved above the EUR 50,000 threshold (requires a BAdI on VBAK save), or manually by the sales rep clicking a button? The design above assumes manual trigger for simplicity — confirm with business.\n\n2. **Re-approval on order change**: If the sales order value is changed after approval is given (e.g., item added), should the approval restart from step 1? This is not covered in the current design and would require additional validation logic.\n\n3. **Approval by role vs. by specific user**: The spec says \"role ZSM_SALES_MANAGER\" — does any user with that role in any org unit qualify, or must the approver be the manager of the specific sales organization on the order? Row-level approval hierarchy requires additional data (org structure lookup) and significantly increases complexity.\n\n4. **Concurrent approval race condition**: If two users with the manager role both see the same pending approval, could both approve simultaneously? The RAP locking mechanism handles single-object locking, but confirm that the two-step logic in the behavior pool is tested for concurrent calls.\n\n---\n\n### Out of Scope\n- Automatic creation of approval requests via BAdI on sales order save (described in Open Design Questions — follow-up feature)\n- Approval delegation (out-of-office substitution)\n- Approval history beyond current status (audit log enhancement can be added later via a second table)\n- Mobile app / SAP Mobile Start integration (covered by Fiori Elements service binding but not explicitly tested)\n- Mass approval of multiple requests in one action\n```\n",
51
- "sha256": "c05e781ecb4bf5a4b8c92ea1e3a8ea121461435389e6be7cc6786f6c07d956a1",
57
+ "body": "---\r\nname: abap-design\r\ndescription: \"Design the solution architecture — object list, dependency sequence, pattern selection. Use after spec-gap, before coding.\"\r\nuser-invocable: true\r\nversion: \"1.2\"\r\n---\r\n\r\n# Skill: abap-design\r\n\r\n## Purpose\r\n\r\nProduce a concrete, implementable architecture design for an ABAP development task. Given a requirement (ideally after `/abap-spec-gap` has been run), produce the full object list, dependency sequence, pattern decisions, and design rationale. This skill designs — it does not generate code. Its output is the blueprint that `/abap-generate`, `/abap-rap`, and other BUILD skills will execute.\r\n\r\n## When to Use\r\n\r\n- After `/abap-spec-gap` has been run and blockers resolved (or documented)\r\n- When a new feature, report, interface, or enhancement needs a technical design\r\n- When reviewing or validating an existing design before implementation begins\r\n- When the team needs a design document to review before coding starts\r\n- Before estimating (the object list from this skill feeds `/abap-estimate`)\r\n\r\nWhen NOT to use: requirements still vague — run `/abap-spec-gap` first; multi-session project planning — `/abap-plan`; generating the code — `/abap-generate`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE or MORE of the following:\r\n\r\n1. **Requirement description** — user story, functional spec, or informal description\r\n2. **Gap analysis output** — from `/abap-spec-gap`, with answers filled in\r\n3. **System context** — SAP release (ECC 6.0, S/4HANA 2023, BTP ABAP), module, existing objects to consider\r\n4. **Constraints** — clean core required, no new Z tables, must use existing RAP stack, etc.\r\n5. **Object name hints** — if the customer has naming conventions, state them\r\n- If the source requirement is a `.pdf`/`.docx`, read it with `read_document(path)` first.\r\n\r\nIf no system context is given, design for S/4HANA ABAP with clean core principles as the default.\r\n\r\n**System context is auto-provided when CSPeach is connected to a SAP system.** A `<session_context>` block at the top of the user message exposes a `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>` element. **Do not ask the user about any field already present there.** Treat that block as ground truth — the `Target System:` line in your output, the platform-conditional defaults in Pattern Decisions, and any clean-core recommendations must be derived from it.\r\n\r\n## Required Behavior\r\n\r\n0. **Read `<session_context>` FIRST.** Before drafting the design, scan the prompt for a `<session_context>` block. If `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>` is present, those facts are KNOWN — never ask about them, never list them as Open Design Questions. Use them to set the design defaults:\r\n - `platform=\"S/4HANA\"` → default architecture is **RAP managed BO + CDS interface/projection + Fiori Elements**. Released APIs / CDS over direct table access. Frame classic patterns as \"OR if you have a documented reason for direct VBAK access, say so.\"\r\n - `platform=\"ECC\"` → default is **classic ALV report + global class + FOR ALL ENTRIES on tables**. Don't propose RAP unless ECC ≥ 7.50. CDS only on ECC ≥ 7.40 SP08.\r\n - `abap_version=\"7.55\"+` → use modern syntax in any ABAP examples (FILTER, REDUCE, FOR..., inline DATA). Don't write TYPES BEGIN OF when DATA inline works.\r\n - `deployment=\"public-cloud\"` → no SE38, no classic GUI, no direct DDIC writes. Everything must be RAP / Fiori Elements / released APIs only.\r\n When `<sap_system>` is missing or `platform=\"unknown\"`, the existing default (\"S/4HANA + clean core\") still applies, and platform questions are fair game in Open Design Questions.\r\n\r\n0a. **Resolve BLOCKERS before drafting — but ask the developer HOW to resolve them first.** Before producing any design, scan the requirement for SHOWSTOPPER ambiguities — interpretations of key terms that would produce *different object lists* or *different pattern choices*. Examples:\r\n - Definition of \"open\" / \"overdue\" / \"active\" — multiple plausible date/status interpretations\r\n - Trigger event — manual button vs. automatic on save (BAdI) vs. scheduled job\r\n - Authorization scope — by user, by role, by org unit, by plant\r\n - Data scope — single sales org vs. company-wide; current period vs. historical\r\n\r\n **Critical UX rule:** many of these are FUNCTIONAL / BUSINESS questions, not developer questions. A developer often does not own \"what does *open* mean to this customer's sales process\" — that's a business analyst / product owner call. Forcing a developer to guess is wrong; locking them out of their own design tool when they don't have the answers is also wrong.\r\n\r\n When 1–3 such blockers exist, **first ask ONE meta-question via `ask_question`** with this framing:\r\n\r\n > \"I found N blocker question(s) before I can draft this design. Many are functional/business decisions, not technical defaults. How do you want to handle them?\r\n > (a) **Answer interactively** — I'll ask each one and produce a tight, tailored design when you're done.\r\n > (b) **Create a handoff file** — I'll write the questions to a shareable doc you can send to the functional analyst / business owner, then you re-run `/abap-design` once they've replied.\"\r\n\r\n **Branch on the answer:**\r\n\r\n - **(a) Interactive** — Use `ask_question` for each blocker, ONE AT A TIME, with named options + a free-text fallback. Wait for each answer. Then produce the full design tailored to the resolved interpretation.\r\n\r\n - **(b) Handoff file** — Produce a structured handoff document (NOT a design). Format:\r\n ```\r\n ## Design Handoff — [Requirement Title]\r\n\r\n **Requested by:** <developer name from session if available>\r\n **For:** Functional analyst / business owner\r\n **Why this exists:** I need these decisions before I can build the design. Please answer below and return.\r\n\r\n ### Question 1 — <topic>\r\n **Why it matters:** <1 line on what differs between options>\r\n **Options:**\r\n a) <option A> — <implication>\r\n b) <option B> — <implication>\r\n c) <option C> — <implication>\r\n **Your answer:**\r\n\r\n ### Question 2 — ...\r\n\r\n ### Once answered\r\n The developer should re-run `/abap-design` and paste this file's answers in the prompt, OR run `/abap-spec-gap` for a fuller question set.\r\n ```\r\n End the turn. **Do NOT produce a design** — that's the entire point of path (b). The harness's \"Save as project file?\" prompt then offers to persist this handoff doc.\r\n\r\n Cap inline blockers at 3 in path (a). If more than 3 truly-blocking ambiguities exist, the requirement is too vague for `/abap-design` regardless of path — recommend the user run `/abap-spec-gap` first.\r\n\r\n The \"Open Design Questions\" section in the design output is reserved for NICE-TO-KNOW refinements (rejected items? plant filter? tile vs. semantic object?) — questions that refine but don't change the architecture. Blockers go through this 0a flow; refinements go to that output section.\r\n\r\n1. **Parse the requirement and context.** Identify the business process, the users, the data involved, and the integration points. Note any constraints explicitly.\r\n\r\n2. **Select the appropriate architecture pattern.** For each major category, make a deliberate choice. **When `<sap_system>` is provided, default to the modern clean-core option for that platform — don't list it as a question.** Document the alternative considered and why it was rejected (or why it's offered as an opt-out).\r\n - **Data access layer**: CDS view (default on S/4HANA) / direct SELECT (default on ECC < 7.40 SP08) / RAP entity / existing released BAPI\r\n - **Business logic layer**: RAP behavior (default on S/4HANA for transactional flows) / global class / procedural report / function module\r\n - **UI layer**: Fiori Elements via RAP (default on S/4HANA) / classic ALV (default on ECC) / WebDynpro / RFC consumer / batch report\r\n - **Output/integration**: ALV native spreadsheet export (default — no `cl_gui_frontend_services`) / IDoc / RFC / REST API / file / email\r\n For each chosen layer, document the alternative considered and the reason — even if the alternative is \"the legacy path, which the user can opt into by request.\"\r\n\r\n3. **Produce the object list.** For every SAP object that must be created or extended, produce a row in the table: object name, type, purpose, and transport assignment.\r\n\r\n4. **Sequence the dependencies.** DDIC objects before CDS views before classes before reports. Activation dependencies must be respected. Produce a numbered sequence showing which objects must exist before others can be created.\r\n\r\n5. **Identify reuse opportunities.** What standard SAP objects (BAPIs, CDS views, function modules, authority objects) can be used as-is? What custom objects already exist in the system that should be extended rather than duplicated?\r\n\r\n6. **Flag open design questions.** Decisions that require business or technical input before implementation: field mapping uncertainties, authorization object selection, integration protocol choice. **Do not list as \"open\" anything `<session_context>` already answered** — platform, release, ABAP version, deployment model are facts, not questions.\r\n\r\n7. **Label all inferences.** If a naming convention is assumed from context rather than stated by the user, mark it `[ASSUMED]`. If an existing object is assumed to exist in the system but not confirmed, mark it `[UNCONFIRMED]`.\r\n\r\n8. **State what is out of scope.** Explicitly name things the design does not cover that a reader might expect to be there.\r\n\r\n9. **Forge Rules compliance.** This skill is read-only (Rule 6). Design documents reference but do not create SAP objects. No write tools are called. Rule 2 (no change without impact thinking) is fulfilled by the Dependency Sequence and Reuse Opportunities sections.\r\n\r\n10. **Offer revision before ending the turn.** After the complete design is rendered, before yielding control, use `ask_question` ONE more time with these options:\r\n - **Save as-is** — design is good as drafted\r\n - **Rename objects** — different prefix/suffix/convention; user types the rename rule in custom answer (e.g. \"use ZI_SO_* not ZI_SalesOrder_*\")\r\n - **Add or drop objects** — e.g. \"drop the metadata extension\", \"add a custom auth object ZAOBJ_...\"\r\n - **Change a Pattern Decision** — e.g. \"use Z table not CDS\", \"V2 binding not V4\", \"managed → unmanaged\", \"draft enabled\"\r\n - **Other change** — free-text describes the override\r\n\r\n Wait for the answer.\r\n\r\n If \"Save as-is\" — end the turn. The harness's file-save prompt persists the design.\r\n\r\n For ANY other answer — apply the change(s) the user described, then **re-render the COMPLETE updated design** (full document, not a diff — the user is saving the file, they need everything coherent). Update the object list, dependency sequence, pattern decisions, reuse list, and any cross-references touched by the change. Then re-ask the same Save/Revise question. Loop.\r\n\r\n Cap at 5 revision rounds. Beyond 5, the requirement is too unsettled for incremental edits — recommend the user run `/abap-spec-gap` and re-run `/abap-design` fresh.\r\n\r\n **Why this matters:** the developer is the one saving the file and owning the design output. They must be able to override naming conventions, swap pattern choices, drop suggestions they disagree with — without abandoning the whole turn and starting from scratch. One-shot output without a revision loop forces \"accept or restart\"; the revision loop offers \"accept, refine, or restart\" — the right granularity for a design artifact.\r\n\r\n11. **After save, offer the next-chain skill — design-adaptive.** Once \"Save as-is\" is selected in step 10 and the design persists, use `ask_question` ONE more time before ending the turn. **Read your own design's object list to pick the right recommended build skill:**\r\n\r\n - If the object list contains any of: **Behavior Definition (BDEF)**, **Behavior Implementation / Behavior Pool**, **Service Binding (SRVB)**, or **Service Definition (SRVD)** — the design is a RAP stack. Recommend **/abap-rap next** as the FIRST option.\r\n When the design's UI Layer Decision says an app is needed, add to the recommendation text: \"The plan will carry ui.build/ui.deploy phases for the [flavor] app — /abap-plan from this design covers backend AND frontend.\"\r\n - Otherwise — the design is a classic build (ALV report, function module, utility class, etc.). Recommend **/abap-generate next** as the FIRST option.\r\n - Always offer the OTHER build skill as the second option (override).\r\n - **/abap-estimate is NOT a default next step.** It's optional. List it AFTER both build options, labelled as \"effort estimate (optional)\".\r\n\r\n Prompt:\r\n\r\n > \"Design saved. Want to continue the chain in this session? The full context will carry over — no re-paste needed.\"\r\n\r\n Options for a **RAP design**:\r\n > - Run **/abap-rap** next — build the RAP stack (recommended for this design)\r\n > - Run **/abap-generate** next — build as classic ABAP (override the recommendation)\r\n > - Run **/abap-estimate** next — effort estimate (optional, can be done anytime)\r\n > - Run **/abap-impact** next — assess impact of the new objects on existing custom code\r\n > - End turn — I'll continue manually\r\n > - (Other — type the next skill name in the custom answer)\r\n\r\n Options for a **classic design**:\r\n > - Run **/abap-generate** next — build the objects (recommended for this design)\r\n > - Run **/abap-rap** next — re-design as a RAP stack (override — this design is classic)\r\n > - Run **/abap-estimate** next — effort estimate (optional, can be done anytime)\r\n > - Run **/abap-impact** next — assess impact of the new objects on existing custom code\r\n > - End turn — I'll continue manually\r\n > - (Other — type the next skill name in the custom answer)\r\n\r\n On any \"Run /abap-X next\" answer — print a one-line continuation hint as the final line before ending the turn:\r\n\r\n > `→ Run \\`/files\\` and pick the just-saved design, then choose /abap-X. Or type \\`/abap-X --from @\\` and tab-complete the filename.`\r\n\r\n Then end the turn. **Why not auto-dispatch via dispatch_skill?** This turn's design file does NOT exist yet — the harness's save hook fires AFTER step 11 ends. We don't know the saved filename to put in the dispatch command. The /files picker is the cleanest user-side bridge: one keypress to find the just-saved file, one keypress to pick the right BUILD skill (which is already shape-recommended by the picker post the design-shape work). Auto-dispatch from this step is achievable but requires a placeholder + post-save substitution mechanism — separate session.\r\n\r\n On \"End turn\" / \"(Other)\" — end the turn cleanly, no hint line. Standard prompt resumes.\r\n\r\n On \"End turn\" / \"(Other)\" — end the turn cleanly, no hint line, no dispatch_skill. Standard prompt resumes.\r\n\r\n **Why this matters:** estimate is an optional, secondary path — not the canonical next step. The canonical next step is BUILD. Whether that BUILD is /abap-rap or /abap-generate depends on what the design actually contains. Reading the design and recommending the right next skill prevents the common error of running /abap-generate on a RAP design (which then has to re-route to /abap-rap mid-flow). Auto-routing via `dispatch_skill` collapses the whole hand-off into one picker click — no retype, no Enter — because the picker pick IS the user's consent and re-asking for it is friction.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## ABAP Design — [FEATURE/TICKET TITLE]\r\n\r\n**Pattern:** [e.g., RAP Business Object + Fiori Elements / ALV Report + CDS Data Layer / RFC-enabled FM + IDoc]\r\n**Target System:** [e.g., S/4HANA 2023 / ECC 6.0 / BTP ABAP Environment]\r\n**Approach:** [1-sentence summary of the architectural decision]\r\n\r\n---\r\n\r\n### Objects to Create\r\n\r\n| # | Name | Type | Purpose | Transport |\r\n|---|------|------|---------|-----------|\r\n| 1 | ZDOM_APPROVAL_STATUS | Domain | Status values: OPEN, APPROVED, REJECTED, PENDING | [transport] |\r\n| 2 | ZDE_APPROVAL_STATUS | Data Element | Reusable type for approval status fields | [transport] |\r\n| 3 | ZAPPROVAL_HEADER | DB Table | Custom table: approval request header | [transport] |\r\n...\r\n\r\n---\r\n\r\n### Architecture Notes\r\n\r\n**Data Access Layer:**\r\n[Chosen approach and reason. E.g., \"CDS view ZI_ApprovalRequest on top of ZAPPROVAL_HEADER. Direct table access avoided to allow future extension without changing consumers.\"]\r\n\r\n**Business Logic Layer:**\r\n[Chosen approach and reason.]\r\n\r\n**UI Layer:**\r\n[Chosen approach and reason.]\r\n\r\n**Output / Integration:**\r\n[Chosen approach and reason.]\r\n\r\n---\r\n\r\n### UI Layer Decision\r\n\r\n| Decision | Value |\r\n|---|---|\r\n| App needed | [yes / no — if no, one line why] |\r\n| Flavor | [fiori-elements / freestyle] |\r\n| Floorplan (FE only) | [listReport / worklist / ovp / alp] |\r\n| Editability | [editable (draft) / display-only — REQUIRED whenever the app is transactional (create/update/delete). An **editable** answer seeds a draft-table backend phase (draft table + BDEF `with draft` + `draft determine action Prepare` + projection `use … with draft`), giving Create and an editable Object Page. **display-only** for read-only reports/analytics (alp, pure list output) — no draft. Never leave this blank and silently default to non-draft.] |\r\n| Reasoning | [1-2 sentences. Default: fiori-elements when the requirement maps to a standard floorplan; freestyle when it needs layouts/controls/flows annotations cannot express; alp when the requirement is analytical (KPIs, charts over aggregates, drill-down) — an alp decision seeds analytical backend phases (cube + query views, see abap-rap analytical-layer template).] |\r\n| App id / title | [reverse-domain id, human title — proposed, user confirms] |\r\n\r\n**Editability is a forced decision — ask it like DCL / package / transport.** When the app is transactional and the requirement does not state editable-vs-display, ask the developer via `ask_question` (\"Should users create/edit records (draft) or is this display-only?\") and RECORD the answer in the row above — do not silently default to non-draft. An **editable** answer must be reflected everywhere the design derives from it: add the draft table to the object list, add a `Draft | Enabled` row to Pattern Decisions, and (for an editable app) the recommendation in step 11 notes the plan seeds the draft-table backend phase alongside ui.build/ui.deploy. A display-only report has no draft and no draft table.\r\n\r\n---\r\n\r\n### Dependency Sequence\r\n\r\nObjects must be created and activated in this order:\r\n\r\n1. [Object name] ([type]) — foundation, no dependencies\r\n2. [Object name] ([type]) — depends on step 1\r\n3. [Object name] ([type]) — depends on steps 1–2\r\n...\r\n\r\n---\r\n\r\n### Pattern Decisions\r\n\r\n| Decision | Choice | Alternatives Considered | Reason |\r\n|----------|--------|------------------------|--------|\r\n| Authorization | New auth object ZAPPROVAL_AA | Reuse V_VBAK_AAT | Domain-specific data requires dedicated object |\r\n| Data storage | New Z table | Custom fields on VBAK | Approval workflow has own lifecycle, not order lifecycle |\r\n\r\n---\r\n\r\n### Reuse Opportunities\r\n\r\n| Object | Type | How Used |\r\n|--------|------|---------|\r\n| BAPI_SALESORDER_GETLIST | BAPI | Fetch sales order context for approval requests [UNCONFIRMED — must verify it exists in target system] |\r\n| I_BusinessUser | CDS View | Look up approver name from business user ID |\r\n| CL_ABAP_SENDMAIL | Class | Send approval notification emails |\r\n\r\n---\r\n\r\n### Open Design Questions\r\n\r\n1. [Design decision that needs input before implementation]\r\n2. [Field mapping question]\r\n3. [Integration protocol choice pending]\r\n\r\n---\r\n\r\n### Out of Scope\r\n- [Thing explicitly not included]\r\n- [Thing that might be expected but is not in this design]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Do not generate code.** This skill produces a design document, not source code. Use `/abap-generate` or `/abap-rap` to generate code from this design.\r\n- **Do not invent SAP objects.** If a standard SAP BAPI, CDS view, or FM is listed under Reuse, it must be a real, known SAP-delivered object. Mark anything unverified as `[UNCONFIRMED]`.\r\n- **Honor clean core.** By default, prefer BAdI/RAP extensions, CDS extensions, and official APIs over table modifications or implicit enhancements. If the system context requires a modification, flag it explicitly and explain why no extension point covers the need.\r\n- **Never restate known facts as questions.** If `<sap_system platform=\"S/4HANA\"/>` is in context, \"Target system?\" is not an Open Design Question — it's already answered. Padding the output with answered-by-context items is a quality regression.\r\n- **Quality over count.** Aim for a complete object list and a tight Pattern Decisions table. When `<sap_system>` is known, fewer Pattern Decisions (4–6 high-impact rows) beat a long table that re-litigates platform-level defaults.\r\n- **Name objects following CSPeach conventions.** Z prefix for all custom objects. ZCL_ for classes, ZIF_ for interfaces, ZI_ for interface CDS views, ZC_ for consumption CDS views, ZDE_ for data elements, ZDOM_ for domains. If the customer uses a different prefix, follow theirs.\r\n- **Do not skip the dependency sequence.** Activation failures from incorrect creation order are a common source of wasted time. The sequence must be explicit.\r\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind code generation — this design must be reviewed before BUILD skills are invoked).\r\n\r\n## Example Prompt\r\n\r\n*(In a live CSPeach session connected to SAP, `<session_context><sap_system .../></session_context>` would be auto-prepended; the prompt below is what the user typed.)*\r\n\r\n```\r\n/abap-design\r\n\r\nRequirement: Order Approval System\r\n\r\nSales orders above EUR 50,000 net value must go through a two-step approval process before they can be released for delivery:\r\n1. First approval by the sales manager (role: ZSM_SALES_MANAGER)\r\n2. Second approval by the finance controller (role: ZFI_CONTROLLER)\r\n\r\nThe approver should see a Fiori tile showing pending approvals. They can approve or reject with a mandatory comment. The sales order is blocked from delivery (delivery block ZLCK) until both approvals are given. Rejections trigger an email notification to the order creator.\r\n\r\nSystem: S/4HANA 2023 on-premise. Clean core preferred. Use existing sales order infrastructure (VBAK/VBAP). Custom namespace: Z.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## ABAP Design — Order Approval System\r\n\r\n**Pattern:** RAP Business Object (managed) + Fiori Elements List Report + OData V4\r\n**Target System:** S/4HANA 2023 on-premise\r\n**Approach:** A dedicated RAP business object manages approval requests as a custom entity linked to sales orders; approval actions are RAP actions; delivery block is set/lifted via BAPI_SALESORDER_CHANGE as a side-effect.\r\n\r\n---\r\n\r\n### Objects to Create\r\n\r\n| # | Name | Type | Purpose | Transport |\r\n|---|------|------|---------|-----------|\r\n| 1 | ZDOM_APPROVAL_STATUS | Domain | Fixed values: PENDING, APPROVED, REJECTED | ZDEV_TR001 |\r\n| 2 | ZDOM_APPROVAL_STEP | Domain | Fixed values: STEP1_MANAGER, STEP2_CONTROLLER | ZDEV_TR001 |\r\n| 3 | ZDE_APPROVAL_STATUS | Data Element | Reusable type for approval status | ZDEV_TR001 |\r\n| 4 | ZDE_APPROVAL_STEP | Data Element | Reusable type for approval step | ZDEV_TR001 |\r\n| 5 | ZAPPR_REQUEST | DB Table | Approval request header: VBELN, step, status, requestor, created_on, approved_by, approved_on, comment | ZDEV_TR001 |\r\n| 6 | ZI_ApprovalRequest | CDS Interface View | Read access to ZAPPR_REQUEST with VBAK join for order value | ZDEV_TR001 |\r\n| 7 | ZC_ApprovalRequest | CDS Projection View | Consumption view with @UI annotations for Fiori Elements | ZDEV_TR001 |\r\n| 8 | ZBDEF_APPROVAL | Behavior Definition | RAP behavior: managed, draft-disabled; actions: approve, reject | ZDEV_TR001 |\r\n| 9 | ZBDEF_APPROVAL_PROJ | Behavior Definition | Projection behavior definition | ZDEV_TR001 |\r\n| 10 | ZCL_APPROVAL_BEHAVIOR | Class (ABAP Behavior Pool) | Implements approve/reject actions, delivery block logic, email notification | ZDEV_TR001 |\r\n| 11 | ZIF_APPROVAL_BEHAVIOR | Interface | Behavior pool interface for testability | ZDEV_TR001 |\r\n| 12 | ZCL_APPROVAL_MAILER | Class | Sends rejection email via CL_ABAP_SENDMAIL | ZDEV_TR001 |\r\n| 13 | ZIF_MAILER | Interface | Mailer interface for unit test double | ZDEV_TR001 |\r\n| 14 | ZAOBJ_APPROVAL_AA | Auth Object | Custom auth object: VBELN (order) + ACTVT (approve=Z1, reject=Z2, display=03) | ZDEV_TR001 |\r\n| 15 | ZSRV_APPROVAL | Service Definition | OData V4 service definition | ZDEV_TR001 |\r\n| 16 | ZBND_APPROVAL | Service Binding | OData V4 UI service binding for Fiori Elements | ZDEV_TR001 |\r\n| 17 | ZCL_APPROVAL_TEST | Test Class | ABAP Unit tests for behavior pool | ZDEV_TR001 |\r\n\r\n---\r\n\r\n### Architecture Notes\r\n\r\n**Data Access Layer:**\r\nCDS views ZI_ApprovalRequest and ZC_ApprovalRequest sit on top of custom table ZAPPR_REQUEST with a LEFT OUTER JOIN to VBAK for order net value and KUNNR. This avoids direct table access in the behavior pool and allows the UI to consume a single structured entity. The CDS views are annotated for OData exposure.\r\n\r\n**Business Logic Layer:**\r\nRAP managed scenario. The behavior definition declares two actions — `approve` and `reject` — both requiring a mandatory `comment` import parameter. The behavior pool class ZCL_APPROVAL_BEHAVIOR implements: validation that the action is performed by a user in the correct role for the current step, state transition logic (PENDING → APPROVED → second PENDING → final APPROVED, or any step → REJECTED), and a side-effect call to BAPI_SALESORDER_CHANGE to set or remove delivery block ZLCK. After a rejection, ZCL_APPROVAL_MAILER is called to send notification email.\r\n\r\n**UI Layer:**\r\nFiori Elements List Report (OData V4). ZC_ApprovalRequest carries @UI.lineItem and @UI.identification annotations so that no hand-coded UI is needed. The approve and reject actions are exposed as Fiori action buttons. The approver list is filtered by user's pending step using a @Consumption.filter annotation on the status and step fields.\r\n\r\n- UI Layer Decision: fiori-elements, listReport, display-only (no draft — records are read + acted on via approve/reject actions, never created/edited in the UI; see the `Draft | Disabled` Pattern Decision) — standard list+object page over ZC_MaintReq; app id zmaintreq.app\r\n\r\n**Output / Integration:**\r\nRejection notification via CL_ABAP_SENDMAIL (available in S/4HANA 2023). Email recipient is ERNAM from VBAK (order creator). Subject and body assembled via string template. No IDoc or RFC required for email. Sales order delivery block set via BAPI_SALESORDER_CHANGE — standard BAPI, no modification of VBAK directly.\r\n\r\n---\r\n\r\n### Dependency Sequence\r\n\r\nObjects must be created and activated in this order:\r\n\r\n1. ZDOM_APPROVAL_STATUS (Domain) — no dependencies\r\n2. ZDOM_APPROVAL_STEP (Domain) — no dependencies\r\n3. ZDE_APPROVAL_STATUS (Data Element) — depends on domain 1\r\n4. ZDE_APPROVAL_STEP (Data Element) — depends on domain 2\r\n5. ZAPPR_REQUEST (DB Table) — depends on data elements 3 and 4\r\n6. ZAOBJ_APPROVAL_AA (Authorization Object) — no ABAP dependencies; must exist before auth check in behavior pool\r\n7. ZIF_MAILER (Interface) — no dependencies\r\n8. ZIF_APPROVAL_BEHAVIOR (Interface) — no dependencies\r\n9. ZI_ApprovalRequest (CDS Interface View) — depends on ZAPPR_REQUEST (step 5)\r\n10. ZC_ApprovalRequest (CDS Projection View) — depends on ZI_ApprovalRequest (step 9)\r\n11. ZBDEF_APPROVAL (Behavior Definition) — depends on ZI_ApprovalRequest (step 9)\r\n12. ZBDEF_APPROVAL_PROJ (Behavior Definition Projection) — depends on ZC_ApprovalRequest (step 10) and ZBDEF_APPROVAL (step 11)\r\n13. ZCL_APPROVAL_MAILER (Class) — depends on ZIF_MAILER (step 7)\r\n14. ZCL_APPROVAL_BEHAVIOR (ABAP Behavior Pool) — depends on ZBDEF_APPROVAL (step 11), ZIF_APPROVAL_BEHAVIOR (step 8), ZCL_APPROVAL_MAILER (step 13)\r\n15. ZCL_APPROVAL_TEST (Test Class) — depends on ZCL_APPROVAL_BEHAVIOR (step 14)\r\n16. ZSRV_APPROVAL (Service Definition) — depends on ZC_ApprovalRequest (step 10)\r\n17. ZBND_APPROVAL (Service Binding) — depends on ZSRV_APPROVAL (step 16)\r\n\r\n---\r\n\r\n### Pattern Decisions\r\n\r\n| Decision | Choice | Alternatives Considered | Reason |\r\n|----------|--------|------------------------|--------|\r\n| Business object storage | New custom table ZAPPR_REQUEST | Append structure on VBAK | Approval lifecycle is separate from order lifecycle; storing in VBAK couples two domains and risks transport contamination |\r\n| Behavior management | RAP managed (framework handles CRUD) | RAP unmanaged | No custom persistence logic needed; framework handles locking and buffering cleanly |\r\n| Draft | Disabled (no draft) | Draft-enabled | Approval is a committed action, not a staged one — no draft needed; simpler implementation |\r\n| Delivery block mechanism | BAPI_SALESORDER_CHANGE | Direct UPDATE on VBAK-LIFSK | Direct table update bypasses SAP update logic and is disallowed by clean core; BAPI is the safe API |\r\n| Email notification | CL_ABAP_SENDMAIL | SCOT / workflow email | CL_ABAP_SENDMAIL is available in S/4HANA 2023 and does not require workflow configuration |\r\n| Authorization | New auth object ZAOBJ_APPROVAL_AA | Reuse V_VBAK_AAT | Approval actions (approve/reject) are not standard SD activities; dedicated object gives precise control |\r\n| First approval trigger | On-demand: approver opens Fiori tile | Automatic: BAdI on order save | On-demand avoids build complexity in the first version; BAdI trigger can be added in a follow-up |\r\n\r\n---\r\n\r\n### Reuse Opportunities\r\n\r\n| Object | Type | How Used |\r\n|--------|------|---------|\r\n| BAPI_SALESORDER_CHANGE | BAPI | Set/remove delivery block ZLCK on the sales order. Caller must handle RETURN table for errors. [UNCONFIRMED — verify that BAPI supports delivery block field in this S/4HANA release] |\r\n| I_SalesOrder | CDS View (SAP) | Join to ZI_ApprovalRequest to expose VBELN, NETWR, KUNNR without direct VBAK access |\r\n| CL_ABAP_SENDMAIL | Class (SAP) | Send rejection notification email. Available in S/4HANA 2022+. |\r\n| I_BusinessUser | CDS View (SAP) | Resolve approver user ID to full name for display in Fiori |\r\n\r\n---\r\n\r\n### Open Design Questions\r\n\r\n1. **Approval trigger**: Should the approval request be created automatically when a sales order is saved above the EUR 50,000 threshold (requires a BAdI on VBAK save), or manually by the sales rep clicking a button? The design above assumes manual trigger for simplicity — confirm with business.\r\n\r\n2. **Re-approval on order change**: If the sales order value is changed after approval is given (e.g., item added), should the approval restart from step 1? This is not covered in the current design and would require additional validation logic.\r\n\r\n3. **Approval by role vs. by specific user**: The spec says \"role ZSM_SALES_MANAGER\" — does any user with that role in any org unit qualify, or must the approver be the manager of the specific sales organization on the order? Row-level approval hierarchy requires additional data (org structure lookup) and significantly increases complexity.\r\n\r\n4. **Concurrent approval race condition**: If two users with the manager role both see the same pending approval, could both approve simultaneously? The RAP locking mechanism handles single-object locking, but confirm that the two-step logic in the behavior pool is tested for concurrent calls.\r\n\r\n---\r\n\r\n### Out of Scope\r\n- Automatic creation of approval requests via BAdI on sales order save (described in Open Design Questions — follow-up feature)\r\n- Approval delegation (out-of-office substitution)\r\n- Approval history beyond current status (audit log enhancement can be added later via a second table)\r\n- Mobile app / SAP Mobile Start integration (covered by Fiori Elements service binding but not explicitly tested)\r\n- Mass approval of multiple requests in one action\r\n```\r\n",
58
+ "sha256": "8a20802d77845cff28a0d77a4581546c21d463298bf4f17a11ada73e02416cc4",
52
59
  "signature": "",
53
- "signedAt": "2026-09-17T20:19:04.422Z"
60
+ "signedAt": "2026-09-28T14:37:04.336Z"
54
61
  },
55
62
  "abap-document": {
56
63
  "name": "abap-document",
57
64
  "body": "---\r\nname: abap-document\r\ndescription: >\r\n Generate developer documentation for any ABAP object. Reads source code,\r\n traces where-used references, analyzes structure, and produces a clear\r\n technical document covering purpose, logic flow, interfaces, dependencies,\r\n and data access. For onboarding, handovers, and audit readiness.\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-document\r\n\r\n## Purpose\r\n\r\nGenerate comprehensive developer documentation for an ABAP object without\r\nthe developer reading a single line of code. Reads the source, analyzes the\r\nstructure, traces dependencies, and produces a document that a new team\r\nmember can use to understand the object in minutes.\r\n\r\nUse cases:\r\n- **Developer onboarding** — new team member needs to understand 50 custom programs\r\n- **Project handover** — document what was built before the contractor leaves\r\n- **Audit / compliance** — \"show us documentation for your custom code\"\r\n- **Pre-change assessment** — understand an object before modifying it\r\n\r\n## When to Use\r\n\r\n- Use for a developer reference document on ONE ABAP object — purpose, logic flow, interfaces, dependencies, where-used.\r\n- NOT for a whole-package handover dossier — use `/abap-handover`; NOT for teaching code to a human in plain language — use `/abap-explain`.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_get_source` | Read the full source code |\r\n| `sap_object_structure` | Get object metadata (type, package, author, dates) |\r\n| `sap_usage_references` | Find what this object calls and what calls it |\r\n| `sap_search_object` | Find related objects (same package, naming pattern) |\r\n| `sap_sql_query` | Look up object metadata from TADIR, TFDIR, etc. |\r\n\r\n## Inputs\r\n\r\nRequired:\r\n- Object type (PROG, CLAS, FUGR, DDLS, etc.)\r\n- Object name\r\n\r\nOptional:\r\n- Depth: `summary` (1-2 pages) or `full` (comprehensive). Default: `summary`\r\n- Include where-used: yes/no. Default: yes\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Read the Object\r\n\r\n```\r\nsap_get_source (objectType, objectName)\r\nsap_object_structure (objectType, objectName) — if available\r\n```\r\n\r\nFor classes: also read the class definition to understand public interface.\r\nFor function groups: identify all function modules in the group.\r\n\r\n### Step 2 — Analyze the Code\r\n\r\nExtract from source:\r\n- **Purpose** — what does this program/class/FM do? (from comments, naming, logic)\r\n- **Inputs/Outputs** — parameters, selection screen, importing/exporting\r\n- **Business logic** — key decisions, calculations, validations\r\n- **Data access** — which tables are read/written, which CDS views used\r\n- **External calls** — function modules called, classes used, BAPIs, RFCs\r\n- **Error handling** — exceptions raised, messages used\r\n- **Authorization** — authority checks performed\r\n\r\n### Step 3 — Trace Dependencies\r\n\r\n```\r\nsap_usage_references (objectType, objectName)\r\n```\r\n\r\nBuild two lists:\r\n- **Calls out** — what does this object depend on?\r\n- **Called by** — what depends on this object?\r\n\r\n### Step 4 — Produce Documentation\r\n\r\nFormat the output as a structured document:\r\n\r\n```\r\n## {OBJECT_NAME} ({OBJECT_TYPE})\r\n\r\n### Overview\r\nPackage: {package} | Author: {author} | Last changed: {date}\r\n{1-3 sentence summary of what this object does}\r\n\r\n### Purpose\r\n{Detailed description of the business purpose. What problem does it solve?\r\nWhat process does it support?}\r\n\r\n### Interface\r\n{For programs: selection screen parameters}\r\n{For classes: public methods with signatures}\r\n{For FMs: importing/exporting/changing/tables parameters}\r\n\r\n### Logic Flow\r\n{Step-by-step description of what the code does, in business terms.\r\nNot a line-by-line translation — a developer-readable summary.}\r\n\r\n1. Read input data from {tables}\r\n2. Validate {conditions}\r\n3. Process {business logic}\r\n4. Write results to {output}\r\n\r\n### Data Access\r\n| Table/View | Operation | Purpose |\r\n|------------|-----------|---------|\r\n| {table} | READ | {why} |\r\n| {table} | WRITE | {why} |\r\n\r\n### Dependencies\r\nCalled by: {list of objects that reference this one}\r\nCalls: {list of objects this one references}\r\n\r\n### Error Handling\r\n{Exceptions, messages, return codes}\r\n\r\n### Notes\r\n{Anything unusual, workarounds, known issues, TODOs in comments}\r\n```\r\n\r\n## Guardrails\r\n\r\n- Read-only skill. Never write, modify, or activate anything.\r\n- If an object is very large (>1000 lines), summarize by method/section rather\r\n than trying to document every line.\r\n- For classes with many methods, document the public interface first. Private\r\n methods only if specifically requested.\r\n- If where-used returns many results (>50), show the top 10 most relevant and\r\n note the total count.\r\n- Do not invent business context. If the code doesn't make the purpose clear,\r\n say \"purpose not clear from code — confirm with business team.\"\r\n",
58
65
  "sha256": "6510b58f84528dffecc0cd1c1540fe1419f3604f48c430687807b6dc66b5e318",
59
66
  "signature": "",
60
- "signedAt": "2026-09-17T20:19:04.496Z"
67
+ "signedAt": "2026-09-28T14:37:04.370Z"
61
68
  },
62
69
  "abap-dump": {
63
70
  "name": "abap-dump",
64
- "body": "---\nname: abap-dump\ndescription: >\n Diagnose ABAP short dumps (runtime errors / ST22) and fix them. Default\n path: read the dump diagnostics via sap_short_dump (120 lines from a\n 6000-line dump), identify the crash point, read the source, and apply the\n fix with developer approval. Triage mode (on request): ranked root-cause\n hypotheses with evidence vs inference, read-only, no code changes. Replaces\n abap-dump-analyze and abap-dump-triage.\nphase: DEBUG\nrequires_mcp: optional\nforge_rules: [6, 7, 10]\nversion: \"1.0\"\nmin_cli_version: \"0.3.0\"\nwidget: stack-frames\n---\n\n# abap-dump\n\n## Purpose\n\nDiagnose and fix ABAP runtime errors (short dumps). A program that passes\nsyntax check can still crash at runtime — wrong field names, type mismatches,\nmissing data, unhandled exceptions. This skill reads the dump, identifies the\nroot cause, reads the crashing source, and proposes a fix with developer\napproval.\n\nUse cases:\n- **Upgrade remediation:** Code compiles after BSEG→ACDOCA migration but\n crashes at runtime (wrong field name, missing ledger filter, type mismatch)\n- **Support/incidents:** Production dump reported by end user or monitoring\n- **Post-deployment:** New transport activated, users start getting dumps\n- **Development:** Testing a new program, it dumps — diagnose and fix\n- **Triage before escalation:** Rank root-cause candidates for a dump you\n cannot (or must not) fix yet — read-only, evidence vs inference labeled\n\nSkill chain: `/abap-upgrade-fix` → run/test → **`/abap-dump`** → fix → retest\n\n## When to Use\n\n- Use when a program dumps: \"it dumps\", \"runtime error\", \"short dump\", ST22\n screenshot, or monitoring shows rising dump counts — diagnose AND fix.\n- Use (triage mode) when you only want ranked hypotheses / a second opinion /\n a safe next-steps checklist — say \"triage\" or \"don't change anything\".\n- **NOT for** the full dump→fix→transport→report pipeline — use\n `/abap-incident`; **NOT for** performance-only complaints without a dump —\n use `/abap-performance`.\n\n## Two Modes — Pick by Intent\n\n**Default = analyze + fix.** Run the full path below: find the dump, diagnose,\nread the source, propose the fix, and (on approval) apply it with all Forge\nRules.\n\n**Triage mode (structured investigation, read-only).** Switch to it ONLY when\nthe user asks for triage, ranking, hypotheses, a second opinion, or explicitly\nsays not to touch code (e.g. \"triage this dump\", \"what are the likely causes\",\n\"rank the candidates\", \"read-only\", \"before I escalate\"). In triage mode:\nNEVER write or propose `sap_set_source` calls — produce the Triage Report\n(see Triage Mode section) and stop. Production dumps reported by non-developers\ndefault to triage when no system write access is available.\n\nIf the user starts in triage mode and then says \"fix it\", continue into\nSteps 4–6 of the default path — the investigation already done counts.\n\n## Required Tools\n\n| Tool | Purpose |\n|------|---------|\n| `sap_short_dump` (list) | Find recent dumps, filter by user/program |\n| `sap_short_dump` (detail) | Get diagnostic extract: error, source, call stack, variables |\n| `sap_get_source` | Read the full source of the crashing program/class |\n| `sap_syntax_check` | Verify fix compiles before writing |\n| `sap_snapshot` | Backup before writing fix (Forge Rule 7) |\n| `sap_set_source` | Write the fix (default mode only) |\n| `sap_activate` | Explicit activation (default mode only) |\n| `sap_inactive_objects` | Verify activation (Forge Rule 10) |\n\nWithout MCP: work from pasted dump text (ST22 short text + analysis +\nstack). The fix path then produces a proposed diff for the developer to\napply manually; triage mode is fully available.\n\n## Required Behavior (default mode — analyze + fix)\n\n### Step 1 — Find the Dump\n\nIf the developer provides a specific dump (error name + program + time), go\ndirectly to Step 2. Otherwise, list recent dumps:\n\n```\nsap_short_dump (operation: \"list\", maxResults: 10)\n```\n\nOptional filters: `user` (who caused it), `program` (which program dumped).\n\nPresent the list:\n\n```\nRecent Short Dumps:\n # Date/Time Error Program User\n 1 2026-04-01 05:56:59 RAISE_EXCEPTION CL_BUS_TABSTRIP DEVELOPER_2\n 2 2026-03-31 08:33:49 CALL_FUNCTION_PARM_UNKNOWN ZCL_ABAPFORGE_API JSMITH\n 3 2026-03-31 04:45:38 SYNTAX_ERROR ZC_USERMASTER JSMITH\n\nWhich dump to analyze? [1-3]\n```\n\n### Step 2 — Read Dump Diagnostics\n\n```\nsap_short_dump (operation: \"detail\", dumpId: \"<id from list>\")\n```\n\nThe tool returns ~120 lines extracted from the full dump (~6000 lines):\n- **Error name and short text** — what went wrong\n- **Error analysis** — SAP's explanation\n- **Where terminated** — program, include, method, line number\n- **Source code extract** — 30 lines around the crash with `>>>>>` marker\n- **System fields** — SY-SUBRC, SY-TABIX, SY-MSGID, SY-TITLE\n- **Call stack** — top 15 active calls with program, include, and line\n\n### Step 3 — Diagnose Root Cause\n\nAnalyze the diagnostic text to determine root cause. Common patterns:\n\n| Error | Root Cause | Typical Fix |\n|-------|-----------|-------------|\n| `DBSQL_SQL_ERROR` | Field doesn't exist on table | Check DD03L, fix field name |\n| `CX_SY_OPEN_SQL_DB` | SQL error (wrong field, missing table) | Verify table/field exists |\n| `GETWA_NOT_ASSIGNED` | Field symbol not assigned (READ TABLE failed) | Add SY-SUBRC check |\n| `ITAB_LINE_NOT_FOUND` | READ TABLE with key not found | Add SY-SUBRC check or use OPTIONAL |\n| `CALL_FUNCTION_PARM_MISSING` | Required FM parameter not supplied | Check FM interface, add parameter |\n| `CALL_FUNCTION_PARM_UNKNOWN` | FM parameter doesn't exist | Check FM interface, remove parameter |\n| `RAISE_EXCEPTION` | Unhandled RAISE in FM/method | Add TRY/CATCH or handle the condition |\n| `UNCAUGHT_EXCEPTION` | OO exception not caught | Add TRY/CATCH for the exception class |\n| `OBJECTS_OBJREF_NOT_ASSIGNED` | Method call on a null object reference | Find why the instance is initial; guard with IS BOUND |\n| `SYNTAX_ERROR` | Generated code has syntax error | Fix source and regenerate |\n| `MESSAGE_TYPE_X` | MESSAGE TYPE 'X' (terminate) | Find the message, fix the condition |\n| `CONVT_NO_NUMBER` | Type conversion failed | Check data types, add validation |\n| `TIME_OUT` | Infinite loop or very long processing | Find the loop, add exit condition |\n| `STACK_OVERFLOW_NO_EXTENSION` | Infinite recursion | Find the recursive call, add termination |\n| `NO_AUTHORITY` | Missing authorization | Authorization fix, not a code fix |\n\n**For upgrade-related dumps:** After BSEG→ACDOCA migration, common issues:\n- Field `BUKRS` doesn't exist on ACDOCA → use `RBUKRS`\n- Field `BUZEI` doesn't exist → use `DOCLN`\n- Missing `RLDNR = '0L'` filter → returns too many/wrong rows\n- Status fields missing on VBAK → check which `%STK` fields exist via DD03L\n\n**Stack boundary check (from triage practice):** note where the dump sits —\ncustom Z-code, SAP standard code, or the boundary. If the innermost frame is\nSAP standard, the root cause is usually the last Z-frame above it.\n\n### Step 4 — Read Crashing Source\n\nThe dump tells us the program and line. Read the full source:\n\n```\nsap_get_source (objectType: \"CLAS\"/\"PROG\", objectName: \"<program>\")\n```\n\nFocus on the area around the crash line. Understand the full context:\n- What data is being processed?\n- What conditions lead to the crash?\n- Is there error handling that should have caught this?\n\n### Step 5 — Propose Fix\n\nPresent the diagnosis and proposed fix:\n\n```\n=== Dump Analysis ===\nError: DBSQL_SQL_ERROR\nProgram: ZLEGACY_FI_REPORT, line 25\nCause: Field BUKRS does not exist on table ACDOCA\n\nRoot cause: After BSEG→ACDOCA migration, BUKRS was not renamed to RBUKRS.\nACDOCA uses RBUKRS for company code (verified via DD03L).\n\nProposed fix (line 25):\n- SELECT rbukrs, belnr, gjahr FROM acdoca WHERE bukrs = @lv_bukrs.\n+ SELECT rbukrs, belnr, gjahr FROM acdoca WHERE rbukrs = @lv_bukrs.\n\nApply fix? [y/n]\n```\n\n### Step 6 — Apply Fix (with full safety)\n\nIf approved, apply with all Forge Rules:\n\n1. **Field verification** — if fix involves table fields, query DD03L first\n2. **Syntax check** — verify fix compiles before writing\n3. **Snapshot** (Forge Rule 7) — backup current source\n4. **Write** — `sap_set_source` with transport\n5. **Activate** — `sap_activate` explicitly\n6. **Verify** (Forge Rule 10) — `sap_inactive_objects` confirms active\n7. **Report** — \"Fix applied. Re-run the transaction to verify.\"\n\n### Step 7 — Verify (Optional)\n\nAfter the developer re-runs the transaction:\n- If it works: done\n- If it dumps again: repeat from Step 1 with the new dump\n- If a different error: new root cause, analyze the new dump\n\n## Triage Mode (structured investigation, read-only)\n\nWhen the user asks for triage/ranking only, run Steps 1–4 above (skip any\nwrite tools), then produce a **Triage Report** instead of a fix proposal:\n\n1. **Parse the dump** — error type, program/include, line, transaction, user,\n work process type (dialog/batch/update/RFC), timestamp, frequency\n (first / sporadic / consistent), system role (DEV/QA/PROD).\n2. **Categorize** — null reference / SQL error / type conflict / recursion /\n authorization / assertion / memory / lock / custom exception.\n3. **Stack trace analysis** — dump origin (innermost frame), call chain,\n custom-vs-standard boundary, last Z-object before the dump location.\n4. **Ranked root-cause candidates** (most likely first). For each:\n - **Hypothesis:** what specifically might be wrong\n - **Evidence:** what in the dump supports this — label explicitly as\n `Evidence:` (from dump text) or `Inferred:` (reasoned, e.g. timing\n correlation with a transport import)\n - **Likelihood:** High / Medium / Low\n - **Verification:** how to confirm or rule out\n5. **What to inspect next** — specific variables, call chains, tables,\n recent transports.\n6. **Questions for the team** — transport imported before onset? all users\n or one? specific time/data?\n7. **Safe next steps** — reproduce in DEV first, ST22 active-variables check,\n STMS import history, read-only debugging. Plus a **Prevention** note once\n root cause is confirmed (guard clauses, unit test, null-object pattern).\n\nTriage report skeleton:\n\n```\n## Dump Triage Report\n\n**Error Type / Program / Line / Transaction / User / Work Process / System / Timestamp**\n\n## Error Summary (category, location, first occurrence, frequency)\n## Stack Trace Analysis (origin, call chain, custom/standard boundary)\n## Root Cause Candidates (ranked; Hypothesis / Likelihood / Evidence|Inferred / Verification)\n## Probable Failing Area (1-2 sentences + confidence)\n## What to Inspect Next (numbered, specific)\n## Questions for the Team\n## Safe Next Steps (checklist — nothing that risks data or stability)\n## Prevention (after root cause confirmed)\n```\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure (default mode)\n\n```\n=== Short Dump Analysis ===\n\nError: {RUNTIME_ERROR}\nShort Text: {short text from dump}\nProgram: {MAINPROG}\nMethod/Form: {method or form name}\nLine: {line number}\nUser: {user who caused dump}\nDate/Time: {timestamp}\nTransaction: {from SY-TITLE or SY-TCODE}\n\n--- Root Cause ---\n{1-3 sentence explanation of why the dump occurred}\n\n--- Crash Location ---\n{source code extract with >>>>> marker, 10-15 lines}\n\n--- Call Stack (top 5) ---\n{top 5 frames from Active Calls}\n\n--- Proposed Fix ---\n{diff showing the fix}\n\nConfidence: {HIGH/MEDIUM/LOW}\nReason: {why this fix}\n\nApply fix? [y/n]\n```\n\nIn triage mode, use the Triage Report skeleton above instead.\n\n## Guardrails\n\n- The `sap_short_dump` detail operation returns ~120 lines from a ~6000 line\n dump. NEVER fetch the full formatted dump — it will consume the entire\n context window.\n- If the dump is in SAP standard code (not Z/Y namespace): DO NOT propose\n code changes. Note that the root cause may still be a Z-object higher up\n the call stack; otherwise search for SAP Notes with the error keywords and\n recommend applying the note.\n- For custom code dumps: follow all Forge Rules (snapshot, syntax check,\n activation verification) before and after writing fixes.\n- If the root cause is data-related (wrong config, missing master data, bad\n user input): explain the data issue and recommend the data fix, not a code\n change.\n- If the dump is TIME_OUT: analyze the loop structure but DO NOT attempt to\n fix without developer guidance — the fix might change business logic.\n- Always re-read the source via `sap_get_source` before proposing changes —\n the dump's source extract may be from an older version.\n- **Triage mode:** read-only, no exceptions. NEVER claim a definitive root\n cause — present candidates with likelihood levels, every claim labeled\n `Evidence:` or `Inferred:`. Never recommend modifying production code\n without reproducing in DEV first. If the dump name is unfamiliar: say so\n rather than inventing an interpretation.\n- For production dumps: do not recommend debugging in production without\n first checking whether DEV reproduction is feasible.\n",
65
- "sha256": "74409c965482dc28cb2a6be7b9c9496b00201e342de3ff089ed8630ad8480ec8",
71
+ "body": "---\r\nname: abap-dump\r\ndescription: >\r\n Diagnose ABAP short dumps (runtime errors / ST22) and fix them. Default\r\n path: read the dump diagnostics via sap_short_dump (120 lines from a\r\n 6000-line dump), identify the crash point, read the source, and apply the\r\n fix with developer approval. Triage mode (on request): ranked root-cause\r\n hypotheses with evidence vs inference, read-only, no code changes. Replaces\r\n abap-dump-analyze and abap-dump-triage.\r\nphase: DEBUG\r\nrequires_mcp: optional\r\nforge_rules: [6, 7, 10]\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.3.0\"\r\nwidget: stack-frames\r\n---\r\n\r\n# abap-dump\r\n\r\n## Purpose\r\n\r\nDiagnose and fix ABAP runtime errors (short dumps). A program that passes\r\nsyntax check can still crash at runtime — wrong field names, type mismatches,\r\nmissing data, unhandled exceptions. This skill reads the dump, identifies the\r\nroot cause, reads the crashing source, and proposes a fix with developer\r\napproval.\r\n\r\nUse cases:\r\n- **Upgrade remediation:** Code compiles after BSEG→ACDOCA migration but\r\n crashes at runtime (wrong field name, missing ledger filter, type mismatch)\r\n- **Support/incidents:** Production dump reported by end user or monitoring\r\n- **Post-deployment:** New transport activated, users start getting dumps\r\n- **Development:** Testing a new program, it dumps — diagnose and fix\r\n- **Triage before escalation:** Rank root-cause candidates for a dump you\r\n cannot (or must not) fix yet — read-only, evidence vs inference labeled\r\n\r\nSkill chain: `/abap-upgrade-fix` → run/test → **`/abap-dump`** → fix → retest\r\n\r\n## When to Use\r\n\r\n- Use when a program dumps: \"it dumps\", \"runtime error\", \"short dump\", ST22\r\n screenshot, or monitoring shows rising dump counts — diagnose AND fix.\r\n- Use (triage mode) when you only want ranked hypotheses / a second opinion /\r\n a safe next-steps checklist — say \"triage\" or \"don't change anything\".\r\n- **NOT for** the full dump→fix→transport→report pipeline — use\r\n `/abap-incident`; **NOT for** performance-only complaints without a dump —\r\n use `/abap-performance`.\r\n\r\n## Two Modes — Pick by Intent\r\n\r\n**Default = analyze + fix.** Run the full path below: find the dump, diagnose,\r\nread the source, propose the fix, and (on approval) apply it with all Forge\r\nRules.\r\n\r\n**Triage mode (structured investigation, read-only).** Switch to it ONLY when\r\nthe user asks for triage, ranking, hypotheses, a second opinion, or explicitly\r\nsays not to touch code (e.g. \"triage this dump\", \"what are the likely causes\",\r\n\"rank the candidates\", \"read-only\", \"before I escalate\"). In triage mode:\r\nNEVER write or propose `sap_set_source` calls — produce the Triage Report\r\n(see Triage Mode section) and stop. Production dumps reported by non-developers\r\ndefault to triage when no system write access is available.\r\n\r\nIf the user starts in triage mode and then says \"fix it\", continue into\r\nSteps 4–6 of the default path — the investigation already done counts.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_short_dump` (list) | Find recent dumps, filter by user/program |\r\n| `sap_short_dump` (detail) | Get diagnostic extract: error, source, call stack, variables |\r\n| `sap_get_source` | Read the full source of the crashing program/class |\r\n| `sap_syntax_check` | Verify fix compiles before writing |\r\n| `sap_snapshot` | Backup before writing fix (Forge Rule 7) |\r\n| `sap_set_source` | Write the fix (default mode only) |\r\n| `sap_activate` | Explicit activation (default mode only) |\r\n| `sap_inactive_objects` | Verify activation (Forge Rule 10) |\r\n\r\nWithout MCP: work from pasted dump text (ST22 short text + analysis +\r\nstack). The fix path then produces a proposed diff for the developer to\r\napply manually; triage mode is fully available.\r\n\r\n## Required Behavior (default mode — analyze + fix)\r\n\r\n### Step 1 — Find the Dump\r\n\r\nIf the developer provides a specific dump (error name + program + time), go\r\ndirectly to Step 2. Otherwise, list recent dumps:\r\n\r\n```\r\nsap_short_dump (operation: \"list\", maxResults: 10)\r\n```\r\n\r\nOptional filters: `user` (who caused it), `program` (which program dumped).\r\n\r\nPresent the list:\r\n\r\n```\r\nRecent Short Dumps:\r\n # Date/Time Error Program User\r\n 1 2026-04-01 05:56:59 RAISE_EXCEPTION CL_BUS_TABSTRIP DEVELOPER_2\r\n 2 2026-03-31 08:33:49 CALL_FUNCTION_PARM_UNKNOWN ZCL_ABAPFORGE_API JSMITH\r\n 3 2026-03-31 04:45:38 SYNTAX_ERROR ZC_USERMASTER JSMITH\r\n\r\nWhich dump to analyze? [1-3]\r\n```\r\n\r\n### Step 2 — Read Dump Diagnostics\r\n\r\n```\r\nsap_short_dump (operation: \"detail\", dumpId: \"<id from list>\")\r\n```\r\n\r\nThe tool returns ~120 lines extracted from the full dump (~6000 lines):\r\n- **Error name and short text** — what went wrong\r\n- **Error analysis** — SAP's explanation\r\n- **Where terminated** — program, include, method, line number\r\n- **Source code extract** — 30 lines around the crash with `>>>>>` marker\r\n- **System fields** — SY-SUBRC, SY-TABIX, SY-MSGID, SY-TITLE\r\n- **Call stack** — top 15 active calls with program, include, and line\r\n\r\n### Step 3 — Diagnose Root Cause\r\n\r\nAnalyze the diagnostic text to determine root cause. Common patterns:\r\n\r\n| Error | Root Cause | Typical Fix |\r\n|-------|-----------|-------------|\r\n| `DBSQL_SQL_ERROR` | Field doesn't exist on table | Check DD03L, fix field name |\r\n| `CX_SY_OPEN_SQL_DB` | SQL error (wrong field, missing table) | Verify table/field exists |\r\n| `GETWA_NOT_ASSIGNED` | Field symbol not assigned (READ TABLE failed) | Add SY-SUBRC check |\r\n| `ITAB_LINE_NOT_FOUND` | READ TABLE with key not found | Add SY-SUBRC check or use OPTIONAL |\r\n| `CALL_FUNCTION_PARM_MISSING` | Required FM parameter not supplied | Check FM interface, add parameter |\r\n| `CALL_FUNCTION_PARM_UNKNOWN` | FM parameter doesn't exist | Check FM interface, remove parameter |\r\n| `RAISE_EXCEPTION` | Unhandled RAISE in FM/method | Add TRY/CATCH or handle the condition |\r\n| `UNCAUGHT_EXCEPTION` | OO exception not caught | Add TRY/CATCH for the exception class |\r\n| `OBJECTS_OBJREF_NOT_ASSIGNED` | Method call on a null object reference | Find why the instance is initial; guard with IS BOUND |\r\n| `SYNTAX_ERROR` | Generated code has syntax error | Fix source and regenerate |\r\n| `MESSAGE_TYPE_X` | MESSAGE TYPE 'X' (terminate) | Find the message, fix the condition |\r\n| `CONVT_NO_NUMBER` | Type conversion failed | Check data types, add validation |\r\n| `TIME_OUT` | Infinite loop or very long processing | Find the loop, add exit condition |\r\n| `STACK_OVERFLOW_NO_EXTENSION` | Infinite recursion | Find the recursive call, add termination |\r\n| `NO_AUTHORITY` | Missing authorization | Authorization fix, not a code fix |\r\n\r\n**For upgrade-related dumps:** After BSEG→ACDOCA migration, common issues:\r\n- Field `BUKRS` doesn't exist on ACDOCA → use `RBUKRS`\r\n- Field `BUZEI` doesn't exist → use `DOCLN`\r\n- Missing `RLDNR = '0L'` filter → returns too many/wrong rows\r\n- Status fields missing on VBAK → check which `%STK` fields exist via DD03L\r\n\r\n**Stack boundary check (from triage practice):** note where the dump sits —\r\ncustom Z-code, SAP standard code, or the boundary. If the innermost frame is\r\nSAP standard, the root cause is usually the last Z-frame above it.\r\n\r\n### Step 4 — Read Crashing Source\r\n\r\nThe dump tells us the program and line. Read the full source:\r\n\r\n```\r\nsap_get_source (objectType: \"CLAS\"/\"PROG\", objectName: \"<program>\")\r\n```\r\n\r\nFocus on the area around the crash line. Understand the full context:\r\n- What data is being processed?\r\n- What conditions lead to the crash?\r\n- Is there error handling that should have caught this?\r\n\r\n### Step 5 — Propose Fix\r\n\r\nPresent the diagnosis and proposed fix:\r\n\r\n```\r\n=== Dump Analysis ===\r\nError: DBSQL_SQL_ERROR\r\nProgram: ZLEGACY_FI_REPORT, line 25\r\nCause: Field BUKRS does not exist on table ACDOCA\r\n\r\nRoot cause: After BSEG→ACDOCA migration, BUKRS was not renamed to RBUKRS.\r\nACDOCA uses RBUKRS for company code (verified via DD03L).\r\n\r\nProposed fix (line 25):\r\n- SELECT rbukrs, belnr, gjahr FROM acdoca WHERE bukrs = @lv_bukrs.\r\n+ SELECT rbukrs, belnr, gjahr FROM acdoca WHERE rbukrs = @lv_bukrs.\r\n\r\nApply fix? [y/n]\r\n```\r\n\r\n### Step 6 — Apply Fix (with full safety)\r\n\r\nIf approved, apply with all Forge Rules:\r\n\r\n1. **Field verification** — if fix involves table fields, query DD03L first\r\n2. **Syntax check** — verify fix compiles before writing\r\n3. **Snapshot** (Forge Rule 7) — backup current source\r\n4. **Write** — `sap_set_source` with transport\r\n5. **Activate** — `sap_activate` explicitly\r\n6. **Verify** (Forge Rule 10) — `sap_inactive_objects` confirms active\r\n7. **Report** — \"Fix applied. Re-run the transaction to verify.\"\r\n\r\n### Step 7 — Verify (Optional)\r\n\r\nAfter the developer re-runs the transaction:\r\n- If it works: done\r\n- If it dumps again: repeat from Step 1 with the new dump\r\n- If a different error: new root cause, analyze the new dump\r\n\r\n## Triage Mode (structured investigation, read-only)\r\n\r\nWhen the user asks for triage/ranking only, run Steps 1–4 above (skip any\r\nwrite tools), then produce a **Triage Report** instead of a fix proposal:\r\n\r\n1. **Parse the dump** — error type, program/include, line, transaction, user,\r\n work process type (dialog/batch/update/RFC), timestamp, frequency\r\n (first / sporadic / consistent), system role (DEV/QA/PROD).\r\n2. **Categorize** — null reference / SQL error / type conflict / recursion /\r\n authorization / assertion / memory / lock / custom exception.\r\n3. **Stack trace analysis** — dump origin (innermost frame), call chain,\r\n custom-vs-standard boundary, last Z-object before the dump location.\r\n4. **Ranked root-cause candidates** (most likely first). For each:\r\n - **Hypothesis:** what specifically might be wrong\r\n - **Evidence:** what in the dump supports this — label explicitly as\r\n `Evidence:` (from dump text) or `Inferred:` (reasoned, e.g. timing\r\n correlation with a transport import)\r\n - **Likelihood:** High / Medium / Low\r\n - **Verification:** how to confirm or rule out\r\n5. **What to inspect next** — specific variables, call chains, tables,\r\n recent transports.\r\n6. **Questions for the team** — transport imported before onset? all users\r\n or one? specific time/data?\r\n7. **Safe next steps** — reproduce in DEV first, ST22 active-variables check,\r\n STMS import history, read-only debugging. Plus a **Prevention** note once\r\n root cause is confirmed (guard clauses, unit test, null-object pattern).\r\n\r\nTriage report skeleton:\r\n\r\n```\r\n## Dump Triage Report\r\n\r\n**Error Type / Program / Line / Transaction / User / Work Process / System / Timestamp**\r\n\r\n## Error Summary (category, location, first occurrence, frequency)\r\n## Stack Trace Analysis (origin, call chain, custom/standard boundary)\r\n## Root Cause Candidates (ranked; Hypothesis / Likelihood / Evidence|Inferred / Verification)\r\n## Probable Failing Area (1-2 sentences + confidence)\r\n## What to Inspect Next (numbered, specific)\r\n## Questions for the Team\r\n## Safe Next Steps (checklist — nothing that risks data or stability)\r\n## Prevention (after root cause confirmed)\r\n```\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure (default mode)\r\n\r\n```\r\n=== Short Dump Analysis ===\r\n\r\nError: {RUNTIME_ERROR}\r\nShort Text: {short text from dump}\r\nProgram: {MAINPROG}\r\nMethod/Form: {method or form name}\r\nLine: {line number}\r\nUser: {user who caused dump}\r\nDate/Time: {timestamp}\r\nTransaction: {from SY-TITLE or SY-TCODE}\r\n\r\n--- Root Cause ---\r\n{1-3 sentence explanation of why the dump occurred}\r\n\r\n--- Crash Location ---\r\n{source code extract with >>>>> marker, 10-15 lines}\r\n\r\n--- Call Stack (top 5) ---\r\n{top 5 frames from Active Calls}\r\n\r\n--- Proposed Fix ---\r\n{diff showing the fix}\r\n\r\nConfidence: {HIGH/MEDIUM/LOW}\r\nReason: {why this fix}\r\n\r\nApply fix? [y/n]\r\n```\r\n\r\nIn triage mode, use the Triage Report skeleton above instead.\r\n\r\n## Guardrails\r\n\r\n- The `sap_short_dump` detail operation returns ~120 lines from a ~6000 line\r\n dump. NEVER fetch the full formatted dump — it will consume the entire\r\n context window.\r\n- If the dump is in SAP standard code (not Z/Y namespace): DO NOT propose\r\n code changes. Note that the root cause may still be a Z-object higher up\r\n the call stack; otherwise search for SAP Notes with the error keywords and\r\n recommend applying the note.\r\n- For custom code dumps: follow all Forge Rules (snapshot, syntax check,\r\n activation verification) before and after writing fixes.\r\n- If the root cause is data-related (wrong config, missing master data, bad\r\n user input): explain the data issue and recommend the data fix, not a code\r\n change.\r\n- If the dump is TIME_OUT: analyze the loop structure but DO NOT attempt to\r\n fix without developer guidance — the fix might change business logic.\r\n- Always re-read the source via `sap_get_source` before proposing changes —\r\n the dump's source extract may be from an older version.\r\n- **Triage mode:** read-only, no exceptions. NEVER claim a definitive root\r\n cause — present candidates with likelihood levels, every claim labeled\r\n `Evidence:` or `Inferred:`. Never recommend modifying production code\r\n without reproducing in DEV first. If the dump name is unfamiliar: say so\r\n rather than inventing an interpretation.\r\n- For production dumps: do not recommend debugging in production without\r\n first checking whether DEV reproduction is feasible.\r\n",
72
+ "sha256": "6c02ca012a343663c86b7d376ae8638385e4c8a0ec38d1d7b5a4091f5ba6e0c0",
66
73
  "signature": "",
67
- "signedAt": "2026-09-17T20:19:04.542Z"
74
+ "signedAt": "2026-09-28T14:37:04.401Z"
68
75
  },
69
76
  "abap-eml": {
70
77
  "name": "abap-eml",
71
78
  "body": "---\r\nname: abap-eml\r\ndescription: >\r\n Write and test EML (Entity Manipulation Language) for RAP business objects.\r\n Generates correct EML for any context: behavior handlers (IN LOCAL MODE),\r\n external consumers (with COMMIT), unit tests, and class runners. Handles\r\n %cid management, %control flags, FAILED/REPORTED/MAPPED responses, draft\r\n operations, deep create, and action execution. Reference: eml-reference.md.\r\nphase: BUILD\r\nrequires_mcp: required\r\nforge_rules: [7, 10]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-eml\r\n\r\n## Purpose\r\n\r\nGenerate and test correct EML code for RAP business objects. EML is the API\r\nlanguage for RAP — like SQL is for database tables. Every RAP interaction\r\n(create, read, update, delete, execute action) uses EML.\r\n\r\nGetting EML wrong causes subtle bugs: missing %cid crashes on CREATE, missing\r\n%control silently skips fields on UPDATE, COMMIT inside a handler causes a\r\nruntime error. This skill prevents those mistakes.\r\n\r\nUse cases:\r\n- **Behavior implementation** — write handler methods that use EML in LOCAL MODE\r\n- **External consumer** — call a RAP BO from any ABAP class\r\n- **Unit testing** — test RAP behavior with EML + test doubles\r\n- **API consumption** — call released SAP APIs (purchase requisition, business partner)\r\n- **Class runner demo** — quick EML test via IF_OO_ADT_CLASSRUN\r\n- **Troubleshoot EML errors** — diagnose FAILED responses, silent DISABLED failures\r\n\r\n## When to Use\r\n\r\n- Use to write or debug EML for a RAP BO — behavior handlers (IN LOCAL MODE), external consumers (with COMMIT), %cid/%control/FAILED handling, draft and action calls.\r\n- NOT for scaffolding the RAP stack itself — use `/abap-rap`; NOT for full unit-test classes on non-RAP code — use `/abap-test`.\r\n\r\n## Reference\r\n\r\nFull EML syntax reference: `.claude/skills/abap-rap/reference/eml-reference.md`\r\n(2,395 lines covering every statement form, variation, and gotcha)\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_get_source` | Read BDEF, behavior class, CDS views |\r\n| `sap_update_method` | Write EML into handler methods |\r\n| `sap_set_source` | Create new consumer/test classes |\r\n| `sap_syntax_check` | Verify EML compiles |\r\n| `sap_activate` | Activate after writing |\r\n| `sap_run_unit_test` | Run EML-based unit tests |\r\n\r\n## Context Rules — The Most Important Decision\r\n\r\nBefore writing ANY EML, determine the context. It changes everything:\r\n\r\n| Context | IN LOCAL MODE? | COMMIT? | %cid required? |\r\n|---------|---------------|---------|----------------|\r\n| Behavior handler (ABP) | **YES** — always | **NO** — forbidden | Yes on CREATE |\r\n| Behavior saver | Read only | **NO** — forbidden | N/A |\r\n| External consumer | **NO** — forbidden | **YES** — required | Yes on CREATE |\r\n| Unit test class | **NO** | **YES** | Yes on CREATE |\r\n| Class runner | **NO** | **YES** | Yes on CREATE |\r\n\r\n**Get this wrong and you get runtime errors.** COMMIT inside a handler =\r\n`BEHAVIOR_ILLEGAL_STATEMENT`. IN LOCAL MODE outside a handler = syntax error.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Understand the Business Object\r\n\r\nRead the BDEF to understand the entity structure:\r\n```\r\nsap_get_source (objectType: \"BDEF\", objectName: \"{bo_name}\")\r\n```\r\n\r\nExtract:\r\n- Entity names and aliases\r\n- Available operations (create, update, delete)\r\n- Actions (with parameters and return types)\r\n- Validations and determinations\r\n- Draft enabled?\r\n- Authorization master?\r\n- Numbering (early vs late)\r\n\r\n### Step 2 — Determine Context\r\n\r\nAsk or infer: where will this EML run?\r\n- Handler method → IN LOCAL MODE, no COMMIT\r\n- External class → no LOCAL MODE, COMMIT required\r\n- Test class → no LOCAL MODE, COMMIT + ROLLBACK in teardown\r\n\r\n### Step 3 — Generate EML\r\n\r\nFollow these patterns exactly.\r\n\r\n#### CREATE (in handler — LOCAL MODE)\r\n\r\n```abap\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n CREATE FIELDS ( CustomerID OrderDate )\r\n WITH VALUE #(\r\n ( %cid = 'new1'\r\n CustomerID = ls_data-CustomerID\r\n OrderDate = sy-datum ) )\r\n MAPPED mapped\r\n FAILED failed\r\n REPORTED reported.\r\n```\r\n\r\n#### CREATE (external consumer — with COMMIT)\r\n\r\n```abap\r\nMODIFY ENTITIES OF zi_salesorder\r\n ENTITY SalesOrder\r\n CREATE FIELDS ( CustomerID OrderDate )\r\n WITH VALUE #(\r\n ( %cid = 'new1'\r\n CustomerID = 'CUST001'\r\n OrderDate = sy-datum ) )\r\n MAPPED DATA(lt_mapped)\r\n FAILED DATA(lt_failed)\r\n REPORTED DATA(lt_reported).\r\n\r\nCOMMIT ENTITIES.\r\nIF sy-subrc <> 0.\r\n \" Handle failure\r\nENDIF.\r\n```\r\n\r\n#### UPDATE (with %control — MANDATORY)\r\n\r\n```abap\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n UPDATE FROM VALUE #(\r\n ( SalesOrderID = ls_key-SalesOrderID\r\n Status = 'APPROVED'\r\n %control-Status = if_abap_behv=>mk-on ) )\r\n FAILED failed\r\n REPORTED reported.\r\n```\r\n\r\n**Every field you want to update MUST have %control-{field} = mk-on.**\r\nWithout it, the field is silently ignored even if you set a value.\r\n\r\n#### DELETE\r\n\r\n```abap\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n DELETE FROM VALUE #(\r\n ( SalesOrderID = ls_key-SalesOrderID ) )\r\n FAILED failed\r\n REPORTED reported.\r\n```\r\n\r\n#### READ\r\n\r\n```abap\r\nREAD ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n FIELDS ( CustomerID OrderDate Status )\r\n WITH CORRESPONDING #( keys )\r\n RESULT DATA(lt_orders).\r\n```\r\n\r\nOr read ALL fields:\r\n```abap\r\nREAD ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n ALL FIELDS\r\n WITH CORRESPONDING #( keys )\r\n RESULT DATA(lt_orders).\r\n```\r\n\r\n#### EXECUTE ACTION\r\n\r\n```abap\r\n\" Action without parameter\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n EXECUTE Approve\r\n FROM CORRESPONDING #( keys )\r\n FAILED failed\r\n REPORTED reported.\r\n\r\n\" Action with parameter\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n EXECUTE Reject\r\n FROM VALUE #(\r\n ( %tky = ls_key-%tky\r\n %param-RejectionReason = 'Budget exceeded' ) )\r\n RESULT DATA(lt_result)\r\n FAILED failed\r\n REPORTED reported.\r\n```\r\n\r\n#### DEEP CREATE (parent + children)\r\n\r\n```abap\r\nMODIFY ENTITIES OF zi_salesorder IN LOCAL MODE\r\n ENTITY SalesOrder\r\n CREATE FIELDS ( CustomerID OrderDate )\r\n WITH VALUE #(\r\n ( %cid = 'order1'\r\n CustomerID = 'CUST001'\r\n OrderDate = sy-datum ) )\r\n\r\n CREATE BY \\_Items\r\n FIELDS ( MaterialID Quantity )\r\n WITH VALUE #(\r\n ( %cid_ref = 'order1' \" parent's %cid\r\n %cid = 'item1'\r\n MaterialID = 'MAT001'\r\n Quantity = 10 )\r\n ( %cid_ref = 'order1'\r\n %cid = 'item2'\r\n MaterialID = 'MAT002'\r\n Quantity = 5 ) )\r\n\r\n MAPPED mapped\r\n FAILED failed\r\n REPORTED reported.\r\n```\r\n\r\n**%cid_ref on child = %cid of parent.** This links them before keys exist.\r\n\r\n#### DRAFT Operations\r\n\r\n```abap\r\n\" Create a draft instance\r\nMODIFY ENTITIES OF zi_salesorder\r\n ENTITY SalesOrder\r\n CREATE FIELDS ( CustomerID )\r\n WITH VALUE #(\r\n ( %cid = 'draft1'\r\n %is_draft = if_abap_behv=>mk-on \" DRAFT\r\n CustomerID = 'CUST001' ) )\r\n MAPPED DATA(lt_mapped)\r\n FAILED DATA(lt_failed)\r\n REPORTED DATA(lt_reported).\r\n\r\n\" Activate draft (make it real)\r\nMODIFY ENTITIES OF zi_salesorder\r\n ENTITY SalesOrder\r\n EXECUTE Activate\r\n FROM VALUE #(\r\n ( %key-SalesOrderID = lt_mapped-salesorder[ 1 ]-SalesOrderID\r\n %is_draft = if_abap_behv=>mk-on ) )\r\n FAILED DATA(lt_act_failed)\r\n REPORTED DATA(lt_act_reported).\r\n\r\nCOMMIT ENTITIES.\r\n```\r\n\r\n**%is_draft must be consistent** — if parent is draft, children must be draft.\r\n\r\n#### Late Numbering (CONVERT KEY)\r\n\r\n```abap\r\n\" After COMMIT, get the final key assigned by the system\r\nCOMMIT ENTITIES\r\n RESPONSES OF zi_salesorder\r\n FAILED DATA(lt_commit_failed)\r\n REPORTED DATA(lt_commit_reported).\r\n\r\n\" Convert provisional %pid to final key\r\nCONVERT KEY OF zi_salesorder\r\n FROM VALUE #(\r\n ( %pid = lt_mapped-salesorder[ 1 ]-%pid ) )\r\n TO VALUE #(\r\n ( SalesOrderID = DATA(lv_final_id) ) ).\r\n```\r\n\r\n### Step 4 — Handle Responses\r\n\r\n**Always check FAILED.** EML can fail silently.\r\n\r\n```abap\r\nIF lt_failed-salesorder IS NOT INITIAL.\r\n \" Inspect failures\r\n LOOP AT lt_failed-salesorder INTO DATA(ls_fail).\r\n CASE ls_fail-%fail-cause.\r\n WHEN if_abap_behv=>cause-not_found.\r\n \" Entity doesn't exist\r\n WHEN if_abap_behv=>cause-unauthorized.\r\n \" No permission\r\n WHEN if_abap_behv=>cause-disabled.\r\n \" Operation disabled by feature control\r\n WHEN if_abap_behv=>cause-unspecific.\r\n \" Check REPORTED for details\r\n ENDCASE.\r\n ENDLOOP.\r\nENDIF.\r\n```\r\n\r\n**REPORTED contains the user-facing messages:**\r\n```abap\r\nLOOP AT lt_reported-salesorder INTO DATA(ls_msg).\r\n DATA(lo_msg) = ls_msg-%msg.\r\n \" lo_msg->if_message~get_text( ) — the message text\r\n \" lo_msg->if_message~get_severity( ) — E/W/I/S\r\nENDLOOP.\r\n```\r\n\r\n### Step 5 — Write and Verify\r\n\r\nUse `sap_update_method` for handler methods (preserves the rest of the class).\r\nUse `sap_set_source` only for new classes (test classes, consumers).\r\n\r\nAlways: snapshot → syntax check → write → activate → verify.\r\n\r\n## Guardrails\r\n\r\n### The Three Fatal Mistakes\r\n1. **COMMIT inside handler** → runtime error `BEHAVIOR_ILLEGAL_STATEMENT`\r\n2. **IN LOCAL MODE outside handler** → syntax error\r\n3. **Missing %cid on CREATE** → runtime error or contract violation\r\n\r\n### %control is Not Optional\r\nOn UPDATE with FROM, every field you want changed needs `%control-{field} = if_abap_behv=>mk-on`. Without it, the field is **silently ignored** — no error, no warning, just nothing happens. This is the #1 source of \"my update doesn't work\" bugs.\r\n\r\n### Check FAILED, Not Just sy-subrc\r\nEML operations can put entries in FAILED without setting sy-subrc <> 0. Always check the FAILED response table explicitly, especially for cause = DISABLED (feature control blocking the operation silently).\r\n\r\n### Dynamic EML: Entity Names Must Be UPPERCASE\r\n```abap\r\nentity_name = 'ZI_SALESORDER' \" correct\r\nentity_name = 'zi_salesorder' \" runtime dump\r\n```\r\n\r\n### Draft Consistency\r\nIf `%is_draft = mk-on` on the parent, ALL child entities in the same request must also have `%is_draft = mk-on`. Mixing draft and non-draft in one operation causes inconsistencies.\r\n\r\n### Never Call MODIFY in a Saver Method\r\nSaver methods (cleanup, cleanup_finalize, save_modified, adjust_numbers) can only READ, not MODIFY. Any MODIFY in a saver causes a runtime error.\r\n",
72
79
  "sha256": "eb2a9b60234e1c5d906df7ffe8f2a75108a91f2220f884a5aa649fb199496077",
73
80
  "signature": "",
74
- "signedAt": "2026-09-17T20:19:04.588Z"
81
+ "signedAt": "2026-09-28T14:37:04.434Z"
75
82
  },
76
83
  "abap-enhance": {
77
84
  "name": "abap-enhance",
78
85
  "body": "---\r\nname: abap-enhance\r\ndescription: >\r\n Extend SAP-DELIVERED STANDARD functionality using BAdI implementations,\r\n enhancement spots, implicit enhancements, or customer exits. For SAP standard\r\n objects ONLY — never for editing your own custom Z/Y code (use abap-refactor\r\n to change existing custom code, or abap-generate for new objects). Identifies\r\n the correct extension point for the requirement, explains tradeoffs, and\r\n generates a clean implementation skeleton. Read-only by default; writes only\r\n with MCP confirmation.\r\nphase: BUILD\r\nrequires_mcp: optional\r\nforge_rules: [1, 2, 3, 6, 7, 10]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-enhance\r\n\r\n## Purpose\r\n\r\nImplement SAP standard enhancements without modifying SAP source code.\r\nThe skill identifies the correct extension point (BAdI, enhancement spot,\r\nimplicit enhancement, customer exit, user exit, or substitution), explains\r\nthe tradeoffs between approaches, and generates a properly structured\r\nimplementation skeleton.\r\n\r\nThis skill is explicitly read-only unless an MCP connection is present and\r\nthe user confirms a write. It never recommends modifying SAP standard source\r\ncode directly.\r\n\r\n## When to Use\r\n\r\n- Adding a custom validation or check during a standard SAP process\r\n- Injecting custom logic at a specific point in standard program execution\r\n- Implementing a BAdI to override or supplement standard behavior\r\n- Adding fields or code to a standard screen (screen exits)\r\n- Replacing a standard function with a custom implementation\r\n\r\nDo NOT use this skill if:\r\n- You need to modify a Z-object (use `/abap-refactor` or `/abap-generate`)\r\n- You need to create a new standalone RAP object (use `/abap-rap`)\r\n- You are in BTP ABAP — only released BAdIs are available in cloud\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Business requirement: what should the enhancement do?\r\n- Business transaction or program area: where does it belong?\r\n\r\nUseful (will be asked if not provided):\r\n- SAP transaction code or program name (e.g. ME21N, MIGO, SAPMM06E)\r\n- Whether this is ECC, S/4HANA on-prem, or BTP ABAP\r\n- Known BAdI or enhancement spot name (if already identified)\r\n- Whether the enhancement needs to read or write data\r\n- Package and transport\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Understand the Requirement\r\n\r\nAsk:\r\n1. What exactly should happen that does not happen in standard SAP today?\r\n2. Which transaction or process is involved?\r\n3. Is this ECC, S/4HANA on-prem, or BTP ABAP?\r\n4. Should the enhancement read data, modify it, or raise errors?\r\n\r\n### Step 2 — Identify the Extension Point\r\n\r\nBased on the requirement and transaction, identify candidates:\r\n\r\n- **BAdI** — preferred for modern S/4HANA enhancements; clearly defined\r\n interface; multiple implementations possible; filter-capable\r\n- **Enhancement Spot / Explicit Enhancement** — named hook in source code;\r\n source-code plug-in; survives upgrades in most cases\r\n- **Implicit Enhancement** — at begin or end of function module or method;\r\n no named hook; use only when explicit spot not available\r\n- **Customer Exit (EXIT_)** — legacy mechanism from ECC era; function module\r\n or include-based; still used in older transactions\r\n- **User Exit (USEREXIT_)** — very old; source includes in standard programs;\r\n not recommended for new development\r\n\r\nIf MCP is connected, use `sap_search_object` to look for BAdIs matching the\r\nbusiness context. State explicitly if you cannot confirm the BAdI exists —\r\ndo not invent enhancement point names.\r\n\r\n### Step 3 — Recommend an Approach\r\n\r\nPresent:\r\n- Primary recommendation with justification\r\n- Alternative approaches with tradeoffs\r\n- What cannot be done without SAP modification (and why to avoid that)\r\n\r\n### Step 4 — Show the Implementation Plan\r\n\r\n- BAdI name / enhancement spot name (confirmed or inferred — state which)\r\n- Interface or method to implement\r\n- Skeleton code that will be generated\r\n- Any filter conditions needed\r\n- Package and naming convention\r\n\r\nAsk for confirmation before generating.\r\n\r\n### Step 5 — Generate Implementation Skeleton\r\n\r\nGenerate:\r\n- BAdI implementation class (if BAdI)\r\n- Enhancement implementation (if enhancement spot)\r\n- All methods stubbed with inline comments explaining what to add\r\n- ABAP Doc on the class and all public methods\r\n\r\n### Step 6 — MCP Creation (if confirmed)\r\n\r\nIf MCP is active:\r\n1. Create the BAdI implementation or enhancement implementation via sap_create_object\r\n2. Run sap_syntax_check\r\n3. Report result\r\n4. Remind user to activate and assign to a filter value if applicable\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Enhancement Analysis\r\n\r\n**Requirement:** {short_requirement_description}\r\n**Transaction / Program:** {transaction_or_program}\r\n**Environment:** {ECC | S/4HANA | BTP ABAP}\r\n\r\n---\r\n\r\n## Extension Point Candidates\r\n\r\n| Approach | Name / Location | Confidence | Recommended |\r\n|---------------------|-------------------------------|------------|-------------|\r\n| BAdI | {badi_name} | Confirmed | Yes |\r\n| Enhancement Spot | {spot_name} | Inferred | No |\r\n| Implicit Enhancement| End of FM {fm_name} | Possible | Fallback |\r\n\r\n**Recommendation:** {approach} — {justification}\r\n\r\n---\r\n\r\n## Implementation Plan\r\n\r\n**BAdI Name:** {badi_name}\r\n**Interface:** {interface_name}\r\n**Method(s) to Implement:** {method_list}\r\n**Filter:** {filter_condition or N/A}\r\n**Implementation Class:** {class_name}\r\n**Package:** {package}\r\n\r\n---\r\n\r\n## Generated Implementation\r\n\r\n```abap\r\n{implementation_code}\r\n```\r\n\r\n---\r\n\r\n## Activation Steps\r\n\r\n1. In ADT or SE19: create new BAdI implementation for {badi_name}\r\n2. Assign implementation class {class_name}\r\n3. Set filter: {filter_value or N/A}\r\n4. Activate the implementation\r\n5. Test by executing {transaction_or_program}\r\n\r\n## Guardrails Notice\r\n\r\n{any_risks_or_limitations_specific_to_this_enhancement}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method logic — use `sap_update_method` per method instead (Rule 7a)\r\n- NEVER recommend modifying SAP standard source code (OSS modifications)\r\n- NEVER invent BAdI names — clearly state \"inferred\" vs \"confirmed via MCP\"\r\n- NEVER claim a BAdI exists in a release without evidence\r\n- BTP ABAP: only released (C1-use allowed) extension points are valid\r\n- If the only viable option is an implicit enhancement at an undocumented point,\r\n say so clearly and warn about upgrade risk\r\n- If MCP is not connected and BAdI name is inferred: add a disclaimer to\r\n verify the BAdI name in SAP before implementing\r\n- Customer exits in ECC may conflict with S/4HANA migration — note this\r\n- Always check whether a released API already achieves the goal before\r\n recommending an enhancement\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-enhance\r\n\r\nWe need to add a custom validation when a purchase order is created or changed\r\nin ME21N / ME22N. The validation should check that the vendor is on our\r\napproved vendor list (stored in ZVENDR_APPRVD table). If the vendor is not\r\napproved, block saving with an error message. We are on S/4HANA 2022 on-prem.\r\nPackage ZMMT_ENHANCE.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Enhancement Analysis\r\n\r\n**Requirement:** Block PO save if vendor is not in ZVENDR_APPRVD\r\n**Transaction / Program:** ME21N / ME22N (Materials Management — Purchasing)\r\n**Environment:** S/4HANA 2022 on-premise\r\n\r\n---\r\n\r\n## Extension Point Candidates\r\n\r\n| Approach | Name | Confidence | Recommended |\r\n|-------------------|------------------------------------------------|------------|-------------|\r\n| BAdI | MM_PURCHASE_ORDER_ENHANCEMENTS | Inferred | Yes |\r\n| BAdI | ME_PROCESS_PO_CUST | Inferred | Alternative |\r\n| Customer Exit | EXIT_SAPMM06E_012 | Known-ECC | Not for S/4 |\r\n| Implicit Enh. | End of FM ME_DIRECT_INPUT_PO_HEADER | Possible | Fallback |\r\n\r\n**Note:** BAdI names above are inferred based on documented S/4HANA extension\r\npoints for ME21N. Please verify in SAP transaction SE18 or via MCP\r\nsap_search_object before implementing.\r\n\r\n**Recommendation:** BAdI MM_PURCHASE_ORDER_ENHANCEMENTS with method\r\nCHECK_BEFORE_SAVE — this is the standard S/4HANA extension point for PO\r\nheader/item validation before save. It provides access to the complete PO\r\nstructure and allows raising error messages that block saving.\r\n\r\n---\r\n\r\n## Implementation Plan\r\n\r\n**BAdI Name:** MM_PURCHASE_ORDER_ENHANCEMENTS\r\n**Interface:** IF_MM_PURCHASE_ORDER_ENHANCEMENTS\r\n**Method:** CHECK_BEFORE_SAVE\r\n**Filter:** N/A (applies to all PO types — add BSART filter if needed)\r\n**Implementation Class:** ZCL_MM_VENDOR_APPROVAL_CHECK\r\n**Package:** ZMMT_ENHANCE\r\n\r\n---\r\n\r\n## Generated Implementation\r\n\r\n```abap\r\n\"! <p class=\"shorttext synchronized\">Vendor approval check for PO save</p>\r\n\"! BAdI implementation for MM_PURCHASE_ORDER_ENHANCEMENTS.\r\n\"! Blocks PO save if the vendor is not in the approved vendor list.\r\nCLASS zcl_mm_vendor_approval_check DEFINITION\r\n PUBLIC FINAL\r\n CREATE PUBLIC.\r\n\r\n PUBLIC SECTION.\r\n INTERFACES if_mm_purchase_order_enhancements.\r\n\r\n PRIVATE SECTION.\r\n \"! Check if vendor is on the approved vendor list\r\n METHODS is_vendor_approved\r\n IMPORTING iv_lifnr TYPE lifnr\r\n RETURNING VALUE(rv_result) TYPE abap_bool.\r\nENDCLASS.\r\n\r\nCLASS zcl_mm_vendor_approval_check IMPLEMENTATION.\r\n\r\n METHOD if_mm_purchase_order_enhancements~check_before_save.\r\n \"im_header: PO header data structure\r\n \"it_item: Table of PO items\r\n \"io_message: Message handler — use to report errors\r\n\r\n DATA(lv_approved) = is_vendor_approved(\r\n iv_lifnr = im_header-lifnr ).\r\n\r\n IF lv_approved = abap_false.\r\n \"Add error message — this blocks the save\r\n io_message->add_message(\r\n i_msgid = 'ZMM'\r\n i_msgty = 'E'\r\n i_msgno = '001'\r\n i_msgv1 = im_header-lifnr ).\r\n ENDIF.\r\n ENDMETHOD.\r\n\r\n METHOD is_vendor_approved.\r\n SELECT SINGLE @abap_true\r\n FROM zvendr_apprvd\r\n WHERE lifnr = @iv_lifnr\r\n AND valid_to >= @sy-datum\r\n INTO @rv_result.\r\n\r\n IF sy-subrc <> 0.\r\n rv_result = abap_false.\r\n ENDIF.\r\n ENDMETHOD.\r\n\r\nENDCLASS.\r\n```\r\n\r\n**Assumption:** ZVENDR_APPRVD has fields LIFNR and VALID_TO. Adjust the SELECT\r\nif the structure differs. Message class ZMM with message 001 must exist with\r\ntext \"Vendor & is not on the approved vendor list\".\r\n\r\n---\r\n\r\n## Activation Steps\r\n\r\n1. Open SE18 in SAP or use ADT BAdI explorer\r\n2. Find BAdI MM_PURCHASE_ORDER_ENHANCEMENTS\r\n3. Create new implementation → assign ZCL_MM_VENDOR_APPROVAL_CHECK\r\n4. Activate the implementation\r\n5. Test in ME21N with an unapproved vendor — expect error message on save\r\n\r\n## Guardrails Notice\r\n\r\n- BAdI name MM_PURCHASE_ORDER_ENHANCEMENTS was inferred, not confirmed via MCP.\r\n Verify it exists in your system before proceeding.\r\n- The io_message interface signature may differ between S/4HANA releases.\r\n Check the actual method signature in your system via SE18.\r\n- If the BAdI does not exist, fallback option is ME_PROCESS_PO_CUST method\r\n PROCESS_ITEM — but that fires per line item, not once per header.\r\n```\r\n",
79
86
  "sha256": "e7dabb80545f3df496ee28c96b8ca83a8d4000004b47546df30f457c9d002858",
80
87
  "signature": "",
81
- "signedAt": "2026-09-17T20:19:04.633Z"
88
+ "signedAt": "2026-09-28T14:37:04.466Z"
82
89
  },
83
90
  "abap-estimate": {
84
91
  "name": "abap-estimate",
85
92
  "body": "---\r\nname: abap-estimate\r\ndescription: \"Effort estimation for ABAP development tickets — component breakdown with hour ranges and risk adjustments.\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-estimate\r\n\r\n## Purpose\r\n\r\nProduce a structured, defensible effort estimate for an ABAP development task. Break the work down into concrete components, assign hour ranges (optimistic / realistic / pessimistic) to each, apply risk adjustments, and produce a final estimate with stated assumptions and caveats. This skill never generates code — it estimates the work of generating it.\r\n\r\nEstimates are inherently uncertain. This skill makes uncertainty explicit through ranges and risk factors rather than hiding it in a single point estimate. Every assumption that affects the estimate is stated so that stakeholders can challenge or confirm them.\r\n\r\n## When to Use\r\n\r\n- Before sprint planning to size a ticket\r\n- When a project manager needs an hours range for a feature\r\n- When a developer needs to justify an estimate to a business owner\r\n- After `/abap-spec-gap` and `/abap-design` to estimate the designed object list\r\n- When comparing build-vs-buy or custom-vs-standard options on cost\r\n\r\nWhen NOT to use: no confirmed object list yet — run `/abap-design` first (estimates without it carry high uncertainty); finding missing requirements — `/abap-spec-gap`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE or MORE of the following:\r\n\r\n1. **Ticket or story description** — what needs to be built\r\n2. **Object list from `/abap-design`** — the definitive list of objects to create\r\n3. **System context** — SAP release, whether MCP tools are available, team familiarity with the domain\r\n4. **Complexity signals** — known integrations, performance requirements, complex business rules\r\n5. **Developer profile** — `senior` (default), `mid-level`, or `junior` — affects realistic hours\r\n6. **Includes** — whether to include: unit tests, documentation, transport, UAT support, code review\r\n\r\nIf no object list is provided, the estimate is based on the requirement description and is marked `[HIGH UNCERTAINTY — object list not confirmed]`.\r\n\r\n## Required Behavior\r\n\r\n1. **Parse the input.** Identify all development work items. If an object list from `/abap-design` is provided, use it directly. If not, infer the likely object list from the requirement and mark it `[INFERRED]`.\r\n\r\n2. **Decompose into components.** For each distinct type of work — DDIC design, DDIC creation, CDS view, RAP behavior, class implementation, unit tests, Fiori configuration, transport, documentation, code review, UAT support — create a component row.\r\n\r\n3. **Assign hour ranges.** For each component, provide:\r\n - **Optimistic** — everything goes right, no surprises, developer knows the domain cold\r\n - **Realistic** — some discovery, some rework, one integration issue to debug\r\n - **Pessimistic** — requirement gap discovered mid-build, first attempt at this pattern, external dependency delays\r\n\r\n4. **Sum the ranges.** Produce totals for all three columns. The realistic total is the primary estimate.\r\n\r\n5. **Apply risk adjustments.** For each relevant risk factor, state the adjustment as a percentage and reason:\r\n - First time using this pattern (RAP, EWM, new BAdI): +20–40%\r\n - External dependency (RFC to legacy system, third-party API): +15–25%\r\n - Unclear requirements (spec gaps not yet resolved): +20–50%\r\n - Legacy system with undocumented side effects: +20–30%\r\n - Team unfamiliar with domain: +15–25%\r\n - Clean core constraint (cannot modify standard, must find extension points): +10–20%\r\n\r\n6. **Produce the adjusted estimate.** Apply the total risk adjustment percentage to the realistic hours. Round to half-days (4-hour increments).\r\n\r\n7. **State assumptions.** Every assumption that materially affects the estimate must be listed. If an assumption proves wrong, the estimate changes.\r\n\r\n8. **State caveats.** Known unknowns that are not quantified in the risk adjustments: items explicitly out of scope, dependencies on other teams, items that would trigger a re-estimate.\r\n\r\n9. **Label inferences.** If the object list was inferred from the description rather than confirmed by a design document, label each inferred component `[INFERRED]`.\r\n\r\n10. **Forge Rules compliance.** This skill is read-only (Rule 6). No code is generated. Rule 1 is honored — gaps in the spec that affect the estimate are called out rather than assumed away.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Effort Estimate — [TICKET/STORY TITLE]\r\n\r\n**Input:** [Object List from /abap-design / Requirement Description Only]\r\n**Developer Profile:** [Senior / Mid-Level / Junior]\r\n**Uncertainty:** [LOW — confirmed object list / MEDIUM — mostly confirmed / HIGH — requirement only]\r\n**Includes:** [Unit Tests: Y/N | Documentation: Y/N | Transport: Y/N | UAT Support: Y/N | Code Review: Y/N]\r\n\r\n---\r\n\r\n### Component Breakdown\r\n\r\n| # | Component | Objects | Optimistic (h) | Realistic (h) | Pessimistic (h) | Notes |\r\n|---|-----------|---------|---------------|--------------|----------------|-------|\r\n| 1 | DDIC design and creation | 3 domains, 4 DEs, 1 table | 2 | 3 | 5 | |\r\n| 2 | CDS interface view | ZI_ApprovalRequest | 1 | 2 | 4 | |\r\n| 3 | ... | ... | ... | ... | ... | |\r\n| | **TOTAL (before risk)** | | **X** | **Y** | **Z** | |\r\n\r\n---\r\n\r\n### Risk Adjustments\r\n\r\n| Risk Factor | Adjustment | Reason |\r\n|------------|-----------|--------|\r\n| [risk] | +[N]% | [specific reason from this ticket] |\r\n| [risk] | +[N]% | [specific reason from this ticket] |\r\n| **Total Risk** | **+[N]%** | |\r\n\r\n---\r\n\r\n### Final Estimate\r\n\r\n| Scenario | Hours | Days (8h) |\r\n|----------|-------|-----------|\r\n| Optimistic | [X] | [X/8] |\r\n| Realistic (recommended) | [Y × (1 + risk%)] | [(Y × risk)/8] |\r\n| Pessimistic | [Z × (1 + risk%)] | [(Z × risk)/8] |\r\n\r\n**Recommended estimate: [N] hours ([N] days)**\r\nRange: [optimistic] – [pessimistic] hours\r\n\r\n---\r\n\r\n### Assumptions\r\n1. [Assumption that if wrong would change the estimate]\r\n2. [Assumption]\r\n...\r\n\r\n---\r\n\r\n### Caveats and Re-estimate Triggers\r\n- [Thing that is out of scope]\r\n- [Change that would trigger a re-estimate]\r\n- [Dependency on another team or external system]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Never give a single-point estimate without a range.** A single number without a range is not an estimate — it is a guess presented as a commitment. Always show optimistic/realistic/pessimistic.\r\n- **Never estimate what has not been specified.** If a scope item is unclear, do not silently include or exclude it — state it as an assumption or flag it as a re-estimate trigger.\r\n- **Do not underestimate to please.** If the realistic estimate is 40 hours, present 40 hours with full transparency about what it includes. An underestimate that leads to a blown deadline serves no one.\r\n- **Label all inferred objects.** If the object list was not confirmed by a `/abap-design` run, mark it `[INFERRED]` and add a high-uncertainty flag to the overall estimate.\r\n- **Do not include items out of scope without flagging them.** If UAT support is not requested, do not include it in the total — but do note in Caveats that it is not included.\r\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind generation — this applies to estimation assumptions too: do not assume away spec gaps).\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-estimate developer-profile: senior includes: unit-tests, transport, code-review\r\n\r\nTicket: ZSD-441 — Material Pricing Report with Enhancement\r\n\r\nRequirement: Build a report showing material pricing conditions for all materials in a plant. The report must:\r\n1. Show materials, their base price (PR00), any customer-specific discounts (K007), and calculated net price\r\n2. Allow filtering by plant, material group, and customer\r\n3. The pricing logic uses an existing BAdI ZBAdI_PRICING_DISPLAY that another team implemented — we must call it to get the final net price\r\n4. Output as ALV with export to Excel\r\n5. Additionally, add a pricing history tab showing the last 6 months of price changes from KONP/KONV\r\n\r\nObject list confirmed from /abap-design:\r\n- ZI_MaterialPrice (CDS interface view on A004/A005/KONV/MARA/MARC)\r\n- ZC_MaterialPrice (CDS projection view)\r\n- ZCL_PRICING_REPORT (report class: selection handling, ALV setup, BAdI call)\r\n- ZCL_PRICING_HISTORY (history data provider class)\r\n- ZIF_PRICING_DATA_PROVIDER (interface for test doubles)\r\n- ZREP_MATERIAL_PRICING (executable program calling ZCL_PRICING_REPORT)\r\n- Unit test class in ZCL_PRICING_REPORT\r\n- Unit test class in ZCL_PRICING_HISTORY\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Effort Estimate — ZSD-441 Material Pricing Report with Enhancement\r\n\r\n**Input:** Confirmed object list from /abap-design\r\n**Developer Profile:** Senior\r\n**Uncertainty:** MEDIUM — object list confirmed but BAdI integration behavior undocumented\r\n**Includes:** Unit Tests: YES | Documentation: NO | Transport: YES | UAT Support: NO | Code Review: YES\r\n\r\n---\r\n\r\n### Component Breakdown\r\n\r\n| # | Component | Objects | Optimistic (h) | Realistic (h) | Pessimistic (h) | Notes |\r\n|---|-----------|---------|---------------|--------------|----------------|-------|\r\n| 1 | CDS interface view — pricing conditions | ZI_MaterialPrice (A004, A005, KONV, MARA, MARC join) | 3 | 5 | 9 | SAP pricing condition tables (A004/A005) have complex key structures; join logic is non-trivial |\r\n| 2 | CDS projection view + UI annotations | ZC_MaterialPrice | 1 | 2 | 3 | Straightforward once ZI_ is correct |\r\n| 3 | Report class — selection screen and ALV setup | ZCL_PRICING_REPORT (core) | 2 | 4 | 6 | ALV with two tabs (main + history) adds complexity vs single-tab |\r\n| 4 | BAdI integration — net price calculation | ZCL_PRICING_REPORT (BAdI call section) | 1 | 3 | 7 | BAdI exists but behavior/interface not fully documented — discovery time needed |\r\n| 5 | Pricing history data provider | ZCL_PRICING_HISTORY (KONP/KONV 6-month query) | 2 | 4 | 7 | KONP is condition record header; KONV is document condition — different tables, needs clarification |\r\n| 6 | Interface for test doubles | ZIF_PRICING_DATA_PROVIDER | 0.5 | 1 | 1.5 | Interface design follows from class structure |\r\n| 7 | Executable program wiring | ZREP_MATERIAL_PRICING | 0.5 | 1 | 2 | Thin shell calling ZCL_PRICING_REPORT constructor |\r\n| 8 | Unit tests — report class | ZCL_PRICING_REPORT test class | 2 | 4 | 6 | Mock BAdI + mock data provider; positive/negative/auth cases |\r\n| 9 | Unit tests — history class | ZCL_PRICING_HISTORY test class | 1 | 3 | 5 | Mock DB layer; date boundary cases |\r\n| 10 | Transport setup and assignment | All objects to session transport | 0.5 | 1 | 2 | Includes verifying transport route and activating all objects |\r\n| 11 | Code review preparation and response | All objects | 1 | 2 | 4 | Addressing review comments; senior reviewer may have pricing domain questions |\r\n| | **TOTAL (before risk)** | | **14.5** | **30** | **52.5** | |\r\n\r\n---\r\n\r\n### Risk Adjustments\r\n\r\n| Risk Factor | Adjustment | Reason |\r\n|------------|-----------|--------|\r\n| External BAdI integration (undocumented) | +20% | ZBAdI_PRICING_DISPLAY was built by another team. Its interface, error handling, and performance behavior are not documented in the ticket. Discovery and integration debugging time is likely. |\r\n| Pricing condition table complexity | +15% | SAP pricing condition tables (A004, A005, KONP, KONV) have access-sequence-dependent key structures. The CDS join for ZI_MaterialPrice may require iteration to get condition type filtering correct. |\r\n| Dual ALV tab layout | +10% | Two-tab ALV (main + history) is less common than single-tab; layout configuration and column management doubles. |\r\n| **Total Risk** | **+45%** | Applied to realistic hours only |\r\n\r\n---\r\n\r\n### Final Estimate\r\n\r\n| Scenario | Hours | Days (8h) |\r\n|----------|-------|-----------|\r\n| Optimistic | 14.5 | 1.8 days |\r\n| Realistic (recommended) | 30 × 1.45 = **43.5 → 44h** | **5.5 days** |\r\n| Pessimistic | 52.5 × 1.45 = **76 → 76h** | **9.5 days** |\r\n\r\n**Recommended estimate: 44 hours (5.5 days)**\r\nRange: 15 – 76 hours (2 – 9.5 days)\r\n\r\nNote: The wide range reflects genuine uncertainty, primarily in the BAdI integration and pricing table join logic. The pessimistic scenario assumes significant re-work on the CDS view after discovering condition table key nuances.\r\n\r\n---\r\n\r\n### Assumptions\r\n1. The BAdI ZBAdI_PRICING_DISPLAY is already active and working in the target development system. If it needs to be created or fixed first, add that effort separately.\r\n2. A004 and A005 condition tables contain the PR00 and K007 data for the plants in scope. If a different access sequence or condition table is used in this system, the CDS join design changes.\r\n3. \"Last 6 months of price changes\" means condition records in KONP with validity dates in the last 6 months — not CDHDR/CDPOS change document history. If change document history is required, the KONP/KONV approach must be replaced and this component triples in complexity.\r\n4. ALV export to Excel is fulfilled by the standard ALV built-in spreadsheet export. No custom download button or file server path required.\r\n5. The developer has access to a development system with pricing condition data (PR00/K007) populated. Without test data, unit tests must use mocked data only and integration testing is not possible.\r\n6. Code review is a single round with one reviewer. Multiple review rounds (with significant rework) are in the pessimistic scenario but not explicitly broken out.\r\n7. Transport route DEV → QAS → PRD exists and is functional. If transport lane setup is needed, add 2–4 hours of basis coordination.\r\n\r\n---\r\n\r\n### Caveats and Re-estimate Triggers\r\n- **Out of scope:** UAT support (demonstrating report to warehouse / pricing team, handling UAT defects). Budget 4–8h separately if UAT involvement is expected.\r\n- **Out of scope:** Documentation (functional design document, user guide). Budget 4–8h separately if required.\r\n- **Out of scope:** Performance tuning for large data volumes. The estimate assumes standard query performance is acceptable. If the report must handle > 100,000 condition records with sub-10-second response time, performance analysis and potential index creation adds 8–16h.\r\n- **Re-estimate trigger:** If \"pricing history\" is clarified to mean CDHDR/CDPOS change document history instead of KONP validity dates, component 5 estimate increases from 4h to 12–20h.\r\n- **Re-estimate trigger:** If the BAdI ZBAdI_PRICING_DISPLAY has a defect or its interface does not match what this report needs, BAdI remediation is a separate ticket — but it will block this work until resolved.\r\n- **Re-estimate trigger:** If two-tab ALV is replaced by a Fiori Elements app, the entire UI layer must be redesigned (estimate increases by 20–30h for CDS annotations, service binding, Fiori testing).\r\n```\r\n",
86
93
  "sha256": "8b6a393cfdfa41113a9bfc639480cd22a9d6321bfb64d6e1db315e74c284fa3b",
87
94
  "signature": "",
88
- "signedAt": "2026-09-17T20:19:04.680Z"
95
+ "signedAt": "2026-09-28T14:37:04.499Z"
89
96
  },
90
97
  "abap-explain": {
91
98
  "name": "abap-explain",
92
- "body": "---\nname: abap-explain\ndescription: \"Understand an existing ABAP object. Default `plain` mode explains it in jargon-free language for juniors/business; `technical` mode gives a developer orientation — dependencies, tables, risk areas, modernization notes. Use when you need to understand code you didn't write. For *what breaks if I change it* use `abap-impact`; for full documentation use `abap-handover`.\"\nuser-invocable: true\n---\n\n# Skill: abap-explain\n\n## Purpose\n\nTranslate ABAP source code into plain language that any developer — or a business stakeholder — can understand without SAP or ABAP background. This skill demystifies SAP-specific constructs, explains the business logic behind the code, and produces a structured explanation with a step-by-step walkthrough and a glossary of terms. It never modifies code and never makes assumptions about missing context.\n\n## When to Use\n\n- A junior developer needs to understand what a piece of code does before modifying it\n- A business analyst or product owner needs to understand what a program actually does (versus what the spec says it does)\n- A developer from a non-SAP background (Java, Python, .NET) needs to work on ABAP code\n- You need to document what legacy code does before a refactoring effort\n- A code review needs a plain-language description attached to the ticket\n\nThis skill carries two audience modes (see **Audience Mode** below): `plain` (default — for juniors / business / non-ABAP readers) and `technical` (a developer cold-read orientation: dependencies, tables, risk areas, modernization notes — formerly `/abap-radar`).\n\nWhen NOT to use: *what breaks if I change this object* (blast radius / where-used impact) — `/abap-impact`; full documentation, a developer reference document, or a whole-package handover deliverable — `/abap-handover`.\n\n## Inputs Expected\n\nProvide ONE of the following:\n\n1. **Source code pasted directly** — full or partial ABAP source, any object type\n2. **Object name** — if MCP is available, the skill will retrieve source via `sap_get_source`\n3. **A specific section** — you may paste only a method, a LOOP block, or a subroutine and ask for that section to be explained\n\nYou may also specify the **audience mode** (`--audience plain|technical`) — see the **Audience Mode** section below. Default is `plain`.\n\nWithin `plain` mode you may further tune the reader level:\n- `audience: junior` — assumes ABAP knowledge, explains SAP domain concepts\n- `audience: non-abap-dev` (default within plain) — assumes general programming knowledge, explains ABAP and SAP concepts\n- `audience: business` — no technical assumptions, pure business language\n\nIf no audience is specified at all, `plain` mode with the `non-abap-dev` reader level is assumed as the most broadly useful default.\n\n## Audience Mode\n\nThis skill answers \"help me understand this one object\" at two depths. Pick the mode with `--audience`:\n\n| Mode | Reader | Goal | Output |\n|---|---|---|---|\n| `plain` (DEFAULT) | Junior dev / non-ABAP dev / business stakeholder | Demystify what the code does in everyday language | The plain-language walkthrough + glossary defined in the rest of this skill (unchanged) |\n| `technical` | A developer doing a cold read before touching the object | Fast orientation — what it depends on, what it touches, where the risk is, how modern it is | A developer intelligence briefing (dependencies, tables, risk areas, modernization notes) — see **Technical Mode** below |\n\n- **`plain` is the default.** If the user does not pass `--audience`, or asks for an explanation \"for a junior\", \"for the business\", \"in simple terms\", run `plain` mode exactly as documented in the rest of this skill — the **Required Behavior**, **Output Sizing**, **Output Structure**, and example below all describe `plain` mode and are unchanged.\n- **`technical`** is requested with `--audience technical`, or when the user says \"give me a developer orientation\", \"what does this depend on\", \"scan this object for risk\", \"I just inherited this code\", or invokes the legacy `/abap-radar`. It absorbs the former `abap-radar` playbook in full (below).\n- Either way the skill is **read-only** (Forge Rule 6). It reads source and analyses; it never writes.\n\n### Technical Mode (`--audience technical`) — developer orientation\n\nGoal: rapidly orient a developer on an unfamiliar object before they refactor, extend, or debug it. Given source or an object name, produce a structured intelligence briefing — what the object does, what it depends on, what business process it belongs to, where the risk areas are, and how it sits against modern ABAP standards. This mode reads and analyses only — it never modifies anything.\n\n**Behavior (run these steps in `technical` mode):**\n\n1. **Read source** — If source is pasted, use it directly. If an object name is given and MCP is available, call `sap_get_source` (using `name = toUpper(name)`; default type `PROG`, retry `CLAS` on not-found, then `sap_search_object`). Do not invent source code.\n\n2. **Identify object type and probable purpose** — Determine the ABAP object type (report, class, function module, include, CDS view, BAdI implementation, etc.) from structural cues. State the probable business purpose in 2–3 plain sentences. Mark it `[INFERRED]` if deduced from code patterns, `[CONFIRMED]` if the object has a clear title/description, or `[UNKNOWN]` if it cannot be determined.\n\n3. **Extract all dependencies** — Scan for:\n - Database tables accessed via SELECT, UPDATE, INSERT, DELETE, MODIFY\n - Function modules called via CALL FUNCTION\n - Classes instantiated or called\n - BAPIs, RFCs, and remote-enabled FMs\n - Enhancement spots, BAdIs, user exits\n - CDS views referenced\n - Message classes\n - Authorization objects in AUTHORITY-CHECK statements\n - External programs called via SUBMIT, CALL TRANSACTION, CALL SCREEN\n\n4. **Identify business process area** — Map the tables and function modules to an SAP module (SD, MM, FI, PP, WM, HR, PS, etc.). State confidence level.\n\n5. **Flag risk areas** — Identify patterns that represent quality, performance, security, or stability risks:\n - SELECT inside LOOP (N+1 queries)\n - SELECT * (unspecified field list)\n - TABLES statement (obsolete, coupling risk)\n - Missing AUTHORITY-CHECK before sensitive data access\n - Hard-coded clients, company codes, plant values\n - Unhandled sy-subrc after critical calls\n - Direct UPDATE/INSERT/DELETE on SAP-delivered tables\n - Use of obsolete statements (PERFORM, MOVE...TO, COMPUTE)\n - Missing empty-check before FOR ALL ENTRIES\n - Nested LOOPs deeper than 2 levels\n\n6. **Produce modernization notes** — Compare the code against CSPeach ABAP conventions. Note what could be modernized (OO migration, CDS view extraction, inline declarations, string templates, FILTER/REDUCE expressions, etc.). Do not rewrite the code — only observe. For actual rewrites, point to `/abap-refactor`.\n\n7. **List open questions** — Things that cannot be determined from the code alone: undocumented business logic branches, unclear parameter meanings, dependencies that could not be resolved, authorization objects that may be missing.\n\n8. **Forge Rules compliance** — Read-only (Forge Rule 6). No writes. All inferences labeled. Missing information stated explicitly — nothing invented.\n\n**Output Structure (technical mode)** — open with the mandatory `## TL;DR` block (same shape as the rest of this skill), then produce this report:\n\n```\n## Code Explanation (Technical Orientation) — [OBJECT NAME]\n\n**Object Type:** [e.g., Report / Global Class / Function Module / CDS View / BAdI Impl]\n**Analyzed:** [Paste / MCP Retrieved / Partial — first N lines only]\n\n---\n\n### Probable Purpose\n[2–3 sentence plain-language summary of what this object does.]\n[INFERRED / CONFIRMED / UNKNOWN]\n\n---\n\n### Dependencies\n\n| Type | Object | Notes |\n|------|--------|-------|\n| DB Table | VBAK | Sales order header — SELECT |\n| DB Table | VBAP | Sales order items — SELECT |\n| DB Table | KONV | Condition values — SELECT in LOOP ⚠️ |\n| Function Module | CONVERSION_EXIT_ALPHA_INPUT | Input conversion — standard FM |\n| Auth Object | V_VBAK_AAT | Authority check for order type |\n| Message Class | ZSD_MESSAGES | Custom SD messages |\n\n---\n\n### Business Process Area\n[e.g., Sales and Distribution (SD) — Order-to-Cash]\nConfidence: [High / Medium / Low] — [reason]\n\n---\n\n### Risk Areas\n\n| Severity | Pattern | Location | Detail |\n|----------|---------|----------|--------|\n| HIGH | SELECT inside LOOP | Line ~47 | SELECT on KONV inside LOOP AT lt_vbap — N+1 queries |\n| HIGH | Missing empty check | Line ~43 | FOR ALL ENTRIES without IS NOT INITIAL guard |\n| MEDIUM | SELECT * | Line ~31 | VBAK read fetches all columns — unspecified field list |\n| MEDIUM | TABLES statement | Line ~12 | Obsolete coupling — use typed parameters |\n| LOW | Hard-coded plant | Line ~88 | WERKS = '0001' — should be parameterized |\n\n---\n\n### Modernization Notes\n- [ ] Migrate TABLES statement to typed parameter passing\n- [ ] Extract KONV query from inside loop — use FOR ALL ENTRIES with empty check\n- [ ] Replace SELECT * with explicit field list\n- [ ] Consider CDS view for VBAK/VBAP join instead of ABAP-level join\n- [ ] Replace CONCATENATE with string template |...|\n- [ ] Hard-coded plant '0001' should come from selection screen or config table\n\n---\n\n### Open Questions\n1. [Question about unclear logic or missing context]\n2. [Question about a dependency that could not be resolved]\n3. [Question about authorization coverage]\n\n---\n\n### Summary Assessment\n[2–3 sentence overall verdict: is this code stable, risky, ready to touch, needs refactor before extending, etc.]\n```\n\n**Example (technical mode) — ZREP_PRICING_ANALYSIS**\n\n```\n## Code Explanation (Technical Orientation) — ZREP_PRICING_ANALYSIS\n\n**Object Type:** Report (executable program)\n**Analyzed:** Paste\n\n---\n\n### Probable Purpose\nThis report calculates a total pricing value across all standard sales orders (type TA) created today. It reads order headers, their line items, and condition records to accumulate the base price (condition type PR00) into a running total. The result is written to the screen using WRITE.\n[INFERRED] — no title or documentation found in the source.\n\n---\n\n### Dependencies\n\n| Type | Object | Notes |\n|------|--------|-------|\n| DB Table | VBAK | Sales order header — SELECT * all columns |\n| DB Table | VBAP | Sales order items — SELECT * inside LOOP ⚠️ |\n| DB Table | KONV | Condition values — SELECT * inside nested LOOP ⚠️⚠️ |\n| Auth Object | V_VBAK_AAT | Authority check for order type TA, activity 03 (display) |\n\n---\n\n### Business Process Area\nSales and Distribution (SD) — Pricing / Order-to-Cash reporting.\nConfidence: High — VBAK, VBAP, and KONV are core SD tables; condition type PR00 is the standard base price condition.\n\n---\n\n### Risk Areas\n\n| Severity | Pattern | Location | Detail |\n|----------|---------|----------|--------|\n| HIGH | SELECT inside LOOP | Line ~31 | SELECT on VBAP inside LOOP AT lt_orders — one query per order |\n| HIGH | SELECT inside nested LOOP | Line ~36 | SELECT on KONV inside LOOP AT lt_items — one query per line item. For 100 orders × 10 items = 1,000 KONV queries |\n| HIGH | SELECT * | Lines 28, 31, 36 | All three SELECTs fetch every column from VBAK, VBAP, KONV. Only a handful of fields are used |\n| MEDIUM | TABLES statement | Line 3 | TABLES: vbak, vbap — obsolete. Creates implicit global work areas and tight coupling |\n| MEDIUM | WRITE output | Line ~49 | WRITE statement used for output — not suitable for background jobs or integration |\n| LOW | Hard-coded order type | Line ~30 | AUART = 'TA' hard-coded — should be a selection screen parameter |\n| LOW | Hard-coded condition type | Line ~42 | KSCHL = 'PR00' hard-coded — should be configurable |\n| LOW | No date range selection | N/A | Only today's orders (sy-datum) — no selection screen for flexibility |\n\n---\n\n### Modernization Notes\n- [ ] Replace three-level nested SELECT-in-LOOP with a single CDS view joining VBAK, VBAP, KONV, then one SELECT into an internal table\n- [ ] Remove TABLES statement — use typed local structures\n- [ ] Replace SELECT * with explicit field lists: only VBELN, AUART, ERDAT from VBAK; VBELN, POSNR, KNUMV from VBAP; KNUMV, KPOSN, KSCHL, KWERT from KONV\n- [ ] Add selection screen with date range (ERDAT), order type (AUART), sales org (VKORG)\n- [ ] Replace WRITE output with ALV grid (CL_SALV_TABLE) for usability\n- [ ] Consider using standard FM SD_SALESDOCUMENT_READ or CDS view I_SalesOrder as the access layer instead of direct table reads\n- [ ] lv_total declared as TYPE P DECIMALS 2 — may lose precision for large values; consider DECFLOAT34 or CURR with currency key\n\n---\n\n### Open Questions\n1. Is this report run interactively or as a background job? WRITE output does not work in background — if it runs in batch, output is silently lost.\n2. What currency should lv_total be in? KONV-KWERT is in condition currency (KONV-WAERS). The code does not handle currency conversion or mixed currencies.\n3. Who consumes the total? Is this a one-off script or a production report? Its current state suggests it is not production-ready.\n4. Is authorization coverage sufficient? The check only covers order type TA with activity 03. If other order types are added later the auth check needs updating.\n\n---\n\n### Summary Assessment\nThis is a fragile legacy report with three levels of nested database queries that will perform extremely poorly at scale — 100 orders with 10 items each generates over 1,100 individual SELECT statements. It is readable and functionally correct for small datasets but must not be promoted to a high-volume system without a full rewrite of the data access layer. Before extending or modifying this program, extract the data access into a CDS view and flatten the loop structure.\n```\n\nApply all guardrails from the Guardrails section below — especially: never invent SAP objects, label all inferences, state missing information explicitly.\n\nIn `technical` mode the `plain`-mode Output Sizing table, glossary, and step-by-step walkthrough are replaced by the briefing above. The TL;DR rules and Forge-Rule read-only guarantee still apply.\n\n## Output Sizing\n\nThe level of detail MUST scale with the object size:\n\n| Object size (LOC) | Step-by-step | Glossary | What Was Not Analyzed |\n|---|---|---|---|\n| ≤ 100 | 3–4 steps | Only terms a non-ABAP dev wouldn't know AND appear in the explanation; ≤ 5 rows | Skip if everything was retrieved |\n| 100–500 | ≤ 6 steps | ≤ 8 rows, same filter | Optional |\n| > 500 | ≤ 10 steps | ≤ 12 rows, same filter | Mandatory if any object was not opened |\n\nThe TL;DR (Headline + Top 3 + Verdict) is mandatory at every size. Everything else is right-sized.\n\nAnti-pattern to avoid: a 14-row glossary on a 200-line program. The reader does not need a definition of `REPORT` and `MESSAGE TYPE 'E'` if the rest of the explanation makes them obvious from context.\n\n## Required Behavior\n\n1. **Try direct read first.** If the user provides an object name (any case), call `sap_get_source` with `name = toUpper(name)`. If `sap_get_source` returns the source, proceed to Step 2. If it returns \"not found\" OR if the user explicitly asked to discover the object (\"find the program for X\"), THEN call `sap_search_object`. This saves a tool round-trip on the common case where the user knows the exact name. If `sap_get_source` requires an object type, default to `PROG` first; on not-found, retry with `CLAS`. Only fall back to `sap_search_object` when both fail.\n\n2. **Read and parse the source** — Identify the object type, entry point, and major logical sections. Do not invent code that was not provided.\n\n3. **Write the top summary** — 2–3 sentences that explain what the code does at the business level. Avoid ABAP keywords. Use active voice. Start with \"This code...\"\n\n4. **Write the step-by-step walkthrough** — Walk through the code's execution from entry point to completion. Each step should be numbered and correspond to a recognizable section of the code (a SELECT, a LOOP, a method call, a condition, etc.). Steps should be written in plain English. ABAP syntax may be referenced in brackets for technical readers. Cap the step count per the Output Sizing table above.\n\n5. **Build the SAP Concepts Explained glossary** — Only include a term if BOTH conditions hold: (a) a non-ABAP dev wouldn't recognise it without explanation, AND (b) the term appears in the explanation above. Do NOT define general programming concepts (loops, conditions, variables). Do NOT define `REPORT`, `MESSAGE`, `COMMIT WORK`, `sy-subrc`, `sy-datum`, `sy-uzeit` unless they are central to a specific subtle behaviour. The default is to skip the glossary entirely if all terms used are obvious from context.\n\n6. **Write Key Things to Know** — 3–5 observations about the code that matter for understanding it correctly: side effects, dependencies, limitations, assumptions, or important behaviors that are not obvious from reading the steps.\n\n7. **Label all inferences.** If a business meaning is inferred from a table name or variable name rather than confirmed by a comment or documentation, mark it `[INFERRED]`.\n\n8. **State what is missing.** Only include this section if the object actually references things you did not retrieve (an external Z-class, a custom FM not visible, a CDS view referenced but not opened). If everything was retrieved cleanly, omit the section. SAP-standard classes (`CL_SALV_TABLE`, `CX_*`) are baseline knowledge and do NOT count as \"not analyzed\".\n\n9. **Forge Rules compliance.** This skill is read-only (Rule 6). No writes performed. Inferences labeled. Missing information stated.\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\nWhen using ordered lists in the output, write explicit numbers (`1.`, `2.`, `3.`) — do not rely on markdown auto-renumbering (`1.`, `1.`, `1.`). Some terminal renderers do not renumber.\n\n```\n## Code Explanation — [OBJECT NAME or brief description]\n\n**Audience:** [Junior Developer / Non-ABAP Developer / Business Stakeholder]\n**Object Type:** [Report / Method / Function Module / Class / etc.]\n\n---\n\n### What This Code Does\n[2–3 plain-language sentences. Business level, no jargon.]\n\n---\n\n### Step by Step\n\n1. **[Step name]**\n [Plain explanation of what happens. SAP/ABAP terms may appear in brackets.]\n\n2. **[Step name]**\n [Plain explanation.]\n\n...\n\n---\n\n### SAP Concepts Explained\n\n| Term | What It Means |\n|------|--------------|\n| VBAK | The database table that stores sales order headers in SAP. Each row is one sales order. |\n| AUTHORITY-CHECK | A security gate — SAP verifies that the current user has permission to perform the action before continuing. |\n| sy-subrc | A system variable SAP sets after most operations. If it equals 0, the operation succeeded. Any other value means something went wrong. |\n\n---\n\n### Key Things to Know\n\n1. [Important behavior, side effect, or limitation]\n2. [Important behavior, side effect, or limitation]\n3. [Important behavior, side effect, or limitation]\n\n---\n\n### What Was Not Analyzed\n[List any referenced objects — includes, called methods, external FMs — that were not part of the provided source. State that those sections are not covered.]\n```\n\n## Guardrails\n\n- **Never invent SAP table meanings.** If a table is not a well-known SAP standard table, state that its meaning is unknown rather than guessing.\n- **Never invent business logic.** If the code's business purpose cannot be determined from the source, say so.\n- **Label inferences clearly.** Use `[INFERRED]` when deriving meaning from naming conventions rather than documentation.\n- **Do not oversimplify to the point of being wrong.** A glossary entry for AUTHORITY-CHECK must not say \"login check\" if it is an authorization check on a specific object.\n- **Do not modify the code.** This skill is purely explanatory. If improvement opportunities are noticed, mention them in Key Things to Know (`plain` mode) or Modernization Notes (`technical` mode) but do not propose a rewrite here — use `/abap-refactor` for that.\n- **Follow the 10 Forge Rules.** Especially Rule 6 (read only).\n\n## Example Prompt\n\n```\n/abap-explain audience: non-abap-dev\n\nMETHOD get_open_sales_orders.\n\n AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'\n ID 'AUART' FIELD iv_order_type\n ID 'ACTVT' FIELD '03'.\n IF sy-subrc <> 0.\n RAISE EXCEPTION TYPE zcx_not_authorized\n EXPORTING\n textid = zcx_not_authorized=>no_display_auth\n mv_object = 'V_VBAK_AAT'.\n ENDIF.\n\n SELECT vbeln, kunnr, netwr, waers, erdat, auart\n FROM vbak\n INTO TABLE @DATA(lt_headers)\n WHERE auart = @iv_order_type\n AND vkorg = @iv_sales_org\n AND erdat BETWEEN @iv_date_from AND @iv_date_to.\n\n IF sy-subrc <> 0.\n rt_orders = VALUE #( ).\n RETURN.\n ENDIF.\n\n SELECT vbeln, posnr, matnr, kwmeng, vrkme, netwr\n FROM vbap\n INTO TABLE @DATA(lt_items)\n FOR ALL ENTRIES IN @lt_headers\n WHERE vbeln = @lt_headers-vbeln.\n\n rt_orders = VALUE #(\n FOR hdr IN lt_headers\n LET items = FILTER #( lt_items USING KEY primary_key\n WHERE vbeln = hdr-vbeln ) IN\n ( order_number = hdr-vbeln\n customer = hdr-kunnr\n net_value = hdr-netwr\n currency = hdr-waers\n created_on = hdr-erdat\n order_type = hdr-auart\n items = items ) ).\n\nENDMETHOD.\n```\n\n## Example Output Outline\n\n```\n## Code Explanation — GET_OPEN_SALES_ORDERS method\n\n**Audience:** Non-ABAP Developer\n**Object Type:** Instance Method (ABAP OO class)\n\n---\n\n### What This Code Does\nThis method retrieves a list of sales orders from the SAP database. It first checks that the user has permission to view orders, then fetches order headers and their line items within a given date range and sales organization, and returns them as a structured list.\n\n---\n\n### Step by Step\n\n1. **Permission check**\n Before reading any data, the code asks SAP: \"Does the current user have display access (activity 03) for sales orders of this type?\" This uses SAP's built-in authorization system [`AUTHORITY-CHECK`]. If the user does not have permission, the method immediately throws a \"not authorized\" error [`RAISE EXCEPTION TYPE zcx_not_authorized`] and stops — no data is returned.\n\n2. **Fetch order headers**\n The code queries the sales order header table [`VBAK`] and retrieves only six specific fields: order number, customer number, net value, currency, creation date, and order type. It filters to only orders matching the requested order type, sales organization, and date range. The results go into a temporary in-memory list [`lt_headers`].\n\n3. **Handle no results**\n If the database found no matching orders [`sy-subrc <> 0`], the method immediately returns an empty list and exits cleanly — no error, just an empty result.\n\n4. **Fetch order line items**\n For all the order headers found in step 2, the code fetches the corresponding line items from the sales order items table [`VBAP`]. It reads only six fields per item: order number, line number, material number, quantity, unit of measure, and net value. The `FOR ALL ENTRIES` technique means SAP fetches all relevant items in a single database query rather than one query per order.\n\n5. **Combine headers and items into the return structure**\n The code assembles the final return value. For each order header, it collects all matching line items (matching on order number) and packages everything into a single structured result entry. This uses a modern ABAP table-expression technique [`VALUE # ... FOR ... LET ... FILTER`] to do in one statement what older code would do with a nested LOOP.\n\n6. **Return**\n The assembled list `rt_orders` is the method's return value — it contains one entry per sales order, each entry holding the order details and its items as a nested list.\n\n---\n\n### SAP Concepts Explained\n\n| Term | What It Means |\n|------|--------------|\n| AUTHORITY-CHECK | SAP's permission gate. Before the code runs, SAP checks whether the current user's roles grant them the specific action (here: display) on the specific business object (here: sales orders). If not granted, the check fails. |\n| V_VBAK_AAT | An SAP authorization object for sales order access. It controls which users can view, create, or change sales orders of specific types. The letters stand for \"Verkaufsbeleg Kopf Auftragsart\" (Sales Document Header Order Type) in German. |\n| VBAK | The main SAP database table for sales order headers. One row = one sales order. \"V\" = sales (Verkauf), \"BAK\" = document header. |\n| VBAP | The SAP database table for sales order line items (positions). One row = one line on a sales order. \"P\" stands for Position. |\n| sy-subrc | A global system variable SAP updates after nearly every operation. 0 means success. Non-zero means the operation did not find a result or failed in some way. Always check it after database queries. |\n| FOR ALL ENTRIES IN | An SAP SQL technique that says \"run this SELECT query once for all the values in this list, instead of running one query per value.\" Efficient for batch retrieval. |\n| FILTER # | A modern ABAP expression that filters a table in memory by a condition — equivalent to `.filter()` in JavaScript or a list comprehension in Python. |\n| zcx_not_authorized | A custom exception class (indicated by the ZCX_ prefix). When raised, it signals to the caller that authorization failed. The caller is expected to handle it gracefully. |\n| auart | SAP field name for \"order type\" (Auftragsart). Examples: 'TA' = standard order, 'RE' = return, 'KR' = credit memo. |\n| vkorg | SAP field name for \"sales organization\" (Verkaufsorganisation). A sales org groups sales activities for a company. |\n\n---\n\n### Key Things to Know\n\n1. **The method fails silently on empty results, not on errors.** If no orders match the criteria, you get an empty list — not an exception. This is intentional and correct behavior for a query method.\n\n2. **Authorization is checked against the order type passed in, not all order types.** If the caller passes `iv_order_type = 'TA'`, the auth check only covers type TA. A separate call with a different order type would trigger a separate auth check. This means the method is only safe to call with a single order type at a time.\n\n3. **The `FOR ALL ENTRIES` query on VBAP could return incorrect results if lt_headers is empty.** SAP's `FOR ALL ENTRIES` with an empty driving table returns *all rows* from the target table — a dangerous behavior. This code handles that correctly by the early RETURN in step 3 (if no headers found, the method exits before reaching the VBAP query).\n\n4. **Currency handling is the caller's responsibility.** The method returns `netwr` (net value) and `waers` (currency) but does not convert values to a common currency. If orders in multiple currencies are returned, the caller must handle currency-aware aggregation.\n\n5. **This method is read-only.** It does not modify any data in SAP. It is safe to call in display transactions, background reporting, and APIs.\n\n---\n\n### What Was Not Analyzed\n- The class definition and interface that this method belongs to were not provided. The exact return type of `rt_orders` (its structure definition) is unknown — the field names used (`order_number`, `customer`, `items`, etc.) are visible in the code but the full structure definition was not analyzed.\n- The exception class `zcx_not_authorized` was referenced but not provided. Its full error text and textid constants were not analyzed.\n```\n",
93
- "sha256": "80a4d7a945ac6f95cc844f4a77c5901c3716931162714bce92d1b6a442e64ecb",
99
+ "body": "---\r\nname: abap-explain\r\ndescription: \"Understand an existing ABAP object. Default `plain` mode explains it in jargon-free language for juniors/business; `technical` mode gives a developer orientation — dependencies, tables, risk areas, modernization notes. Use when you need to understand code you didn't write. For *what breaks if I change it* use `abap-impact`; for full documentation use `abap-handover`.\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-explain\r\n\r\n## Purpose\r\n\r\nTranslate ABAP source code into plain language that any developer — or a business stakeholder — can understand without SAP or ABAP background. This skill demystifies SAP-specific constructs, explains the business logic behind the code, and produces a structured explanation with a step-by-step walkthrough and a glossary of terms. It never modifies code and never makes assumptions about missing context.\r\n\r\n## When to Use\r\n\r\n- A junior developer needs to understand what a piece of code does before modifying it\r\n- A business analyst or product owner needs to understand what a program actually does (versus what the spec says it does)\r\n- A developer from a non-SAP background (Java, Python, .NET) needs to work on ABAP code\r\n- You need to document what legacy code does before a refactoring effort\r\n- A code review needs a plain-language description attached to the ticket\r\n\r\nThis skill carries two audience modes (see **Audience Mode** below): `plain` (default — for juniors / business / non-ABAP readers) and `technical` (a developer cold-read orientation: dependencies, tables, risk areas, modernization notes — formerly `/abap-radar`).\r\n\r\nWhen NOT to use: *what breaks if I change this object* (blast radius / where-used impact) — `/abap-impact`; full documentation, a developer reference document, or a whole-package handover deliverable — `/abap-handover`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE of the following:\r\n\r\n1. **Source code pasted directly** — full or partial ABAP source, any object type\r\n2. **Object name** — if MCP is available, the skill will retrieve source via `sap_get_source`\r\n3. **A specific section** — you may paste only a method, a LOOP block, or a subroutine and ask for that section to be explained\r\n\r\nYou may also specify the **audience mode** (`--audience plain|technical`) — see the **Audience Mode** section below. Default is `plain`.\r\n\r\nWithin `plain` mode you may further tune the reader level:\r\n- `audience: junior` — assumes ABAP knowledge, explains SAP domain concepts\r\n- `audience: non-abap-dev` (default within plain) — assumes general programming knowledge, explains ABAP and SAP concepts\r\n- `audience: business` — no technical assumptions, pure business language\r\n\r\nIf no audience is specified at all, `plain` mode with the `non-abap-dev` reader level is assumed as the most broadly useful default.\r\n\r\n## Audience Mode\r\n\r\nThis skill answers \"help me understand this one object\" at two depths. Pick the mode with `--audience`:\r\n\r\n| Mode | Reader | Goal | Output |\r\n|---|---|---|---|\r\n| `plain` (DEFAULT) | Junior dev / non-ABAP dev / business stakeholder | Demystify what the code does in everyday language | The plain-language walkthrough + glossary defined in the rest of this skill (unchanged) |\r\n| `technical` | A developer doing a cold read before touching the object | Fast orientation — what it depends on, what it touches, where the risk is, how modern it is | A developer intelligence briefing (dependencies, tables, risk areas, modernization notes) — see **Technical Mode** below |\r\n\r\n- **`plain` is the default.** If the user does not pass `--audience`, or asks for an explanation \"for a junior\", \"for the business\", \"in simple terms\", run `plain` mode exactly as documented in the rest of this skill — the **Required Behavior**, **Output Sizing**, **Output Structure**, and example below all describe `plain` mode and are unchanged.\r\n- **`technical`** is requested with `--audience technical`, or when the user says \"give me a developer orientation\", \"what does this depend on\", \"scan this object for risk\", \"I just inherited this code\", or invokes the legacy `/abap-radar`. It absorbs the former `abap-radar` playbook in full (below).\r\n- Either way the skill is **read-only** (Forge Rule 6). It reads source and analyses; it never writes.\r\n\r\n### Technical Mode (`--audience technical`) — developer orientation\r\n\r\nGoal: rapidly orient a developer on an unfamiliar object before they refactor, extend, or debug it. Given source or an object name, produce a structured intelligence briefing — what the object does, what it depends on, what business process it belongs to, where the risk areas are, and how it sits against modern ABAP standards. This mode reads and analyses only — it never modifies anything.\r\n\r\n**Behavior (run these steps in `technical` mode):**\r\n\r\n1. **Read source** — If source is pasted, use it directly. If an object name is given and MCP is available, call `sap_get_source` (using `name = toUpper(name)`; default type `PROG`, retry `CLAS` on not-found, then `sap_search_object`). Do not invent source code.\r\n\r\n2. **Identify object type and probable purpose** — Determine the ABAP object type (report, class, function module, include, CDS view, BAdI implementation, etc.) from structural cues. State the probable business purpose in 2–3 plain sentences. Mark it `[INFERRED]` if deduced from code patterns, `[CONFIRMED]` if the object has a clear title/description, or `[UNKNOWN]` if it cannot be determined.\r\n\r\n3. **Extract all dependencies** — Scan for:\r\n - Database tables accessed via SELECT, UPDATE, INSERT, DELETE, MODIFY\r\n - Function modules called via CALL FUNCTION\r\n - Classes instantiated or called\r\n - BAPIs, RFCs, and remote-enabled FMs\r\n - Enhancement spots, BAdIs, user exits\r\n - CDS views referenced\r\n - Message classes\r\n - Authorization objects in AUTHORITY-CHECK statements\r\n - External programs called via SUBMIT, CALL TRANSACTION, CALL SCREEN\r\n\r\n4. **Identify business process area** — Map the tables and function modules to an SAP module (SD, MM, FI, PP, WM, HR, PS, etc.). State confidence level.\r\n\r\n5. **Flag risk areas** — Identify patterns that represent quality, performance, security, or stability risks:\r\n - SELECT inside LOOP (N+1 queries)\r\n - SELECT * (unspecified field list)\r\n - TABLES statement (obsolete, coupling risk)\r\n - Missing AUTHORITY-CHECK before sensitive data access\r\n - Hard-coded clients, company codes, plant values\r\n - Unhandled sy-subrc after critical calls\r\n - Direct UPDATE/INSERT/DELETE on SAP-delivered tables\r\n - Use of obsolete statements (PERFORM, MOVE...TO, COMPUTE)\r\n - Missing empty-check before FOR ALL ENTRIES\r\n - Nested LOOPs deeper than 2 levels\r\n\r\n6. **Produce modernization notes** — Compare the code against CSPeach ABAP conventions. Note what could be modernized (OO migration, CDS view extraction, inline declarations, string templates, FILTER/REDUCE expressions, etc.). Do not rewrite the code — only observe. For actual rewrites, point to `/abap-refactor`.\r\n\r\n7. **List open questions** — Things that cannot be determined from the code alone: undocumented business logic branches, unclear parameter meanings, dependencies that could not be resolved, authorization objects that may be missing.\r\n\r\n8. **Forge Rules compliance** — Read-only (Forge Rule 6). No writes. All inferences labeled. Missing information stated explicitly — nothing invented.\r\n\r\n**Output Structure (technical mode)** — open with the mandatory `## TL;DR` block (same shape as the rest of this skill), then produce this report:\r\n\r\n```\r\n## Code Explanation (Technical Orientation) — [OBJECT NAME]\r\n\r\n**Object Type:** [e.g., Report / Global Class / Function Module / CDS View / BAdI Impl]\r\n**Analyzed:** [Paste / MCP Retrieved / Partial — first N lines only]\r\n\r\n---\r\n\r\n### Probable Purpose\r\n[2–3 sentence plain-language summary of what this object does.]\r\n[INFERRED / CONFIRMED / UNKNOWN]\r\n\r\n---\r\n\r\n### Dependencies\r\n\r\n| Type | Object | Notes |\r\n|------|--------|-------|\r\n| DB Table | VBAK | Sales order header — SELECT |\r\n| DB Table | VBAP | Sales order items — SELECT |\r\n| DB Table | KONV | Condition values — SELECT in LOOP ⚠️ |\r\n| Function Module | CONVERSION_EXIT_ALPHA_INPUT | Input conversion — standard FM |\r\n| Auth Object | V_VBAK_AAT | Authority check for order type |\r\n| Message Class | ZSD_MESSAGES | Custom SD messages |\r\n\r\n---\r\n\r\n### Business Process Area\r\n[e.g., Sales and Distribution (SD) — Order-to-Cash]\r\nConfidence: [High / Medium / Low] — [reason]\r\n\r\n---\r\n\r\n### Risk Areas\r\n\r\n| Severity | Pattern | Location | Detail |\r\n|----------|---------|----------|--------|\r\n| HIGH | SELECT inside LOOP | Line ~47 | SELECT on KONV inside LOOP AT lt_vbap — N+1 queries |\r\n| HIGH | Missing empty check | Line ~43 | FOR ALL ENTRIES without IS NOT INITIAL guard |\r\n| MEDIUM | SELECT * | Line ~31 | VBAK read fetches all columns — unspecified field list |\r\n| MEDIUM | TABLES statement | Line ~12 | Obsolete coupling — use typed parameters |\r\n| LOW | Hard-coded plant | Line ~88 | WERKS = '0001' — should be parameterized |\r\n\r\n---\r\n\r\n### Modernization Notes\r\n- [ ] Migrate TABLES statement to typed parameter passing\r\n- [ ] Extract KONV query from inside loop — use FOR ALL ENTRIES with empty check\r\n- [ ] Replace SELECT * with explicit field list\r\n- [ ] Consider CDS view for VBAK/VBAP join instead of ABAP-level join\r\n- [ ] Replace CONCATENATE with string template |...|\r\n- [ ] Hard-coded plant '0001' should come from selection screen or config table\r\n\r\n---\r\n\r\n### Open Questions\r\n1. [Question about unclear logic or missing context]\r\n2. [Question about a dependency that could not be resolved]\r\n3. [Question about authorization coverage]\r\n\r\n---\r\n\r\n### Summary Assessment\r\n[2–3 sentence overall verdict: is this code stable, risky, ready to touch, needs refactor before extending, etc.]\r\n```\r\n\r\n**Example (technical mode) — ZREP_PRICING_ANALYSIS**\r\n\r\n```\r\n## Code Explanation (Technical Orientation) — ZREP_PRICING_ANALYSIS\r\n\r\n**Object Type:** Report (executable program)\r\n**Analyzed:** Paste\r\n\r\n---\r\n\r\n### Probable Purpose\r\nThis report calculates a total pricing value across all standard sales orders (type TA) created today. It reads order headers, their line items, and condition records to accumulate the base price (condition type PR00) into a running total. The result is written to the screen using WRITE.\r\n[INFERRED] — no title or documentation found in the source.\r\n\r\n---\r\n\r\n### Dependencies\r\n\r\n| Type | Object | Notes |\r\n|------|--------|-------|\r\n| DB Table | VBAK | Sales order header — SELECT * all columns |\r\n| DB Table | VBAP | Sales order items — SELECT * inside LOOP ⚠️ |\r\n| DB Table | KONV | Condition values — SELECT * inside nested LOOP ⚠️⚠️ |\r\n| Auth Object | V_VBAK_AAT | Authority check for order type TA, activity 03 (display) |\r\n\r\n---\r\n\r\n### Business Process Area\r\nSales and Distribution (SD) — Pricing / Order-to-Cash reporting.\r\nConfidence: High — VBAK, VBAP, and KONV are core SD tables; condition type PR00 is the standard base price condition.\r\n\r\n---\r\n\r\n### Risk Areas\r\n\r\n| Severity | Pattern | Location | Detail |\r\n|----------|---------|----------|--------|\r\n| HIGH | SELECT inside LOOP | Line ~31 | SELECT on VBAP inside LOOP AT lt_orders — one query per order |\r\n| HIGH | SELECT inside nested LOOP | Line ~36 | SELECT on KONV inside LOOP AT lt_items — one query per line item. For 100 orders × 10 items = 1,000 KONV queries |\r\n| HIGH | SELECT * | Lines 28, 31, 36 | All three SELECTs fetch every column from VBAK, VBAP, KONV. Only a handful of fields are used |\r\n| MEDIUM | TABLES statement | Line 3 | TABLES: vbak, vbap — obsolete. Creates implicit global work areas and tight coupling |\r\n| MEDIUM | WRITE output | Line ~49 | WRITE statement used for output — not suitable for background jobs or integration |\r\n| LOW | Hard-coded order type | Line ~30 | AUART = 'TA' hard-coded — should be a selection screen parameter |\r\n| LOW | Hard-coded condition type | Line ~42 | KSCHL = 'PR00' hard-coded — should be configurable |\r\n| LOW | No date range selection | N/A | Only today's orders (sy-datum) — no selection screen for flexibility |\r\n\r\n---\r\n\r\n### Modernization Notes\r\n- [ ] Replace three-level nested SELECT-in-LOOP with a single CDS view joining VBAK, VBAP, KONV, then one SELECT into an internal table\r\n- [ ] Remove TABLES statement — use typed local structures\r\n- [ ] Replace SELECT * with explicit field lists: only VBELN, AUART, ERDAT from VBAK; VBELN, POSNR, KNUMV from VBAP; KNUMV, KPOSN, KSCHL, KWERT from KONV\r\n- [ ] Add selection screen with date range (ERDAT), order type (AUART), sales org (VKORG)\r\n- [ ] Replace WRITE output with ALV grid (CL_SALV_TABLE) for usability\r\n- [ ] Consider using standard FM SD_SALESDOCUMENT_READ or CDS view I_SalesOrder as the access layer instead of direct table reads\r\n- [ ] lv_total declared as TYPE P DECIMALS 2 — may lose precision for large values; consider DECFLOAT34 or CURR with currency key\r\n\r\n---\r\n\r\n### Open Questions\r\n1. Is this report run interactively or as a background job? WRITE output does not work in background — if it runs in batch, output is silently lost.\r\n2. What currency should lv_total be in? KONV-KWERT is in condition currency (KONV-WAERS). The code does not handle currency conversion or mixed currencies.\r\n3. Who consumes the total? Is this a one-off script or a production report? Its current state suggests it is not production-ready.\r\n4. Is authorization coverage sufficient? The check only covers order type TA with activity 03. If other order types are added later the auth check needs updating.\r\n\r\n---\r\n\r\n### Summary Assessment\r\nThis is a fragile legacy report with three levels of nested database queries that will perform extremely poorly at scale — 100 orders with 10 items each generates over 1,100 individual SELECT statements. It is readable and functionally correct for small datasets but must not be promoted to a high-volume system without a full rewrite of the data access layer. Before extending or modifying this program, extract the data access into a CDS view and flatten the loop structure.\r\n```\r\n\r\nApply all guardrails from the Guardrails section below — especially: never invent SAP objects, label all inferences, state missing information explicitly.\r\n\r\nIn `technical` mode the `plain`-mode Output Sizing table, glossary, and step-by-step walkthrough are replaced by the briefing above. The TL;DR rules and Forge-Rule read-only guarantee still apply.\r\n\r\n## Output Sizing\r\n\r\nThe level of detail MUST scale with the object size:\r\n\r\n| Object size (LOC) | Step-by-step | Glossary | What Was Not Analyzed |\r\n|---|---|---|---|\r\n| ≤ 100 | 3–4 steps | Only terms a non-ABAP dev wouldn't know AND appear in the explanation; ≤ 5 rows | Skip if everything was retrieved |\r\n| 100–500 | ≤ 6 steps | ≤ 8 rows, same filter | Optional |\r\n| > 500 | ≤ 10 steps | ≤ 12 rows, same filter | Mandatory if any object was not opened |\r\n\r\nThe TL;DR (Headline + Top 3 + Verdict) is mandatory at every size. Everything else is right-sized.\r\n\r\nAnti-pattern to avoid: a 14-row glossary on a 200-line program. The reader does not need a definition of `REPORT` and `MESSAGE TYPE 'E'` if the rest of the explanation makes them obvious from context.\r\n\r\n## Required Behavior\r\n\r\n1. **Try direct read first.** If the user provides an object name (any case), call `sap_get_source` with `name = toUpper(name)`. If `sap_get_source` returns the source, proceed to Step 2. If it returns \"not found\" OR if the user explicitly asked to discover the object (\"find the program for X\"), THEN call `sap_search_object`. This saves a tool round-trip on the common case where the user knows the exact name. If `sap_get_source` requires an object type, default to `PROG` first; on not-found, retry with `CLAS`. Only fall back to `sap_search_object` when both fail.\r\n\r\n2. **Read and parse the source** — Identify the object type, entry point, and major logical sections. Do not invent code that was not provided.\r\n\r\n3. **Write the top summary** — 2–3 sentences that explain what the code does at the business level. Avoid ABAP keywords. Use active voice. Start with \"This code...\"\r\n\r\n4. **Write the step-by-step walkthrough** — Walk through the code's execution from entry point to completion. Each step should be numbered and correspond to a recognizable section of the code (a SELECT, a LOOP, a method call, a condition, etc.). Steps should be written in plain English. ABAP syntax may be referenced in brackets for technical readers. Cap the step count per the Output Sizing table above.\r\n\r\n5. **Build the SAP Concepts Explained glossary** — Only include a term if BOTH conditions hold: (a) a non-ABAP dev wouldn't recognise it without explanation, AND (b) the term appears in the explanation above. Do NOT define general programming concepts (loops, conditions, variables). Do NOT define `REPORT`, `MESSAGE`, `COMMIT WORK`, `sy-subrc`, `sy-datum`, `sy-uzeit` unless they are central to a specific subtle behaviour. The default is to skip the glossary entirely if all terms used are obvious from context.\r\n\r\n6. **Write Key Things to Know** — 3–5 observations about the code that matter for understanding it correctly: side effects, dependencies, limitations, assumptions, or important behaviors that are not obvious from reading the steps.\r\n\r\n7. **Label all inferences.** If a business meaning is inferred from a table name or variable name rather than confirmed by a comment or documentation, mark it `[INFERRED]`.\r\n\r\n8. **State what is missing.** Only include this section if the object actually references things you did not retrieve (an external Z-class, a custom FM not visible, a CDS view referenced but not opened). If everything was retrieved cleanly, omit the section. SAP-standard classes (`CL_SALV_TABLE`, `CX_*`) are baseline knowledge and do NOT count as \"not analyzed\".\r\n\r\n9. **Forge Rules compliance.** This skill is read-only (Rule 6). No writes performed. Inferences labeled. Missing information stated.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\nWhen using ordered lists in the output, write explicit numbers (`1.`, `2.`, `3.`) — do not rely on markdown auto-renumbering (`1.`, `1.`, `1.`). Some terminal renderers do not renumber.\r\n\r\n```\r\n## Code Explanation — [OBJECT NAME or brief description]\r\n\r\n**Audience:** [Junior Developer / Non-ABAP Developer / Business Stakeholder]\r\n**Object Type:** [Report / Method / Function Module / Class / etc.]\r\n\r\n---\r\n\r\n### What This Code Does\r\n[2–3 plain-language sentences. Business level, no jargon.]\r\n\r\n---\r\n\r\n### Step by Step\r\n\r\n1. **[Step name]**\r\n [Plain explanation of what happens. SAP/ABAP terms may appear in brackets.]\r\n\r\n2. **[Step name]**\r\n [Plain explanation.]\r\n\r\n...\r\n\r\n---\r\n\r\n### SAP Concepts Explained\r\n\r\n| Term | What It Means |\r\n|------|--------------|\r\n| VBAK | The database table that stores sales order headers in SAP. Each row is one sales order. |\r\n| AUTHORITY-CHECK | A security gate — SAP verifies that the current user has permission to perform the action before continuing. |\r\n| sy-subrc | A system variable SAP sets after most operations. If it equals 0, the operation succeeded. Any other value means something went wrong. |\r\n\r\n---\r\n\r\n### Key Things to Know\r\n\r\n1. [Important behavior, side effect, or limitation]\r\n2. [Important behavior, side effect, or limitation]\r\n3. [Important behavior, side effect, or limitation]\r\n\r\n---\r\n\r\n### What Was Not Analyzed\r\n[List any referenced objects — includes, called methods, external FMs — that were not part of the provided source. State that those sections are not covered.]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Never invent SAP table meanings.** If a table is not a well-known SAP standard table, state that its meaning is unknown rather than guessing.\r\n- **Never invent business logic.** If the code's business purpose cannot be determined from the source, say so.\r\n- **Label inferences clearly.** Use `[INFERRED]` when deriving meaning from naming conventions rather than documentation.\r\n- **Do not oversimplify to the point of being wrong.** A glossary entry for AUTHORITY-CHECK must not say \"login check\" if it is an authorization check on a specific object.\r\n- **Do not modify the code.** This skill is purely explanatory. If improvement opportunities are noticed, mention them in Key Things to Know (`plain` mode) or Modernization Notes (`technical` mode) but do not propose a rewrite here — use `/abap-refactor` for that.\r\n- **Follow the 10 Forge Rules.** Especially Rule 6 (read only).\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-explain audience: non-abap-dev\r\n\r\nMETHOD get_open_sales_orders.\r\n\r\n AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'\r\n ID 'AUART' FIELD iv_order_type\r\n ID 'ACTVT' FIELD '03'.\r\n IF sy-subrc <> 0.\r\n RAISE EXCEPTION TYPE zcx_not_authorized\r\n EXPORTING\r\n textid = zcx_not_authorized=>no_display_auth\r\n mv_object = 'V_VBAK_AAT'.\r\n ENDIF.\r\n\r\n SELECT vbeln, kunnr, netwr, waers, erdat, auart\r\n FROM vbak\r\n INTO TABLE @DATA(lt_headers)\r\n WHERE auart = @iv_order_type\r\n AND vkorg = @iv_sales_org\r\n AND erdat BETWEEN @iv_date_from AND @iv_date_to.\r\n\r\n IF sy-subrc <> 0.\r\n rt_orders = VALUE #( ).\r\n RETURN.\r\n ENDIF.\r\n\r\n SELECT vbeln, posnr, matnr, kwmeng, vrkme, netwr\r\n FROM vbap\r\n INTO TABLE @DATA(lt_items)\r\n FOR ALL ENTRIES IN @lt_headers\r\n WHERE vbeln = @lt_headers-vbeln.\r\n\r\n rt_orders = VALUE #(\r\n FOR hdr IN lt_headers\r\n LET items = FILTER #( lt_items USING KEY primary_key\r\n WHERE vbeln = hdr-vbeln ) IN\r\n ( order_number = hdr-vbeln\r\n customer = hdr-kunnr\r\n net_value = hdr-netwr\r\n currency = hdr-waers\r\n created_on = hdr-erdat\r\n order_type = hdr-auart\r\n items = items ) ).\r\n\r\nENDMETHOD.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Code Explanation — GET_OPEN_SALES_ORDERS method\r\n\r\n**Audience:** Non-ABAP Developer\r\n**Object Type:** Instance Method (ABAP OO class)\r\n\r\n---\r\n\r\n### What This Code Does\r\nThis method retrieves a list of sales orders from the SAP database. It first checks that the user has permission to view orders, then fetches order headers and their line items within a given date range and sales organization, and returns them as a structured list.\r\n\r\n---\r\n\r\n### Step by Step\r\n\r\n1. **Permission check**\r\n Before reading any data, the code asks SAP: \"Does the current user have display access (activity 03) for sales orders of this type?\" This uses SAP's built-in authorization system [`AUTHORITY-CHECK`]. If the user does not have permission, the method immediately throws a \"not authorized\" error [`RAISE EXCEPTION TYPE zcx_not_authorized`] and stops — no data is returned.\r\n\r\n2. **Fetch order headers**\r\n The code queries the sales order header table [`VBAK`] and retrieves only six specific fields: order number, customer number, net value, currency, creation date, and order type. It filters to only orders matching the requested order type, sales organization, and date range. The results go into a temporary in-memory list [`lt_headers`].\r\n\r\n3. **Handle no results**\r\n If the database found no matching orders [`sy-subrc <> 0`], the method immediately returns an empty list and exits cleanly — no error, just an empty result.\r\n\r\n4. **Fetch order line items**\r\n For all the order headers found in step 2, the code fetches the corresponding line items from the sales order items table [`VBAP`]. It reads only six fields per item: order number, line number, material number, quantity, unit of measure, and net value. The `FOR ALL ENTRIES` technique means SAP fetches all relevant items in a single database query rather than one query per order.\r\n\r\n5. **Combine headers and items into the return structure**\r\n The code assembles the final return value. For each order header, it collects all matching line items (matching on order number) and packages everything into a single structured result entry. This uses a modern ABAP table-expression technique [`VALUE # ... FOR ... LET ... FILTER`] to do in one statement what older code would do with a nested LOOP.\r\n\r\n6. **Return**\r\n The assembled list `rt_orders` is the method's return value — it contains one entry per sales order, each entry holding the order details and its items as a nested list.\r\n\r\n---\r\n\r\n### SAP Concepts Explained\r\n\r\n| Term | What It Means |\r\n|------|--------------|\r\n| AUTHORITY-CHECK | SAP's permission gate. Before the code runs, SAP checks whether the current user's roles grant them the specific action (here: display) on the specific business object (here: sales orders). If not granted, the check fails. |\r\n| V_VBAK_AAT | An SAP authorization object for sales order access. It controls which users can view, create, or change sales orders of specific types. The letters stand for \"Verkaufsbeleg Kopf Auftragsart\" (Sales Document Header Order Type) in German. |\r\n| VBAK | The main SAP database table for sales order headers. One row = one sales order. \"V\" = sales (Verkauf), \"BAK\" = document header. |\r\n| VBAP | The SAP database table for sales order line items (positions). One row = one line on a sales order. \"P\" stands for Position. |\r\n| sy-subrc | A global system variable SAP updates after nearly every operation. 0 means success. Non-zero means the operation did not find a result or failed in some way. Always check it after database queries. |\r\n| FOR ALL ENTRIES IN | An SAP SQL technique that says \"run this SELECT query once for all the values in this list, instead of running one query per value.\" Efficient for batch retrieval. |\r\n| FILTER # | A modern ABAP expression that filters a table in memory by a condition — equivalent to `.filter()` in JavaScript or a list comprehension in Python. |\r\n| zcx_not_authorized | A custom exception class (indicated by the ZCX_ prefix). When raised, it signals to the caller that authorization failed. The caller is expected to handle it gracefully. |\r\n| auart | SAP field name for \"order type\" (Auftragsart). Examples: 'TA' = standard order, 'RE' = return, 'KR' = credit memo. |\r\n| vkorg | SAP field name for \"sales organization\" (Verkaufsorganisation). A sales org groups sales activities for a company. |\r\n\r\n---\r\n\r\n### Key Things to Know\r\n\r\n1. **The method fails silently on empty results, not on errors.** If no orders match the criteria, you get an empty list — not an exception. This is intentional and correct behavior for a query method.\r\n\r\n2. **Authorization is checked against the order type passed in, not all order types.** If the caller passes `iv_order_type = 'TA'`, the auth check only covers type TA. A separate call with a different order type would trigger a separate auth check. This means the method is only safe to call with a single order type at a time.\r\n\r\n3. **The `FOR ALL ENTRIES` query on VBAP could return incorrect results if lt_headers is empty.** SAP's `FOR ALL ENTRIES` with an empty driving table returns *all rows* from the target table — a dangerous behavior. This code handles that correctly by the early RETURN in step 3 (if no headers found, the method exits before reaching the VBAP query).\r\n\r\n4. **Currency handling is the caller's responsibility.** The method returns `netwr` (net value) and `waers` (currency) but does not convert values to a common currency. If orders in multiple currencies are returned, the caller must handle currency-aware aggregation.\r\n\r\n5. **This method is read-only.** It does not modify any data in SAP. It is safe to call in display transactions, background reporting, and APIs.\r\n\r\n---\r\n\r\n### What Was Not Analyzed\r\n- The class definition and interface that this method belongs to were not provided. The exact return type of `rt_orders` (its structure definition) is unknown — the field names used (`order_number`, `customer`, `items`, etc.) are visible in the code but the full structure definition was not analyzed.\r\n- The exception class `zcx_not_authorized` was referenced but not provided. Its full error text and textid constants were not analyzed.\r\n```\r\n",
100
+ "sha256": "8c2e00b081b95ceb0ebddaec9ed9f07a52c85e49c9c9504d83b2ec8d9b4ece1c",
94
101
  "signature": "",
95
- "signedAt": "2026-09-17T20:19:04.726Z"
102
+ "signedAt": "2026-09-28T14:37:04.527Z"
96
103
  },
97
104
  "abap-extend-model": {
98
105
  "name": "abap-extend-model",
99
106
  "body": "---\r\nname: abap-extend-model\r\ndescription: >\r\n Add a field or @UI column to an EXISTING custom CDS view or DDLX metadata\r\n extension in place — without rebuilding the stack. Also adds validation,\r\n determination, or action stubs to an existing managed RAP BO's behavior\r\n definition; exposes an existing action on a projection BDEF (projection-use);\r\n or adds a Fiori Elements toolbar button for an action via an MDE annotation\r\n (mde-action-button). Use when the user wants to \"add a column\", \"add a field\",\r\n or \"show X in my app/list\" on an existing custom RAP/CDS model, wants to\r\n \"add a ValidateStatus validation\", \"add a DetermineAmount determination\", or\r\n \"add an Approve action stub\" to a RAP BO, wants to \"expose the Approve action\r\n in my projection\", or wants to \"add a toolbar button for Cancel in my list\r\n report\". Anchored insertion (never blind source editing), snapshot,\r\n syntax-check, activate, and republish-if-needed.\r\n For a NEW RAP stack use /abap-rap; for ABAP class/report code use\r\n /abap-refactor; for SAP-standard extension use /abap-enhance.\r\nphase: BUILD\r\nrequires_mcp: write\r\nforge_rules: [7, 8, 9, 10]\r\nversion: \"1.1\"\r\n---\r\n\r\n# abap-extend-model\r\n\r\n## Purpose\r\n\r\nMake a small, surgical addition to an **existing custom** data model and let it\r\nflow up to the running app: add a field to a CDS projection view, or add an\r\n`@UI.lineItem` column to a DDLX metadata extension, then write → syntax-check →\r\nactivate → republish the service binding if the exposed field set changed.\r\n\r\nThis closes the gap where `/abap-rap` (new stacks only), `/abap-enhance` (SAP\r\nstandard only), and `/abap-refactor` (ABAP code only) each refuse the job: none\r\nof them edit an existing custom CDS/BDEF/DDLX in place. This skill does.\r\n\r\nFor Fiori **Elements** apps the column-add is *pure backend* — add the field +\r\nits `@UI.lineItem` annotation, reactivate, republish; the app re-renders from\r\nthe new metadata with **no UI file changed**.\r\n\r\n## When to Use\r\n\r\n- \"Add a Priority column to my maintenance-request app / list report\"\r\n- \"Expose CreatedBy in the ZC_* projection\"\r\n- \"Show the NetAmount field in the service\"\r\n- \"Make NetAmount a column\" / \"promote an already-exposed field to a list\r\n column\" — when the field is already in the CDS projection but not yet a Fiori\r\n Elements column (use `cds-lineitem`).\r\n- \"Add a field to my table / expose it through the BDEF\" — when the new field\r\n needs a **new database field** (use the B2a table→CDS→BDEF chain below).\r\n- \"Add a column that needs a new database field\" — the field does not exist in\r\n the table yet, so a `table-field` add must precede the CDS/BDEF exposure.\r\n- \"Add a ValidateStatus validation / DetermineAmount determination / Approve\r\n action stub to my RAP BO\" — when the developer wants the BDEF declaration +\r\n empty method shell now and will fill in the EML logic later with `/abap-eml`\r\n (use the B2b `bdef-stub` chain below).\r\n- \"Expose the Approve action in my projection so Fiori Elements can call it\" —\r\n inserting `use action Approve;` into a projection BDEF (use `projection-use`).\r\n- \"Add an Approve toolbar button to my List Report\" — adding a `#FOR_ACTION`\r\n annotation to the DDLX metadata extension (use `mde-action-button`). These\r\n two kinds together complete the action→button chain that `bdef-stub` defers.\r\n- Any in-place add of a field/column to an existing **custom (Z/Y)** CDS view or\r\n DDLX metadata extension.\r\n\r\nDo NOT use this skill if:\r\n\r\n- The object is **SAP standard** — use `/abap-enhance`.\r\n- You are creating a **new** RAP stack or CDS view — use `/abap-rap` or\r\n `/abap-generate`.\r\n- The change is to **ABAP class/report code** (a method, a FORM) — use\r\n `/abap-refactor`.\r\n- The field needs **draft-specific** treatment, or a **truly custom-content\r\n Fiori column** (one needing a UI fragment/controller) — draft handling is\r\n heavier backend work, and custom-content columns are `fiori_fe_extend`\r\n territory in `/abap-fiori-build`, not an in-place annotation edit. Surface this\r\n and stop rather than guessing.\r\n - Note: promoting an **already-exposed** field to a list column *is* in scope —\r\n use `cds-lineitem` (see Inputs Expected). **Value-help wiring** (`cds-valuehelp`)\r\n and **computed criticality columns** (a `cds-field` insert paired with\r\n `ddlx-datapoint`) are now in scope too — see the Vocabulary modes section.\r\n Only draft handling and custom-content Fiori columns remain out of scope.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Target object **name** and **type** (`DDLS` for a CDS view, `DDLX` for a\r\n metadata extension)\r\n- What to add: the **field/element name** (and, for a column, the `@UI.lineItem`\r\n **position**)\r\n- The **kind** of edit:\r\n - `cds-field` — add an element to a CDS view's element list\r\n - `ddlx-lineitem` — add an `@UI.lineItem`-annotated field (a Fiori Elements\r\n column) to a DDLX metadata extension\r\n - `cds-lineitem` — promote an existing exposed field of a CDS view to a Fiori\r\n Elements list column (insert `@UI.lineItem` above its declaration)\r\n - `table-field` — append a **non-key** field to a CDS `define table` body\r\n (the first leg of the B2a chain; key fields are refused — destructive)\r\n - `bdef-field` — add a line to a **managed** behavior definition:\r\n - `target: field` (default) — a `field ( … ) Name;` declaration in the\r\n entity body\r\n - `target: mapping` — a `Name = column;` line in the entity's mapping block\r\n - `alias` — the behavior entity alias to extend; required only when the\r\n BDEF defines more than one behavior\r\n - `projection-use` — expose an action declared in a **base BDEF** inside the\r\n matching **projection BDEF**, by inserting `use action <Name>;` into the\r\n projection's entity body. Used as the second `behavior` phase of an\r\n action→button chain (change-type #4 in `abap-plan` revision mode). Requires:\r\n - `useClause`: the clause to expose, e.g. `\"action Approve\"` (no leading `use`, no trailing `;`)\r\n - `alias`: the projection behavior entity alias (required when the projection\r\n BDEF defines more than one behavior)\r\n - `mde-action-button` — add a Fiori Elements **List-Report toolbar button** for\r\n an exposed action by inserting `@UI.lineItem: [{ type: #FOR_ACTION, ... }]`\r\n above an existing field in a **DDLX metadata extension**. Used as the final\r\n `data-model` phase of an action→button chain (change-type #4 in `abap-plan`\r\n revision mode). This kind renders a **List-Report toolbar button**\r\n (`@UI.lineItem #FOR_ACTION`); the Object-Page action button\r\n (`@UI.identification #FOR_ACTION`) is covered by the `ddlx-identification`\r\n mode (with `forAction`). Requires:\r\n - `action`: the bare action name (the `dataAction` token in the\r\n annotation, e.g. `\"Approve\"`)\r\n - `anchorField`: an existing, exposed, non-`@UI.hidden` field in the\r\n projection — the annotation is inserted above this field's annotation block\r\n - `label`: button label text (optional; defaults to the action name)\r\n - `position`: integer column order for the `@UI.lineItem` entry (optional;\r\n defaults to an appropriate position after discovery)\r\n - `bdef-stub` — add a **validation / determination / action stub** (BDEF\r\n declaration + empty method shell in the behavior pool CCIMP) to an existing\r\n managed RAP BO; logic is deferred to a later `/abap-eml` session. Requires:\r\n - `stubType`: `validation` | `determination` | `action`\r\n - `name`: stub name (e.g. `ValidateStatus`, `DetermineAmount`, `Approve`)\r\n - `alias`: behavior entity alias (required when BDEF has multiple behaviors)\r\n - `trigger` (validation/determination): the trigger clause\r\n - `featureControlled` (action): `true` | `false`\r\n\r\nOptional / derived:\r\n- The **service binding** name (`SRVB`) to republish, if the change alters the\r\n exposed field set of a published service\r\n- The session transport (Forge Rule 9)\r\n\r\nWhen dispatched from `/abap-plan` (revision mode), these arrive in the phase\r\nenvelope — use them without asking.\r\n\r\n## Required Behavior\r\n\r\n> **Single field / single stub per run.** This skill adds ONE field or ONE stub.\r\n> For a single-object edit (`cds-field`, `ddlx-lineitem`, or `cds-lineitem`\r\n> against one CDS/DDLX object) that means ONE object. There are two multi-object\r\n> exceptions, each with their own Rule 8 plan/confirm gate:\r\n> - **B2a table→CDS→BDEF chain:** one new field carried across three objects in\r\n> sequence.\r\n> - **B2b bdef-stub:** one new stub declaration (BDEF) + one empty method shell\r\n> (CCIMP) across two objects.\r\n> To add several fields or stubs, run the skill once per item (or let\r\n> `/abap-plan` sequence them). This keeps the snapshot/diff/verify chain tight\r\n> and auditable.\r\n\r\n### B2a — table → CDS → BDEF chain (one new field that needs a new DB field)\r\n\r\nWhen the requested field does **not exist on the database table yet**, exposing\r\nit is not a single-object edit — the column must first be added to the table,\r\nthen to the CDS layer, then exposed in the BDEF. This skill performs that\r\n**chain for ONE field** as a unit.\r\n\r\n**Capability precheck (before generating any BDEF syntax):** confirm the system\r\nsupports managed RAP — call `system_capability('rap.managed')` **and**\r\n`system_capability('rap.behaviorDefinition')`. If either is `no`, do NOT attempt\r\nthe BDEF leg: stop after the table+CDS legs (or not at all) and tell the user the\r\nrelease cannot host a managed behavior definition.\r\n\r\n**Managed BDEF only.** If the target BDEF is `unmanaged`, do NOT run the BDEF\r\nleg — `bdef-field` will refuse it. Note this to the user and stop (or, if the new\r\ncolumn only needs to surface in a read-only CDS, run just the table+CDS legs).\r\n\r\n**Table add is non-key, at the end, only.** A `table-field` add appends one\r\nnon-key field. Refuse any request to add a key, reorder fields, or change a\r\ntype — those are destructive DDIC table conversions, out of scope here.\r\n\r\n**Plan/confirm gate (Forge Rule 8).** Present the full **3-object plan** before\r\ntouching anything, then wait for explicit `y`:\r\n\r\n```\r\nPlan — add field <Field> (new DB field) across the chain. This modifies 3 SAP objects:\r\n1. <ZTABLE> (TABL) — add non-key field (table-field) → activate\r\n2. <ZI_/ZC_> (DDLS) — add element (cds-field) → activate\r\n [+ <ZC_> (DDLS) — promote to column (cds-lineitem) → activate] (if it should be a list column)\r\n3. <ZR_…_TP> (BDEF) — expose field (bdef-field) → activate\r\nProceed? [y/n]\r\n```\r\n\r\n**Execution order** (snapshot → write → syntax-check → activate → verify at each\r\nstep, exactly as Steps 3–7):\r\n\r\n1. **Table** — `extend_model_insert kind: table-field` → write → syntax-check →\r\n `sap_activate` → verify. The CDS layer cannot see a column the table does not\r\n have active, so the table MUST be active before step 2.\r\n2. **CDS** — `extend_model_insert kind: cds-field` on the interface/projection\r\n view → write → syntax-check → activate → verify. If the field should also be a\r\n list column, follow with `kind: cds-lineitem` (or `ddlx-lineitem` on the DDLX)\r\n → activate.\r\n3. **BDEF** — `extend_model_insert kind: bdef-field` (`target: field`, plus a\r\n second call with `target: mapping` if the managed mapping block needs the new\r\n `Element = column` line) → write → syntax-check → activate → verify.\r\n4. **Republish** the service binding if the exposed field set changed (Step 7c).\r\n\r\n**Rollback across the chain (extends Step 8).** Keep the Step-3 snapshot of\r\n**every** object you touch. On ANY failure at ANY step, restore **all** objects\r\nmodified so far from their snapshots (in reverse order — BDEF, then CDS, then\r\ntable), reactivate each prior version, and report the chain state plainly: which\r\nobjects were reverted, which were never touched, and whether the service is back\r\nto its prior published metadata. Never leave a partially-extended stack.\r\n\r\n### B2b — bdef-stub (add a validation / determination / action stub to an existing managed RAP BO)\r\n\r\nWhen the developer wants to add a new **validation**, **determination**, or\r\n**action** to an existing managed RAP business object — but wants the actual EML\r\nlogic deferred to a later `/abap-eml` session — this skill generates the stub\r\ndeclarations in the BDEF and the empty method shells in the behavior pool class.\r\nThe stub is backend-only: surfacing an action as a Fiori button (projection\r\n`use action` + metadata extension) is multi-layer work owned by Pillar C, not\r\nthis stub.\r\n\r\n**Capability precheck.** Confirm `system_capability('rap.managed')` returns\r\n`yes` before generating any stub syntax. If not, refuse and explain the release\r\ncannot host a managed behavior definition.\r\n\r\n**Preconditions — resolve the behavior pool before touching anything:**\r\n\r\n1. Read the BDEF source with `sap_get_source` to identify the behavior entity\r\n alias and confirm the BO is managed (not unmanaged — `bdef-stub` refuses\r\n unmanaged BDEFs).\r\n2. From the BDEF, extract the behavior pool class name (the `implementation in\r\n class <ClassName>` clause). If the BDEF does not name a pool class, refuse\r\n and point the user to `/abap-rap` to set one up first.\r\n3. **Verify the pool global class actually exists** — call `sap_get_source` for\r\n the class (type `CLAS`). If the call returns an error or the class is missing,\r\n refuse with a `/abap-rap` pointer: the behavior pool must exist before stubs\r\n can be appended to it.\r\n4. Read the CCIMP (local types include) with `sap_class_includes` followed by\r\n `sap_get_source` on the `locals_imp` segment. This is the source the tool will\r\n extend: it contains the `lhc_<Alias>` local handler class.\r\n\r\n**Plan/confirm gate (Forge Rule 8).** Present the plan before touching anything:\r\n\r\n```\r\nPlan — add <stub type> stub '<Name>' to managed RAP BO. This modifies 2 SAP objects:\r\n1. <ZBDEF_…> (BDEF) — insert <validation|determination|action> declaration → write → activate\r\n2. <ZBP_…> (CLAS) — append empty method shell to lhc_<Alias> in CCIMP → write → activate\r\nProceed? [y/n]\r\n```\r\n\r\nWait for explicit `y` before proceeding.\r\n\r\n**Execution chain** (Forge Rules 7, 9, 10 apply at each step):\r\n\r\n1. **Snapshot** both objects — `sap_snapshot_take` for the BDEF (type `BDEF`)\r\n and for the class (type `CLAS`). Verify both snapshots succeed. **If either\r\n snapshot fails, do NOT proceed** (Rule 7 has no exceptions).\r\n\r\n2. **Generate modified sources** — call `extend_model_insert` with\r\n `kind: bdef-stub`. Pass:\r\n - `bdefSource`: the source returned in the precondition BDEF read\r\n - `ccimpSource`: the CCIMP source read in precondition step 4\r\n - `stubType`: `validation` | `determination` | `action`\r\n - `name`: the stub name (e.g. `ValidateStatus`, `DetermineAmount`, `Approve`)\r\n - `alias`: the behavior entity alias (required when the BDEF defines more than\r\n one behavior entity)\r\n - `trigger` (for validation/determination): the trigger clause (e.g.\r\n `on save { create; update; }` or `on modify { field Status; }`)\r\n - `featureControlled` (for action): `true` | `false`\r\n\r\n The tool returns `{ modifiedBdef, modifiedCcimp, summary, createdHandlerClass }`.\r\n `createdHandlerClass` is set only when the CCIMP had no existing `lhc_<Alias>`\r\n handler class and the tool generated a new skeleton class — use it in the\r\n rollback branch (see below).\r\n\r\n3. **Write BDEF** — call `sap_set_source` with the `modifiedBdef` source, assign\r\n to the CSPeach session transport. Then `sap_syntax_check` — stop if errors,\r\n do not continue to activation. Then `sap_activate` and confirm with\r\n `sap_inactive_objects` that the BDEF is active. **Activation is the only\r\n reliable BDEF guard** — never skip it, even for a \"just a declaration\" change.\r\n\r\n4. **Write CCIMP** — call `sap_set_class_include` with segment `implementations`\r\n (not `locals_imp` — the ADT URL segment is `implementations`), the\r\n `modifiedCcimp` source, and the session transport. Then `sap_syntax_check` on\r\n the class. Then `sap_activate` and verify with `sap_inactive_objects`.\r\n\r\n**Feature-controlled actions — warn the developer.** When `featureControlled:\r\ntrue` is passed, the tool inserts a `get_instance_features` stub alongside the\r\naction method. That stub is an **unfilled no-op** — it returns `REQUESTED` for\r\nthe action feature key, which means the action is always enabled. The developer\r\nMUST implement real feature logic before shipping. Additionally, a **second**\r\naction declared with `( features: instance )` in the same BDEF is NOT\r\nauto-wired to the same `get_instance_features` — each feature-controlled action\r\nrequires its own wiring. Surface both warnings in the report.\r\n\r\n**Rollback on any failure.** If any step fails (snapshot, write, syntax, or\r\nactivation), restore ALL objects that were already modified from their snapshots:\r\n\r\n- If the CCIMP step fails after the BDEF was already written and activated,\r\n restore the BDEF from its snapshot first (reverse order), then report.\r\n- If `createdHandlerClass` is set (the tool generated a new skeleton class), and\r\n the overall chain fails, **remove** that skeleton class with\r\n `sap_delete_object` — a half-created handler class is worse than no class.\r\n Use `createdHandlerClass` from the tool result to decide whether to delete.\r\n- Report the chain state plainly: which objects were reverted or removed, which\r\n were never touched.\r\n\r\n### C1a — projection-use (expose an action in a projection BDEF)\r\n\r\nWhen the phase kind is `projection-use`, the goal is to insert `use action <Name>;` into the entity body of a **projection** BDEF — the second `behavior` leg of an action→button chain (change-type #4). The base BDEF's action declaration is the prerequisite (the prior `behavior` phase in the revision plan must already be validated).\r\n\r\n**Preconditions — resolve both BDEFs before touching anything:**\r\n\r\n1. Read the **projection BDEF** source with `sap_get_source`. Confirm it is a projection BDEF — the source must contain a top-level `projection;` keyword. If it does not (i.e. it is a base BDEF), refuse: this kind only edits projection BDEFs.\r\n2. Read the **base BDEF** source with `sap_get_source` to confirm the action named in `useClause` (e.g. `\"action Approve\"`) is declared there. If the action is not found, the prerequisite phase did not complete correctly — block and report; do not proceed.\r\n3. **Idempotency check:** scan the projection BDEF source for any existing `use … action <Name>` clause matching the action name from `useClause` (match the name itself, not the exact line — a grouped or feature-qualified `use` is still a duplicate). If found, the action is already exposed; report the finding and stop without writing.\r\n\r\n**Plan/confirm gate (Forge Rule 8).** Present the plan and wait for explicit `y`:\r\n\r\n```\r\nPlan — expose action '<Name>' in projection BDEF. This modifies 1 SAP object:\r\n1. <ZC_…_TP> (BDEF) — insert 'use action <Name>;' in projection entity body → write → activate\r\nProceed? [y/n]\r\n```\r\n\r\n**Execution chain:**\r\n\r\n1. **Snapshot** the projection BDEF with `sap_snapshot_take`. Verify it succeeded. If it fails, do not proceed (Rule 7).\r\n2. Call `extend_model_insert` with `kind: projection-use`, the projection BDEF source, `useClause` (e.g. `\"action Approve\"`), and `alias` if provided. The tool inserts `use action <Name>;` anchored inside the projection behavior entity body (`define behavior for ZC_… { … }`). It returns `{ modified, summary }`.\r\n3. **Write** the modified source via `sap_set_source` → **syntax-check** via `sap_syntax_check` (stop on errors; do not activate) → **activate** via `sap_activate` → **verify** via `sap_inactive_objects`.\r\n4. **Rollback on any failure:** restore the pre-change source from the Step-1 snapshot, reactivate the prior version, and confirm it is active again. Report the chain state plainly.\r\n\r\nNo republish is triggered by a projection BDEF change alone; republish (if needed) is owned by the subsequent `mde-action-button` phase's exit gate.\r\n\r\n### C1b — mde-action-button (FE List-Report toolbar button for an action)\r\n\r\nWhen the phase kind is `mde-action-button`, the goal is to insert a `@UI.lineItem` `#FOR_ACTION` entry above an existing field in a **DDLX metadata extension** — the final `data-model` leg of an action→button chain (change-type #4), producing a Fiori Elements List-Report toolbar button. This is a **backend-only** annotation change: no UI file is touched. The FE app renders the button from OData metadata after republish.\r\n\r\n**Scope: List-Report toolbar button (mde-action-button) and Object-Page action button (ddlx-identification with forAction).** Both use the same preconditions and idempotency rules. The annotation form is `@UI.lineItem: [{ type: #FOR_ACTION, dataAction: '<actionName>', label: '<label>', position: <n> }]`.\r\n\r\n**Preconditions — resolve the DDLX and anchor before touching anything:**\r\n\r\n1. Read the **DDLX** source with `sap_get_source`. Confirm it is a metadata extension (`annotate view` or `extend metadata`). If the read fails or the object is not a DDLX, report and stop.\r\n2. Locate the **`anchorField`** in the DDLX — the existing, exposed, non-`@UI.hidden` field whose annotation block the `#FOR_ACTION` entry will be inserted above. If the anchor field is not found in the DDLX, refuse and report which field names are present.\r\n3. **Idempotency check:** scan the DDLX source for any existing `#FOR_ACTION` entry with `dataAction: '<action>'` matching the `action` argument (regex-escaped). If found, the button is already present; report and stop without writing.\r\n\r\n**Plan/confirm gate (Forge Rule 8):**\r\n\r\n```\r\nPlan — add List-Report toolbar button for action '<Name>' to DDLX. This modifies 1 SAP object:\r\n1. <ZC_…> (DDLX) — insert @UI.lineItem #FOR_ACTION above field '<anchorField>' → write → activate → republish if needed\r\nProceed? [y/n]\r\n```\r\n\r\n**Execution chain:**\r\n\r\n1. **Snapshot** the DDLX with `sap_snapshot_take`. Verify it succeeded. If it fails, do not proceed (Rule 7).\r\n2. Call `extend_model_insert` with `kind: mde-action-button`, the DDLX source, `action` (e.g. `\"Approve\"`), `anchorField`, `label`, and `position`. The tool inserts the `#FOR_ACTION` annotation entry at the top of the `anchorField`'s contiguous annotation block (never splitting an existing block). It returns `{ modified, summary }`.\r\n3. **Write** via `sap_set_source` → **syntax-check** via `sap_syntax_check` (stop on errors) → **activate** via `sap_activate` → **verify** via `sap_inactive_objects`.\r\n4. **Republish** the service binding if the exposed surface changed — call `sap_service_binding_publish` and check the result severity (HTTP 200 alone is not success). For a `#FOR_ACTION` annotation add, republish is typically needed for the button to appear in OData `$metadata`. This step is the exit gate for the entire action→button chain (\"action button active in DDLX; binding republished; List-Report toolbar button visible on next metadata fetch\").\r\n5. **Rollback on any failure:** restore the DDLX from the Step-1 snapshot, reactivate the prior version, and report the chain state — whether the service is back to its prior published metadata.\r\n\r\n**Together, `projection-use` + `mde-action-button` complete the action→button tail** that `bdef-stub` defers (the stub adds the action to the base BDEF; `projection-use` exposes it in the projection; `mde-action-button` renders it as a List-Report toolbar button). This is the change-type #4 chain used by `abap-plan` revision mode.\r\n\r\n### Step 1 — Confirm the target and the edit\r\n\r\nEstablish the object name, type, the field to add, the `kind`, and (for a\r\ncolumn) the position. If the object is SAP-standard, or the field needs value\r\nhelp / draft / computed treatment, STOP and route the user to the right skill\r\n(see \"When to Use\"). Confirm the object is custom (Z/Y).\r\n\r\n### Step 2 — Read the live source (ground truth)\r\n\r\nCall `sap_get_source` with the object `name` and `type` (request the **active**\r\nversion). Build against what is actually active in the system — never against an\r\nassumed shape. If the read fails or the object is missing/inactive, report and\r\nstop.\r\n\r\n### Step 3 — Snapshot (Forge Rule 7) — MANDATORY\r\n\r\nCall `sap_snapshot_take` with the object `name` and `type` BEFORE any write.\r\nVerify it succeeded. **If the snapshot fails, do NOT proceed** — report the\r\nfailure and ask how to continue (Rule 7 has no exceptions). Keep the captured\r\npre-change source: it is the rollback (Step 8).\r\n\r\n### Step 4 — Anchored insertion (deterministic — never hand-edit)\r\n\r\nCall `extend_model_insert` to produce the modified source. Do NOT edit the\r\nsource yourself — the tool performs a deterministic anchored insertion so an\r\nannotation or association can never be silently dropped:\r\n\r\n```\r\nextend_model_insert\r\n kind: <cds-field | ddlx-lineitem | cds-lineitem | table-field | bdef-field | projection-use | mde-action-button>\r\n source: <the source returned by sap_get_source>\r\n field: <element/field to add or promote; for table-field the field clause\r\n \"name : type\"; for bdef-field the \"field ( … ) Name\" or \"Name = column\">\r\n position: <number> (ddlx-lineitem and cds-lineitem — the column order)\r\n alias: <string> (bdef-field — required only with multiple behaviors)\r\n target: <field | mapping> (bdef-field — default field)\r\n```\r\n\r\nFor `kind: cds-lineitem` the tool takes the **promote** path: it anchors on the\r\ndeclaration line of an element already present in the CDS projection and inserts\r\nthe `@UI.lineItem` annotation directly above it. This path is fail-safe — it\r\n**refuses multi-line / computed elements** (e.g. a `case … end as Name`, where\r\ninserting above the line would split the expression) and is **idempotent**\r\n(refuses when the field is already a column, so a re-run errors rather than\r\nduplicating the annotation).\r\n\r\nIt returns `{ modified, summary }`. If it returns `is_error` (e.g. the anchor —\r\nthe body's closing brace — is missing, the field is not found / ambiguous, the\r\nelement is multi-line/computed, it is already a column, or an unknown kind),\r\nreport the exact message and stop; do not fall back to hand-editing.\r\n\r\n### Step 5 — Present the change and get approval (Forge Rule 8)\r\n\r\nShow the user:\r\n- the object (name + type) and the session transport,\r\n- the tool's `summary`,\r\n- the inserted lines (and, for `cds-field`, that the prior last element gains a\r\n trailing comma),\r\n- whether a service-binding **republish** will follow (Step 7c).\r\n\r\nThen ask: `Proceed? [y/n]`. Wait for explicit `y`. Do not write on an ambiguous\r\nresponse.\r\n\r\n### Step 6 — Write (Forge Rule 9)\r\n\r\nCall `sap_set_source` with `name`, `type`, the `modified` source, the session\r\ntransport, and the `approval_id`. Assign to the CSPeach session transport — never\r\n`$TMP` unless the user explicitly says local-only.\r\n\r\n### Step 7 — Verify, activate, republish (Forge Rule 10)\r\n\r\n**7a — Syntax check.** Call `sap_syntax_check` on the object. If it reports\r\n**errors**, do NOT activate — go to Step 8 (rollback) and report. Warnings only:\r\nrecord them and continue.\r\n\r\n**7b — Activate.** Call `sap_activate` for the object (with `approval_id`), then\r\nconfirm with `sap_inactive_objects` that the object is no longer inactive. A\r\nreported activation error, or the object still appearing inactive, is a failure\r\n→ Step 8.\r\n\r\n**7c — Republish if the exposed set changed.** If the edited projection adds a\r\nfield that a published service exposes, the OData `$metadata` only refreshes on\r\nrepublish — call `sap_service_binding_publish` for the binding and check the\r\nresult severity (HTTP 200 alone is not success). For a `ddlx-lineitem`-only\r\nannotation change on an already-exposed field, activation is usually enough;\r\nrepublish when in doubt about the metadata surface.\r\n\r\n### Step 8 — Rollback on any failure\r\n\r\nIf the syntax check fails, activation fails, or the object stays inactive:\r\nrestore the **pre-change source captured in Step 3** via `sap_set_source` (with a\r\nfresh approval), reactivate the prior version, and confirm it is active again.\r\nThen report the **chain state** plainly: what was reverted, what was never\r\ntouched, and whether the service is back to its prior published metadata. A\r\nrevision edits a *working* object — leaving it broken is worse than greenfield,\r\nso recovery is mandatory, not optional.\r\n\r\n### Step 9 — Report\r\n\r\nReport the final state: object changed + activated, transport, whether the\r\nservice was republished, and (for an FE column) that the app will show the new\r\ncolumn on its next metadata fetch.\r\n\r\n## Guardrails\r\n\r\n- NEVER hand-edit the source — always go through `extend_model_insert` (Forge\r\n Rule 7a's spirit: no blind full-source reconstruction).\r\n- NEVER skip the snapshot (Rule 7) or proceed when it fails.\r\n- NEVER add more than one field per run. A single-object edit touches one object;\r\n the B2a table→CDS→BDEF chain is the only multi-object case, and it carries the\r\n SAME one field across all three objects under one Rule 8 plan/confirm gate.\r\n- Managed BDEF only — `bdef-field` and `bdef-stub` both refuse unmanaged BDEFs.\r\n If the target BDEF is unmanaged, note it and stop (or run only the table+CDS\r\n legs for `bdef-field`; there is no equivalent for `bdef-stub`).\r\n- Table adds are **non-key, appended at the end, only** — refuse key adds,\r\n reordering, or type changes (destructive DDIC conversion).\r\n- Before generating BDEF syntax, confirm `system_capability('rap.managed')` and\r\n `system_capability('rap.behaviorDefinition')` — skip the BDEF leg if either is no.\r\n- For `bdef-stub`: **verify the behavior pool class exists** before generating\r\n stubs — call `sap_get_source` for the pool class (type `CLAS`). If it is\r\n missing, refuse and point the user to `/abap-rap`.\r\n- For `bdef-stub`: **activate is mandatory** after every write — it is the only\r\n reliable BDEF guard. Never skip activation, even for a \"just a declaration\"\r\n stub change.\r\n- For `bdef-stub`: the stub is BACKEND-ONLY. The `projection-use` and\r\n `mde-action-button` kinds (C1a/C1b above) handle surfacing an action as a\r\n Fiori button — `bdef-stub` only declares the action in the base BDEF and CCIMP.\r\n- For `bdef-stub`: always warn the developer that `get_instance_features` stubs\r\n are unfilled no-ops and second feature actions are not auto-wired.\r\n- NEVER activate when `sap_syntax_check` reports errors (Rule 10).\r\n- NEVER leave a failed edit on a working object — restore the snapshot (Step 8).\r\n- NEVER assign to `$TMP` unless the user explicitly says local-only (Rule 9).\r\n- Value-help wiring and computed criticality are now IN scope via the Track 2\r\n vocabulary modes: `cds-valuehelp` mode covers value-help wiring; computed\r\n criticality fields pair a `cds-field` insert with `ddlx-datapoint` — see the\r\n vocabulary modes section. Draft handling remains out of scope — STOP and route\r\n to the right skill for that.\r\n\r\n## Vocabulary modes (Track 2)\r\n\r\nv1.1 adds eight `extend_model_insert` kinds that author Fiori annotation\r\nvocabulary in place. Each runs the SAME execute-chain as every other kind\r\n(snapshot → `extend_model_insert` → syntax-check → activate → republish-if-needed\r\n→ rollback — Steps 3–8). Six target a **DDLX** metadata extension, one a **CDS\r\nview (DDLS)**, one a **managed BDEF**. All are anchored string splices — never\r\nhand-edit the source.\r\n\r\n### `ddlx-headerinfo` — Object-Page header (target: DDLX)\r\n\r\nParams: `typeName`, `typeNamePlural`, `titleField`, `descriptionField?`. Inserts a\r\nview-level `@UI.headerInfo` block ABOVE the `annotate view` line (header\r\nannotations sit on the view, not a field). **Idempotent:** refuses when a\r\n`@UI.headerInfo` already exists — a metadata extension carries exactly one.\r\n\r\n```\r\n@UI.headerInfo: {\r\n typeName: 'Maintenance Request',\r\n typeNamePlural: 'Maintenance Requests',\r\n title: { type: #STANDARD, value: 'RequestId' }\r\n}\r\n```\r\n\r\nWith `descriptionField` a `description: { type: #STANDARD, value: '<field>' }`\r\nline is added alongside `title`.\r\n\r\n### `ddlx-selectionfield` — filter-bar entry (target: DDLX)\r\n\r\nParams: `field`, `position`. Adds `@UI.selectionField: [{ position: N }]`.\r\nDual behaviour: **in-place** above the field's declaration when it is already\r\nexposed, else **appends** `@UI.selectionField …` + `<field>;` before the body's\r\nclosing brace.\r\n\r\n```\r\n@UI.selectionField: [{ position: 10 }]\r\nPriority;\r\n```\r\n\r\n### `ddlx-identification` — Object-Page field / action button (target: DDLX)\r\n\r\nParams: `field?`, `position`, and — for the button — `action` (+ optional\r\n`label`). Two modes:\r\n- **Plain** (`field` given, no `action`): `@UI.identification: [{ position: N }]`\r\n above the field (Object-Page field section).\r\n- **FOR_ACTION button** (`action` given): the Object-Page action button.\r\n **IMPORTANT — the FOR_ACTION button REQUIRES a target `field`:** the annotation\r\n attaches to that field's element; a lone annotation before the closing brace is\r\n invalid DDLX and fails activation, so the button piggybacks on an existing\r\n element line. Passing `action` without `field` is refused.\r\n\r\n**Idempotent:** refuses a duplicate `dataAction` across ANY existing\r\n`@UI.identification` array (whole-source, bracket-balanced — a duplicate inside a\r\nmulti-line hand-authored array is caught, not just a same-line one).\r\n\r\n```\r\n@UI.identification: [{ type: #FOR_ACTION, dataAction: 'Approve', label: 'Approve', position: 10 }]\r\nPriority;\r\n```\r\n\r\n### `ddlx-facet` — collection facet + field group (target: DDLX)\r\n\r\nParams: `facetId`, `label`, `fieldGroupQualifier`, `position`,\r\n`fields: [{ field, position }]`. Lands BOTH parts as a unit: ONE view-level\r\n`@UI.facet` entry (a `#FIELDGROUP_REFERENCE`, MERGED into an existing `@UI.facet`\r\narray if one is present, else a fresh line above `annotate view`) AND a per-field\r\n`@UI.fieldGroup` on each named field (in-place or appended). **Idempotent as a\r\nunit on `facetId`:** refuses a duplicate id BEFORE touching a single field, so a\r\nrefused call is a true no-op.\r\n\r\n```\r\n@UI.facet: [{ id: 'GeneralFacet', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'General', targetQualifier: 'GeneralGroup', position: 10 }]\r\n...\r\n@UI.fieldGroup: [{ qualifier: 'GeneralGroup', position: 10 }]\r\nPriority;\r\n```\r\n\r\n### `ddlx-datapoint` — data point (target: DDLX)\r\n\r\nParams: `field`, `title`, `criticalityField?`. Inserts a `@UI.dataPoint` above an\r\nEXISTING exposed field (adds `, criticality: '<criticalityField>'` when given).\r\nThe data point is element-attached — the field is required and must already be\r\nexposed. **It does NOT create the criticality field** (see the pairing rule\r\nbelow). Throws when the target field is absent.\r\n\r\n```\r\n@UI.dataPoint: { title: 'Total Amount', criticality: 'AmountCriticality' }\r\nAmount;\r\n```\r\n\r\n### `ddlx-chart` — chart + presentation variant (target: DDLX)\r\n\r\nParams: `chartType` (`COLUMN`|`BAR`|`LINE`|`DONUT`), `dimensions[]`, `measures[]`,\r\n`qualifier?`. Emits BOTH a view-level `@UI.chart` AND a paired\r\n`@UI.presentationVariant` (`#AS_CHART`) above `annotate view` — ALP/OVP bind a\r\nchart through the presentation variant, never the raw chart, so the pair is always\r\nemitted together. **Idempotent as a unit on `qualifier`:** a named chart refuses a\r\nduplicate qualifier; an anonymous chart refuses when any anonymous `@UI.chart`\r\nalready exists.\r\n\r\n```\r\n@UI.chart: [{ qualifier: 'SalesChart', chartType: #COLUMN, dimensions: ['Priority'], measures: ['Amount'] }]\r\n@UI.presentationVariant: [{ visualizations: [{ type: #AS_CHART, qualifier: 'SalesChart' }] }]\r\n```\r\n\r\n### `cds-valuehelp` — value help wiring (target: CDS view / DDLS)\r\n\r\nParams: `field`, `entity`, `element`, `additionalBinding?: { localElement, element }`.\r\n**Targets the VIEW source (DDLS), NOT a metadata extension** — a value-help\r\nbinding is a data-model annotation, so it lives on the view element (same anchor\r\nrules as `cds-lineitem`): inserts `@Consumption.valueHelpDefinition` ABOVE the\r\nexisting exposed element. Fail-safe: refuses an unknown/ambiguous field and a\r\nmulti-line/computed element. **Idempotent:** refuses when the field already carries\r\na `@Consumption.valueHelpDefinition`.\r\n\r\n```\r\n@Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_Customer', element: 'CustomerID' } }]\r\nCustomerID,\r\n```\r\n\r\n### `bdef-sideeffects` — RAP side effects clause (target: managed BDEF)\r\n\r\nParams: `sourceFields[]`, `targetFields[]`, `entityAlias?` (required only when the\r\nBDEF defines more than one behavior). Inserts a RAP `side effects { field … affects\r\nfield …; }` clause into the managed entity body. **This is the RAP BDEF `side\r\neffects` clause** (valid on ABAP Platform 2022+/S4H 2023), NOT the invalid CDS\r\n`@Sideeffects` annotation. **It MERGES into an existing singleton `side effects`\r\nblock** rather than emitting a second one (two blocks fail activation), and refuses\r\nan identical clause (idempotency). Managed-only — refuses an unmanaged BDEF; refuses\r\nempty `sourceFields`/`targetFields`.\r\n\r\n```\r\nside effects\r\n{\r\n field Quantity, UnitPrice affects field NetAmount;\r\n}\r\n```\r\n\r\n### Criticality pairing rule (MANDATORY — never reference a phantom field)\r\n\r\nCriticality colouring (`ddlx-datapoint` with `criticalityField`) points at a field\r\nthat must ALREADY exist and be exposed — the transform does NOT create it. To\r\ncolour a value by criticality, do it in TWO ordered inserts:\r\n\r\n1. **First** insert the computed criticality element into the CDS view with\r\n `kind: cds-field` (e.g. a `case … end as AmountCriticality`) and activate it.\r\n2. **Then** insert the `ddlx-datapoint` referencing\r\n `criticalityField: 'AmountCriticality'`.\r\n\r\nNever emit a `ddlx-datapoint` that references a criticality field the view does not\r\nyet expose — activation fails on the unknown element.\r\n\r\n## DDLX create-if-absent\r\n\r\nThe DDLX-targeting vocabulary modes (`ddlx-headerinfo`, `ddlx-selectionfield`,\r\n`ddlx-identification`, `ddlx-facet`, `ddlx-datapoint`, `ddlx-chart`,\r\n`ddlx-lineitem`, `mde-action-button`) need a metadata extension to annotate. If\r\nthe projection view has no DDLX yet, create it first, then run the insert kind\r\nagainst the fresh source.\r\n\r\n1. **Read** — `sap_get_source` for the DDLX (type `DDLX`), active version.\r\n2. **On not-found → create** — `sap_create_object` with objectType `DDLX`, subType\r\n `EX`, a description, the target package, and the session transport.\r\n3. **Seed the skeleton** — `sap_set_source` with the minimal annotate shell for the\r\n projection view `<ZC_View>`:\r\n\r\n```\r\n@Metadata.layer: #CUSTOMER\r\nannotate view <ZC_View> with\r\n{\r\n}\r\n```\r\n\r\n4. **Syntax-check** — `sap_syntax_check`; stop on errors.\r\n5. **Activate** — `sap_activate`, then confirm with `sap_inactive_objects`.\r\n6. **Then proceed** with the requested insert kind against the now-active source,\r\n following the SAME snapshot → `extend_model_insert` → syntax-check → activate →\r\n republish-if-needed → rollback chain as every other mode (Steps 3–8 above — do\r\n not duplicate it).\r\n\r\nIf the DDLX already exists (the read succeeds), skip creation and go straight to the\r\nnormal execute-chain. Creating the DDLX is itself an object create — when combined\r\nwith the subsequent insert it falls under the Rule 8 plan/confirm gate: present the\r\ncreate + insert together as one plan and wait for explicit `y`.\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## abap-extend-model — [Object · Field]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was added and to which object>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <the object + field changed, with activation/transport status>\r\n2. <verification result — syntax/activate/inactive-objects, and republish if any>\r\n3. <what the user sees next, or the blocker/rollback if it failed>\r\n\r\n**Verdict:** <1-line conclusion or next step>\r\n\r\n---\r\n\r\n<then the full build log below>\r\n```\r\n\r\n## Output Structure\r\n\r\n```\r\n## abap-extend-model — {Object} · {Field}\r\n\r\n## TL;DR\r\n...\r\n\r\n---\r\n\r\n## Change Plan\r\n\r\n| Item | Value |\r\n|------|-------|\r\n| Object | {name} ({type}) |\r\n| Add | {field} ({kind}{, position N}) |\r\n| Transport | {transport} |\r\n| Republish | {binding or \"not needed\"} |\r\n\r\nConfirmed — proceeding.\r\n\r\n---\r\n\r\n## Snapshot\r\n\r\nsap_snapshot_take {name} {type} → captured (id / size)\r\n\r\n## Edit\r\n\r\nextend_model_insert {kind} → {summary}\r\nInserted:\r\n + {line(s)}\r\n\r\n## Write + Verify\r\n\r\nsap_set_source → written\r\nsap_syntax_check → OK | ERRORS (listed)\r\nsap_activate → active | FAILED\r\nsap_inactive_objects → clean | still inactive\r\nsap_service_binding_publish {binding} → published (severity OK) | n/a\r\n\r\n## Result\r\n\r\n{Object} active with {field}. {App shows the new column on next metadata fetch.}\r\n{Or: FAILED at <step> — snapshot restored, prior version active again.}\r\n```\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-extend-model\r\n\r\nAdd a Priority column to my maintenance-request list report.\r\nProjection: ZC_PM_MAINTREQ (DDLS) — add field Priority.\r\nMetadata extension: ZC_PM_MAINTREQ (DDLX) — add @UI.lineItem at position 60.\r\nService binding to republish: ZUI_PM_MAINTREQ_O4.\r\n```\r\n",
100
107
  "sha256": "7f278b61f2e7c6b0d440160a326fe48acbb052664aa041bbd1278d1aaea42685",
101
108
  "signature": "",
102
- "signedAt": "2026-09-17T20:19:04.799Z"
109
+ "signedAt": "2026-09-28T14:37:04.555Z"
103
110
  },
104
111
  "abap-fiori-build": {
105
112
  "name": "abap-fiori-build",
106
113
  "body": "---\r\nname: abap-fiori-build\r\ndescription: >\r\n Build a working freestyle SAPUI5 app on the developer's laptop from one or\r\n more already-published OData services. Use when the user wants to\r\n create, scaffold, or generate a Fiori or UI5 freestyle app; a frontend or\r\n UI for a published OData service; or a multi-page UI on top of a RAP or\r\n SEGW backend. Primary mode is freestyle; also supports a Fiori Elements\r\n mode (scaffold a List Report, Worklist, Overview Page, or Analytical List\r\n Page against an already-published OData service, driven by backend @UI\r\n annotations — with annotation authoring via the backend metadata extension\r\n or local EDMX, and V4 Fiori Elements extension points for beyond-annotation\r\n needs). Produces local files, not ADT objects.\r\nphase: BUILD\r\nrequires_mcp: read_only\r\nforge_rules: [1, 8]\r\nversion: \"1.4\"\r\n---\r\n\r\n# abap-fiori-build\r\n\r\n## Purpose\r\n\r\nScaffold and compose a complete, runnable freestyle SAPUI5 app from one or more\r\npublished OData V2/V4 services. The skill covers the full local build lifecycle:\r\nmetadata introspection, scaffold, page/dialog/chart composition, manifest wiring,\r\nnpm build validation, and browser preview against a live SAP backend.\r\n\r\nThe output is a folder of UI5 source files on disk — not ADT objects. If the\r\nbackend service does not yet exist, run `/abap-rap` first to build it, then return\r\nto this skill.\r\n\r\n## When to Use\r\n\r\n- The user has at least one published OData service (V2 or V4) and wants a\r\n custom Fiori app on top of it.\r\n- The user needs pages beyond what Fiori Elements auto-generates (custom layouts,\r\n bespoke controllers, charts, dialogs).\r\n- The user asks for a \"Fiori app\", \"UI5 app\", \"frontend for my service\", or a\r\n \"multi-page UI\" on a RAP or SEGW backend.\r\n- The user wants a quick **Fiori Elements** List Report, Worklist, or Overview\r\n Page against an already-published service with no custom frontend code — use\r\n the FE Mode section below.\r\n\r\nDo NOT use this skill if:\r\n\r\n- The backend OData service does not yet exist — run `/abap-rap` first.\r\n- The requirement needs NEW backend model or behavior objects (a table, CDS view,\r\n or BDEF that does not exist yet) — build those with `/abap-rap`, or extend\r\n SAP-standard objects with `/abap-enhance`, first. Authoring `@UI` annotations on\r\n a stack we already own is NOT out of scope — **FE Mode** below does it (backend\r\n MDE via `/abap-extend-model`, or local EDMX for a foreign service), and V4 charts\r\n / computed columns / controller extensions are handled there too; only\r\n beyond-annotation needs on OVP or OData V2 apps fall back to freestyle.\r\n- The user wants a BTP / Cloud Foundry deployment — only ABAP-repository (BSP)\r\n deploy is supported (Step 10); BTP targets are out of scope.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Published service URL, relative path, and OData version (2.0 or 4.0)\r\n- App id (reverse-domain, e.g. `zso.orderlist`) and title\r\n- What the app should show (entity list, detail, charts, etc.)\r\n\r\nOptional:\r\n- Additional service URLs (multi-service apps)\r\n- Naming prefix and target directory\r\n- Whether a chart (`sap.viz`) or value-help (`sap.ui.comp`) pattern is needed\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Gather Inputs\r\n\r\n> **Amend vs. create (read first).** If the target app directory already contains\r\n> `webapp/manifest.json`, this is an **amend** of an existing app — jump to the\r\n> **Amend Mode** section below. If the directory is empty/absent, this is a\r\n> create — continue with Steps 1–10. If the directory is non-empty but has **no**\r\n> `webapp/manifest.json`, STOP and report (likely a wrong path) — never scaffold\r\n> into a populated directory.\r\n\r\nWhen dispatched from `/abap-plan`, the inputs arrive in the plan envelope — use\r\nthem without asking. Otherwise ask:\r\n\r\n1. What is the published service URL and path? (e.g. `https://host:44300`,\r\n `/sap/opu/odata4/zso/salesorder_srv/`)\r\n2. OData version: 2.0 or 4.0?\r\n3. What is the app id and title?\r\n4. What pages/views does the user need, and against which entities?\r\n5. Should the app include a chart (bar/column/line) or a value-help dialog?\r\n\r\n### Step 2 — Read $metadata\r\n\r\nFor each service, read its `$metadata` endpoint via MCP (`sap_odata_test` or a\r\nplain GET request). From the response, identify:\r\n\r\n- All EntitySet names and key properties\r\n- Navigation properties that connect root to child entities\r\n- Any `Edm.Decimal`/`Edm.Currency` fields needing special handling\r\n- **Coded / status / domain fields and their text source.** Any field that\r\n holds a short code (status `N`/`A`/`R`, priority `1`–`4`, a type code, a\r\n domain fixed-value) must be rendered as a friendly label with semantic colour,\r\n never as the raw code. For each coded field, find where its text lives:\r\n - a **text association** or `@ObjectModel.text.element` on the field (the\r\n text is exposed as a sibling property or a navigation property in the\r\n service — bind it directly);\r\n - a **value-help / text entity** the service exposes for the code;\r\n - a **fixed-value domain** (the code→label map is in the CDS/DDIC source).\r\n\r\n Capture the codes + labels here so they can be wired in the views (see the\r\n \"Coded / Status Fields\" recipe). Plan to render every coded field with\r\n `sap.m.ObjectStatus` + label, never the raw code.\r\n\r\nReconcile entities against the requirement BEFORE building. If the metadata does\r\nnot match what the user described, report the discrepancy and ask how to proceed.\r\n\r\n### Step 3 — Present the Build Plan (Forge Rule 8)\r\n\r\nPresent a numbered plan listing every artifact that will be created or modified:\r\n\r\n| Step | Artifact | Tool | Notes |\r\n|------|----------|------|-------|\r\n| 1 | `<appId>/` scaffold | `fiori_scaffold` | base freestyle app |\r\n| 2 | `view/<Page>.view.xml` + `controller/<Page>.controller.js` | `file_write` | list page |\r\n| 3 | `webapp/model/formatter.js` | `file_write` | code→label+state map for coded/status fields (only when the app has coded fields) |\r\n| 4 | ... | ... | ... |\r\n| N | `webapp/manifest.json` | `file_edit` (route/target wiring) | after each page |\r\n\r\nThen ask:\r\n\r\n```\r\nPlan complete. This will create [N] files. Proceed? [y/n]\r\n```\r\n\r\nWait for explicit `y` before proceeding (Forge Rule 8 — no batch without plan).\r\n\r\n### Step 4 — Safety: Stash If in a Git Repo\r\n\r\nIf the target directory is inside a git repository, run:\r\n\r\n```bash\r\ngit stash -u\r\n```\r\n\r\nThis gives a one-command rollback if anything goes wrong.\r\n\r\n### Step 5 — Scaffold the Base App\r\n\r\n> **The Fiori engine is BUILT IN.** Scaffold, catalog apply, list, and\r\n> deploy-config are first-class CSPeach tools (`fiori_scaffold`, `fiori_apply`,\r\n> `fiori_list`, `fiori_deploy_config`) — no `pnpm`, no repo, and no shell command\r\n> are needed. The same deterministic engine runs for every customer on the\r\n> shipped `cspeach` binary. (These tools are part of the local-build tool set;\r\n> they are enabled together with `file_write`/`shell_exec` when `local_build` is\r\n> on — the same toggle this skill already needs to write app files.)\r\n\r\nCall the `fiori_scaffold` tool:\r\n\r\n```\r\nfiori_scaffold\r\n basePath: <appId> (relative to the project root)\r\n appId: <id> (reverse-domain, e.g. zso.orderlist)\r\n appTitle: \"<title>\"\r\n template: basic (basic | worklist | listdetail)\r\n serviceUrl: <url> (optional, e.g. https://host:44300)\r\n servicePath: <path> (optional, relative OData path)\r\n serviceVersion: <2.0|4.0> (optional, default 4.0)\r\n```\r\n\r\nArgument reference for `fiori_scaffold`:\r\n\r\n| Arg | Required | Description |\r\n|------|----------|-------------|\r\n| `basePath` | Yes | Directory to scaffold into (relative to project root, created if missing). Sandboxed — must stay inside the project root. |\r\n| `appId` | Yes | App namespace, e.g. `zso.orderlist` |\r\n| `appTitle` | No | Human-readable app title (defaults to `appId`) |\r\n| `template` | No | `basic` (default), `worklist`, or `listdetail` |\r\n| `serviceUrl` | No | Backend host, e.g. `https://host:44300` |\r\n| `servicePath` | No | Relative OData service path |\r\n| `serviceVersion` | No | `2.0` or `4.0` (default `4.0`) |\r\n| `ui5Version` | No | UI5 version for manifest `minUI5Version` + preview (default `1.120.0`). Do NOT go below `1.84` — older cores ignore the generated `data-sap-ui-on-init` bootstrap attribute and the app renders a blank page. |\r\n\r\nThe tool returns the app directory and the full list of files written. The\r\nscaffold emits a complete `webapp/` tree: `manifest.json`, `index.html`,\r\n`Component.js`, a `Main.view.xml`, `Main.controller.js`, `i18n/i18n.properties`,\r\nand the `ui5.yaml` / `package.json` needed for `npm run build` and `npm run start`.\r\n\r\n### Step 6 — Compose Catalog Patterns (chart and value-help only)\r\n\r\nThe engine catalog holds **two entries** for the patterns that require\r\n`sap.viz`/`sap.ui.comp` — controls the model gets wrong without a frozen\r\ntemplate.\r\n\r\nList available entries with the `fiori_list` tool (no args):\r\n\r\n```\r\nfiori_list\r\n```\r\n\r\nThe result includes each entry's `params` block — name, type, required flag, and\r\ndescription for every parameter. **Read it before calling `fiori_apply`** and\r\nbuild the complete `params` object from it in one shot; do not guess params and\r\niterate on \"requires param\" errors.\r\n\r\nApply a catalog entry into the scaffolded app with `fiori_apply`:\r\n\r\n```\r\nfiori_apply\r\n appDir: <appId> (relative to the project root)\r\n entry: <viz-chart|value-help>\r\n params: { ... } (object; specs from fiori_list)\r\n```\r\n\r\nArgument reference for `fiori_apply`:\r\n\r\n| Arg | Required | Description |\r\n|------|----------|-------------|\r\n| `appDir` | Yes | Root directory of the already-scaffolded app. Sandboxed to the project root. |\r\n| `entry` | Yes | Catalog entry name: `viz-chart` or `value-help` |\r\n| `params` | Yes | Object of entry parameters (specs are in the `fiori_list` result). A JSON string is also accepted. |\r\n\r\nThe engine renders the entry's EJS files, writes them into `webapp/`, deep-merges\r\nthe manifest patch (errors loudly on conflict — never silent last-wins), and\r\nappends i18n keys.\r\n\r\n**The catalog is ONE of three composition lanes.** Use it for its two\r\ndeterministic entries only — `viz-chart` and `value-help` (this Step 6).\r\nEverything else is composed by the next two lanes, in this order: **grounded\r\nsamples** (Step 6.5) first, then the **recipes** (Step 7) when no sample matches.\r\n\r\n### Step 6.5 — Retrieve Grounding Samples\r\n\r\nBefore hand-authoring any page, dialog, or control the catalog does not cover,\r\nground it in a real OpenUI5 sample instead of composing from memory.\r\n\r\n1. Search the vendored UI5 Demo Kit index with `fiori_sample_search`, passing the\r\n requirement's controls or UI patterns:\r\n\r\n ```\r\n fiori_sample_search\r\n text: \"<the control or pattern you need, e.g. 'wizard multi-step form'>\"\r\n control: <optional ranking boost — the BARE control name, e.g. Wizard (never sap.m.Wizard)>\r\n max: <optional cap on the number of hits>\r\n ```\r\n\r\n2. Pull the top 1–3 scored hits with `fiori_sample_get`:\r\n\r\n ```\r\n fiori_sample_get\r\n name: <sample name from the search result>\r\n ```\r\n\r\n It returns `{ files, license: 'Apache-2.0', attribution }` — the sample's\r\n source files plus the attribution string (always present). Un-vendored samples\r\n are fetched and cached on demand; if a fetch fails, the error itself steers you\r\n to the recipes and the `sap-docs` fallback (Step 7).\r\n\r\n3. **Author the page from the sample in the Step 7 lane.** Take the sample's\r\n control tree, binding shape, and controller wiring as the ground truth, then\r\n adapt every field/entity/route name to the real `$metadata`. The **recipes stay\r\n the style guide**: naming conventions, i18n key layout, and the coded/status\r\n formatter pattern still apply on top of the sample-derived structure.\r\n\r\n**Attribution (required for adapted code).** Any file whose code is *adapted* from\r\na sample — not merely inspired by having read one — gets a one-line comment\r\ncrediting OpenUI5 at the top, e.g.:\r\n\r\n```javascript\r\n// Adapted from OpenUI5 sample \"<sample name>\" (Apache-2.0) — see fiori_sample_get attribution.\r\n```\r\n\r\nUse the `attribution` string from `fiori_sample_get`. A file written from scratch,\r\nmerely informed by a sample, needs no comment.\r\n\r\n### Step 7 — Generate from Recipes (and sap-docs fallback)\r\n\r\nThis is the third composition lane. When a grounding sample (Step 6.5) covers the\r\npage, dialog, or toast, author from it here — the recipes below supply the naming,\r\ni18n, and formatter conventions to layer on top. When **no sample matches** the\r\nrequirement, generate the files directly from the recipes using Write/Edit tools;\r\nthe recipes give you the right controls and structure. For an unfamiliar control\r\nthat neither a sample nor a recipe covers, consult the `sap-docs` MCP world (UI5\r\ncontrol APIs) as the final fallback. Either way, adapt everything to the actual\r\nentity from `$metadata`, and wire `manifest.json` after each file (see anatomy\r\nsection).\r\n\r\n### Step 8 — Validate the Build\r\n\r\n```bash\r\ncd <appId>\r\nnpm install\r\nnpm run build\r\n```\r\n\r\nRead the real error from stderr if the build fails. Fix it and re-run before\r\nproceeding. Do not report success until `npm run build` exits 0.\r\n\r\n### Step 9 — Preview and Render Smoke (freestyle gate)\r\n\r\n```bash\r\nnpm run start\r\n```\r\n\r\nThis serves the app at a local authenticated preview URL that proxies to the live\r\nSAP backend. Run the **render smoke** against it as the freestyle build gate — the\r\nsame gate FE mode uses (see **Render smoke (ui.build gate)**), now wired for\r\nfreestyle:\r\n\r\n```\r\nfiori_render_smoke\r\n appDir: <appId> (derives the smoke spec from the app's OWN\r\n views / i18n / manifest — controlKind, expected\r\n columns with i18n resolution, press exercises via\r\n real DOM selectors, route exercises via router.navTo)\r\n appUrl: <LOCAL AUTHENTICATED PREVIEW url> (the npm run start URL — NOT a\r\n deployed BSP url)\r\n```\r\n\r\nWith `appDir` set, the smoke derives its spec from the freestyle app itself;\r\nexplicit args (e.g. `expectedColumns`) override the derived fields. It returns\r\n`warnings[]` for anything it had to skip — an id-less press, a parameterized\r\nroute, an unresolvable boot view — surface those. A press handler that throws or a\r\nroute that fails is a **check failure**, not a warning.\r\n\r\n**`verification: 'manual'` means the gate did NOT run** — browser missing, the\r\n`render_smoke` flag off, or the preview unreachable — with **exactly the same\r\nsemantics as FE mode**. It is NEVER a pass: say so plainly and require an explicit\r\nhuman preview confirmation that the app renders real rows from the live SAP\r\nservice before declaring the skill done. An empty list is a **FAIL** unless\r\n`allowEmptyRows` is set deliberately and the acknowledgment is surfaced.\r\n\r\n### Step 10 — Deploy to the ABAP Repository (optional, gated)\r\n\r\nOnly when the developer asks to deploy. This writes a BSP application into the\r\nSAP system on a transport — it is a system write, so the full Forge gate applies.\r\n\r\n**10a — Gather and confirm the target (Forge Rules 8 + 9).** Ask for / propose:\r\n\r\n| Input | Rule |\r\n|-------|------|\r\n| BSP app name | max 15 chars, UPPERCASE, Z/Y prefix (e.g. `ZDT_DOWNTIME`) |\r\n| Package | the package the backend stack lives in — keeps UI + backend together |\r\n| Transport | **use the CSPeach session transport** (`sap_transport` list/create) — backend objects and the UI app belong in ONE transport. Never `$TMP` unless the developer explicitly says local-only. |\r\n\r\nPresent the deploy plan (system, client, app name, package, transport) and wait\r\nfor explicit `y` before touching the system.\r\n\r\n**10b — Write the deploy config with the `fiori_deploy_config` tool (engine, not hand-rolled YAML):**\r\n\r\n```\r\nfiori_deploy_config\r\n appDir: <appDir> (relative to the project root)\r\n url: https://<host>:<port>\r\n client: <client>\r\n name: <BSP_NAME> (≤15 chars, UPPERCASE, Z/Y prefix)\r\n package: <PACKAGE>\r\n transport: <TRANSPORT> (required unless package is $TMP)\r\n description: \"<text>\"\r\n ignoreCertErrors: false (see TLS note below — default false)\r\n```\r\n\r\n**TLS / certificates — trust the CA, do not disable verification.** The\r\nproduction-grade answer for a self-signed / internal-CA SAP system is to TRUST\r\nthe corporate CA, not to turn verification off:\r\n\r\n- Configure the CA once: `cspeach config set sap.<alias>.ca_cert_path <path-to-corporate-CA.pem>`.\r\n Verification stays ON; the deploy proxy validates against that CA. Leave\r\n `ignoreCertErrors: false` (the default) in the deploy config.\r\n- Only when a dev box genuinely has no CA available, set\r\n `ignoreCertErrors: true` — this disables verification for the deploy proxy and\r\n the engine writes it with a `# dev only — configure a CA for production`\r\n comment. It is the explicit dev-only fallback, never the default. Mirror this\r\n with `cspeach config set sap.<alias>.insecure_skip_tls_verify on` for the\r\n ADT/REST side (CSPeach logs a loud warning on every connect when verification\r\n is disabled). NEVER use `NODE_TLS_REJECT_UNAUTHORIZED=0` — it disables TLS\r\n verification process-wide and silently, which is a security footgun.\r\n\r\nThis writes `ui5-deploy.yaml` and adds two scripts to the app's `package.json`:\r\n`deploy` (real) and `deploy-test` (backend validates WITHOUT deploying). The tool\r\nvalidates the BSP app name, package namespace, and transport format before\r\nwriting, and never writes credentials.\r\n\r\n**10c — Credentials.** `fiori deploy --username/--password` take the NAMES of\r\nenvironment variables, not values. Have the developer export them first (e.g.\r\n`FIORI_USER` / `FIORI_PASS`). NEVER write credentials into any file.\r\n\r\n**10d — Validate first, then deploy:**\r\n\r\n```bash\r\ncd <appDir>\r\nnpm run deploy-test -- --username FIORI_USER --password FIORI_PASS\r\n# testMode: backend checks package/transport/auth, deploys nothing\r\n\r\nnpm run deploy -- --username FIORI_USER --password FIORI_PASS --yes\r\n# the real deploy — only after deploy-test is green\r\n```\r\n\r\n**`--yes` is REQUIRED on the real deploy in any non-interactive shell.** Without\r\nit, `fiori deploy` waits forever on a hidden \"Start deployment (Y/n)?\" prompt and\r\nthe command hangs silently (observed live 2026-06-10). The Rule-8 user\r\nconfirmation already happened in step 10a — `--yes` here only suppresses the\r\nCLI's own duplicate prompt.\r\n\r\n**10e — Verify (Forge Rule 10).** The app must be reachable from the SAP system\r\nitself:\r\n\r\n```\r\nGET https://<host>:<port>/sap/bc/ui5_ui5/sap/<bsp_name_lowercase>/index.html\r\n```\r\n\r\nExpect HTTP 200. Report the served URL to the developer. A 403/404 usually means\r\nthe SICF node needs activating or the virus scan profile rejected the upload —\r\nsurface the exact backend message from the deploy log, do not guess.\r\n\r\n---\r\n\r\n## FE Mode (Fiori Elements scaffold against an existing published service)\r\n\r\nUse this mode when the developer wants a **Fiori Elements** app — a List Report,\r\nWorklist, Overview Page, or Analytical List Page — against an already-published\r\nservice. The base scaffold writes no hand-authored views or controllers; the\r\nrunning app renders from `@UI` annotations at runtime. But FE mode is NOT limited\r\nto whatever the backend already annotates: it authors those annotations (in the\r\nbackend metadata extension via `/abap-extend-model`, or as local EDMX for a\r\nforeign service) and reaches beyond annotations through V4 extension points\r\n(`fiori_fe_extend`). Work the ladder in the **FE Mode — Capabilities and Order of\r\nPreference** section below. The one boundary: beyond-annotation needs on an OVP or\r\nOData V2 app are NOT covered by `fiori_fe_extend` — those exit to freestyle.\r\n\r\n### When to Use FE Mode\r\n\r\n- \"Give me a quick List Report on my service\" / \"scaffold a Worklist for this\r\n entity\"\r\n- The service is already live and the developer only needs a thin Fiori Elements\r\n shell for prototyping or acceptance testing.\r\n- Charts (ALP / `ddlx-chart`), computed columns (`/abap-extend-model`), and V4\r\n controller/column extensions (`fiori_fe_extend`) are all in scope for FE mode.\r\n The boundary: beyond-annotation needs on an OVP or OData V2 app are freestyle\r\n territory (`fiori_fe_extend` is V4 FE only).\r\n\r\nDo NOT use FE mode if the backend service is not yet published — run\r\n`/abap-rap` first (Step 1 of the standard flow above redirects there too).\r\n\r\n### FE Mode — Steps\r\n\r\n**Step FE-1 — Confirm the service is published.**\r\n\r\nBefore scaffolding, verify the service responds. Call `sap_odata_test` (or a\r\nplain GET) against the service root. A 200 (or 401/403 with auth required, which\r\nstill means the service exists) confirms it is live. A 404 means the binding is\r\nnot published — stop and tell the developer to publish it in the service binding\r\neditor (or via `sap_service_binding_publish`) before continuing.\r\n\r\n**Step FE-2 — Call `fiori_scaffold_fe`.**\r\n\r\nCall the `fiori_scaffold_fe` tool. No `$metadata` fetch is needed before this\r\ncall — the FE writer does not require metadata to scaffold the shell; the\r\nrunning app reads `$metadata` from the live service at runtime. If the developer\r\nalready has the EDMX (e.g. downloaded earlier), they MAY pass it as the optional\r\n`metadata` arg — but it is not required and needs no auth handling.\r\n\r\n```\r\nfiori_scaffold_fe\r\n basePath: <appId> (relative to the project root)\r\n appId: <id> (reverse-domain, e.g. zso.orderlist)\r\n appTitle: \"<title>\"\r\n serviceUrl: <url> (e.g. https://host:44300)\r\n servicePath: <path> (relative OData service path)\r\n serviceVersion: <2.0|4.0>\r\n template: <lrop|worklist|ovp|alp>\r\n (lrop = List Report Page;\r\n worklist = Worklist;\r\n ovp = Overview Page card-less shell;\r\n alp = Analytical List Page — backend\r\n must expose an analytical query)\r\n mainEntity: <EntitySetName> (the primary entity to list)\r\n metadata: <edmx string> (optional — EDMX if already available;\r\n the app reads it live if omitted)\r\n```\r\n\r\nArgument reference for `fiori_scaffold_fe`:\r\n\r\n| Arg | Required | Description |\r\n|-----|----------|-------------|\r\n| `basePath` | Yes | Directory to scaffold into (relative to project root, created if missing). Sandboxed to the project root. |\r\n| `appId` | Yes | App namespace, e.g. `zso.orderlist` |\r\n| `appTitle` | No | Human-readable app title (defaults to `appId`) |\r\n| `serviceUrl` | Yes | Backend host, e.g. `https://host:44300` |\r\n| `servicePath` | Yes | Relative OData service path |\r\n| `serviceVersion` | Yes | `2.0` or `4.0` |\r\n| `template` | No | `lrop` (default = List Report Page), `worklist`, `ovp` (Overview Page), or `alp` (Analytical List Page — backend must expose an analytical query) |\r\n| `mainEntity` | Yes | EntitySet to list, e.g. `SalesOrders` |\r\n| `metadata` | No | EDMX string if the developer already has it; otherwise omit — the app reads `$metadata` live. Do NOT fetch `$metadata` just to pass it here. |\r\n\r\nThe tool writes a `webapp/` tree wired for Fiori Elements: `manifest.json`,\r\n`index.html`, `Component.js`, and the FLP sandbox configuration. For `ovp`,\r\nit scaffolds a card-less Overview Page shell — cards are contributed by backend\r\n`@UI.chart` and `@UI.presentationVariant` annotations at runtime, not authored\r\nhere.\r\n\r\n**Step FE-3 — Build.**\r\n\r\n```bash\r\ncd <appId>\r\nnpm install\r\nnpm run build\r\n```\r\n\r\nRead the real error from stderr if the build fails. Fix it and re-run. Do not\r\nreport success until `npm run build` exits 0.\r\n\r\n**Step FE-4 — Preview.**\r\n\r\n```bash\r\nnpm run start\r\n```\r\n\r\nOpen the browser at the served URL, then run the **render smoke gate** — call\r\n`fiori_render_smoke` against the local authenticated preview URL with\r\n`expectedColumns` derived from the authored `@UI.lineItem` fields (see the\r\n**Render smoke (ui.build gate)** section below). An empty list is a **FAIL**,\r\nnot an expected outcome — it passes only when `allowEmptyRows` is set and the\r\nacknowledgment is surfaced. If the smoke returns `verification: 'manual'` (no\r\nbrowser / flag off / preview unreachable), the gate is NOT verified — say so and\r\nrequire an explicit human preview confirmation. If columns are missing, the fix\r\nis annotations on the backend (`/abap-extend-model`) or a local EDMX (see the FE\r\nMode section), not here.\r\n\r\n### FE Mode — Capabilities and Order of Preference\r\n\r\nFE mode is no longer a thin shell that only renders whatever the backend already\r\nannotates. It now authors annotations and reaches into V4 extension points. Work\r\nthe ladder **top-down** — always prefer the least-invasive rung that satisfies\r\nthe requirement, and record in `work.notes` why you moved to a lower rung.\r\n\r\n1. **Backend-first (default) — annotate the stack we OWN.** For an un-annotated or\r\n under-annotated service on a custom stack we control, author the `@UI`\r\n annotations in the **metadata extension (MDE)** via `/abap-extend-model`\r\n (create-the-MDE-if-absent recipe). Name the mode for the annotation you need:\r\n `ddlx-headerinfo`, `ddlx-lineitem`, `ddlx-selectionfield`,\r\n `ddlx-identification`, `ddlx-facet`, `ddlx-datapoint`, `ddlx-chart`,\r\n `cds-valuehelp`, or `bdef-sideeffects`. This is the default because the\r\n annotation then lives with the model, is language-dependent, and every\r\n consumer of the service benefits — the FE app re-renders from it at runtime.\r\n\r\n2. **Foreign service — author LOCAL annotations (EDMX).** When the service is NOT\r\n modifiable (it belongs to someone else — a standard SAP service or another\r\n team's stack), pass the `localAnnotations` arg on `fiori_scaffold_fe` with\r\n skill-authored EDMX. The tool wires a LOCAL `ODataAnnotation` dataSource that\r\n the running app resolves at runtime, without touching the backend. See the\r\n **Local annotations (EDMX) authoring** subsection below. This rung requires\r\n STATING WHY the backend path was not used (service not modifiable).\r\n\r\n3. **Beyond annotations — `fiori_fe_extend` (V4 FE apps only).** When the\r\n requirement cannot be met by any annotation — a genuinely custom column\r\n renderer, a custom action handler, a custom object-page section, or a\r\n controller extension — use `fiori_fe_extend` (ops: `custom-column`,\r\n `custom-action`, `custom-section`, `controller-extension`). Rule: **annotations\r\n FIRST, extension SECOND** — reach for `fiori_fe_extend` only after confirming\r\n no annotation (rung 1 or 2) does the job, and record the reason in\r\n `work.notes`.\r\n\r\n **`fiori_fe_extend` requires a V4 Fiori Elements app — it does NOT cover OVP or\r\n OData V2 apps.** The writer refuses them with a typed error (`sap.fe.templates`\r\n is missing). This is a real exception to the \"no ceiling\" bar: for an OVP or V2\r\n app that needs something beyond annotations, the path is **freestyle (Track 3)**,\r\n NOT FE extension.\r\n\r\n4. **OVP stays card-annotation-driven.** `ovp` scaffolds the card-less shell; its\r\n cards are contributed by backend `@UI.chart` / `@UI.presentationVariant`\r\n annotations (rung 1) at runtime. `fiori_fe_extend` does not apply to OVP (see\r\n rung 3). **ALP joins the template list** (`alp` on `fiori_scaffold_fe`) — the\r\n backend must expose an analytical query for it; see\r\n `templates/analytical-layer.md`.\r\n\r\n- **Deploy is the same optional Step 10 as the freestyle flow** — gated, opt-in,\r\n with the same transport and Forge Rule 8/9 gates.\r\n\r\n### Local annotations (EDMX) authoring\r\n\r\nUse this ONLY on rung 2 — a FOREIGN service you cannot modify. Backend-first\r\n(rung 1) is the default; taking this path REQUIRES stating why the backend was\r\nnot modifiable (a standard SAP service / another team's stack). The render smoke\r\n(below) verifies the result **identically** to a backend-annotated app — the\r\ncolumns still have to appear.\r\n\r\nAuthor a complete local annotations document and pass it as\r\n`localAnnotations: { technicalName, xml }` to `fiori_scaffold_fe`. The tool\r\nwrites it to `webapp/annotations/<technicalName>.xml` and wires a local\r\n`ODataAnnotation` dataSource in the manifest. Worked example (annotate\r\n`SalesOrder` in service namespace `com.sap.gateway.srvd.zsalesorder_srv.v0001`):\r\n\r\n```xml\r\n<edmx:Edmx Version=\"4.0\"\r\n xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\"\r\n xmlns:edm=\"http://docs.oasis-open.org/odata/ns/edm\">\r\n <edmx:Reference Uri=\"https://sap.github.io/odata-vocabularies/vocabularies/UI.xml\">\r\n <edmx:Include Namespace=\"com.sap.vocabularies.UI.v1\" Alias=\"UI\"/>\r\n </edmx:Reference>\r\n <edmx:DataServices>\r\n <edm:Schema Namespace=\"local\">\r\n <edm:Annotations Target=\"com.sap.gateway.srvd.zsalesorder_srv.v0001.SalesOrder\">\r\n\r\n <edm:Annotation Term=\"UI.HeaderInfo\">\r\n <edm:Record>\r\n <edm:PropertyValue Property=\"TypeName\" String=\"Sales Order\"/>\r\n <edm:PropertyValue Property=\"TypeNamePlural\" String=\"Sales Orders\"/>\r\n <edm:PropertyValue Property=\"Title\">\r\n <edm:Record Type=\"UI.DataField\">\r\n <edm:PropertyValue Property=\"Value\" Path=\"SalesOrderID\"/>\r\n </edm:Record>\r\n </edm:PropertyValue>\r\n </edm:Record>\r\n </edm:Annotation>\r\n\r\n <edm:Annotation Term=\"UI.LineItem\">\r\n <edm:Collection>\r\n <edm:Record Type=\"UI.DataField\">\r\n <edm:PropertyValue Property=\"Value\" Path=\"SalesOrderID\"/>\r\n </edm:Record>\r\n <edm:Record Type=\"UI.DataField\">\r\n <edm:PropertyValue Property=\"Value\" Path=\"CustomerName\"/>\r\n </edm:Record>\r\n <edm:Record Type=\"UI.DataField\">\r\n <edm:PropertyValue Property=\"Value\" Path=\"NetAmount\"/>\r\n </edm:Record>\r\n </edm:Collection>\r\n </edm:Annotation>\r\n\r\n <edm:Annotation Term=\"UI.SelectionFields\">\r\n <edm:Collection>\r\n <edm:PropertyPath>SalesOrderID</edm:PropertyPath>\r\n <edm:PropertyPath>CustomerName</edm:PropertyPath>\r\n </edm:Collection>\r\n </edm:Annotation>\r\n\r\n </edm:Annotations>\r\n </edm:Schema>\r\n </edmx:DataServices>\r\n</edmx:Edmx>\r\n```\r\n\r\n`Target` must be the fully-qualified `<Namespace>.<EntityType>` from the service's\r\n`$metadata` (the EntityType, not the EntitySet). Derive the LineItem `Value` paths\r\nfrom the entity's real properties. The `expectedColumns` you feed the render smoke\r\nare exactly these LineItem fields.\r\n\r\n### Render smoke (ui.build gate)\r\n\r\nAfter the FE app builds and a local preview is up, run the render smoke as the\r\n`ui.build` gate — it is SKIPPABLE BUT NEVER SILENTLY GREEN.\r\n\r\nCall `fiori_render_smoke` with a `SmokeSpec`:\r\n\r\n```\r\nfiori_render_smoke\r\n appUrl: <LOCAL AUTHENTICATED PREVIEW url> (the npm run start /\r\n fiori run URL — NOT the\r\n deployed BSP url)\r\n expectedColumns: [ <fields from the authored @UI.lineItem> ]\r\n controlKind: table (cards for OVP)\r\n allowEmptyRows: false (see below)\r\n```\r\n\r\nIt returns a `SmokeResult { verification: 'smoke' | 'manual', passed, checks }`.\r\n\r\n- `appUrl` MUST be the **local authenticated preview URL** — the URL from\r\n `npm run start` / `fiori run` that proxies to the backend with the developer's\r\n auth. NEVER pass the deployed BSP URL: it serves a SAP logon page and 401s every\r\n OData call, which the smoke detects and treats as unreachable.\r\n- Derive `expectedColumns` from the `@UI.lineItem` fields you authored (rung 1 or\r\n rung 2). The smoke fails if those column headers do not render.\r\n- **`verification: 'manual'` means the gate did NOT run** — browser missing, the\r\n `render_smoke` flag is off, the preview is unreachable/offline, or the page was a\r\n SAP logon form. Treat it as \"gate NOT verified\": say so plainly and require an\r\n explicit human preview confirmation. It is NEVER a pass.\r\n- An **empty list is a FAIL.** It passes ONLY when `allowEmptyRows: true` is set\r\n deliberately AND the acknowledgment is surfaced to the developer.\r\n\r\nThe FREESTYLE flow (Steps 8/9) now runs this same gate — call `fiori_render_smoke`\r\nwith `appDir` set to the freestyle app, which derives the smoke spec from its own\r\nviews / i18n / manifest. The `verification: 'manual'` semantics are identical\r\nthere.\r\n\r\n---\r\n\r\n## Amend Mode (apply a bounded change to an EXISTING freestyle app)\r\n\r\nUse this mode to apply a change to a freestyle SAPUI5 app **already on disk** — an\r\nF4 value help, a validation, a file-upload control, a chart, a new section, a\r\nformatter, a new column, a new button. The scope is **any bounded change a\r\ndeveloper is handed against an existing freestyle app**; there is no fixed\r\nchange-type menu (a freestyle app's premise is arbitrary LLM-authored UI5). What\r\nbounds amend is the **workflow**, not the feature:\r\n\r\n- **Local** — operates on app files already on disk (`webapp/` tree). No remote\r\n state, no ADT writes.\r\n- **Additive** — extends an existing app; never rewrites the create/build path\r\n (Steps 1–10).\r\n- **Build-validated** — every amend ends green on `npm run build` or it is not\r\n done. The build is the hard gate, not a parser.\r\n- **Against an already-scaffolded freestyle app** — if no app exists yet, this is\r\n a *create*, handled by Steps 1–10.\r\n\r\n**No new engine, no edit tool.** The LLM authors the change directly (only the LLM\r\ncan author arbitrary UI5). The deterministic pieces are reused, not built:\r\nintrospection (Read/Glob), the existing `fiori_*` catalog renderer, and\r\n`npm run build`. **Honesty about the build:** `npm run build` validates JS/XML\r\nsyntax, well-formedness, and that `manifest.json` is parseable JSON. It is **blind\r\nto runtime correctness** — a typo'd binding path (`{Statuz}`), a `press=\".onApprove\"`\r\nwith no `onApprove` handler, a `navTo(\"RouteFoo\")` with no matching route, or a\r\nwrong i18n/formatter key all build clean and fail only at runtime. That gap is\r\nclosed by **A4-check** (pre-build cross-check) and the **A6 hard preview gate** —\r\nnot by the build.\r\n\r\n### A-input — Gather inputs (two entry paths)\r\n\r\nAmend has two callers, mirroring create Step 1:\r\n\r\n- **Delegated (from `/abap-plan`):** the app dir (`revision.app.dir`), the binding\r\n (`revision.binding`), the bound entity, and the specific change all arrive in the\r\n envelope — **use them, do not ask**.\r\n- **Standalone (developer types \"add a Priority column to my orders app\"):** ASK:\r\n 1. **Which app directory?** If the developer named an app but not a path, glob\r\n `**/webapp/manifest.json` under the project root: if exactly one match, confirm\r\n it (`Amend zso.orderlist at ./zso.orderlist? [y/n]`); if several, list them and\r\n ask which; if none, say so and stop (there is no freestyle app here — this may\r\n be a create).\r\n 2. **What is the change?** (free text — the LLM resolves it against A1.)\r\n 3. **Which entity/field/action** if not obvious from the change text.\r\n\r\nStandalone amend resolves its own `appDir` here; delegated amend gets it\r\npre-resolved. Everything downstream (A0–A7) is identical for both paths.\r\n\r\n### A0 — Detect amend vs. create (three-way)\r\n\r\nInspect the target `appDir` (resolved in A-input):\r\n\r\n- `webapp/manifest.json` **present** → **amend** (this section).\r\n- directory **empty or absent** → **create** — fall through to Steps 1–10 unchanged.\r\n- directory **non-empty but no `webapp/manifest.json`** → **stop and report**\r\n (almost always a wrong path or a half-scaffolded app). Never `fiori_scaffold`\r\n into a populated directory — that is undefined/destructive.\r\n\r\n### A1 — Read the existing app (introspection, read-only)\r\n\r\nBuild a ground-truth picture before editing anything:\r\n\r\n- `webapp/manifest.json` — dataSources, models, libs, `sap.app.id`, `sap.app.i18n`\r\n path, and the full `sap.ui5.routing` block.\r\n- **Build the route → target → view → controller map from `manifest.json`.** This\r\n is how you pick the *right* file to edit in a multi-page app rather than guessing.\r\n The map also pins the namespace coupling: a view's `controllerName` is\r\n `<sap.app.id>.controller.<Page>`, and any new controller must match `sap.app.id`\r\n or it binds to nothing (and still builds clean).\r\n- The specific view(s) and controller(s) the change targets\r\n (`webapp/view/*.view.xml`, `webapp/controller/*.controller.js`), read **in full**\r\n so edits anchor on real structure.\r\n- `webapp/i18n/i18n.properties` and `webapp/model/formatter.js` if present.\r\n- The backend `$metadata` (via `sap_odata_test` / GET) **only if** the change needs\r\n a field/entity/action the app does not yet bind — same metadata-first discipline\r\n as create Step 2 (coded/status fields → `sap.m.ObjectStatus`, never raw codes).\r\n\r\n### A2 — Present the amend plan (Forge Rule 8)\r\n\r\nA numbered list of every file that will be created or modified, **naming exactly\r\nwhich view/controller file each edit targets and why** (from the A1 route map), the\r\nexact change to each, and whether `fiori_apply` or a hand-authored edit is used. End\r\nwith:\r\n\r\n```\r\nPlan complete. This will modify [N] files. Proceed? [y/n]\r\n```\r\n\r\nWait for explicit `y`. **On `n`**, ask what to adjust and re-present the plan — do\r\n**not** apply anything. (Amend is iterative by nature; the re-plan loop matters more\r\nhere than in create.)\r\n\r\n### A3 — Safety: content snapshot (the rollback boundary)\r\n\r\nBefore editing ANYTHING, snapshot every file the A2 plan will touch: copy each\r\ntarget file's current bytes to `~/.cspeach/amend-backup/<appId>/<relative path>`\r\n(create the folder; clear any stale backup from a previous amend of the same app\r\nfirst). Record which files were snapshotted and which the amend will CREATE\r\n(new files have no snapshot — rollback deletes them).\r\n\r\nThis snapshot — not git — is the rollback boundary, and it is deliberately\r\ngit-independent: with stacked uncommitted amends, a git-based restore\r\n(`git stash` / `git checkout -- <appDir>`) reverts to the last *commit*,\r\nsilently discarding earlier uncommitted amend work along with this change.\r\nWorse, running `git stash -u -- <appDir>` at A3 time on such a tree strips the\r\nprior uncommitted amends from the working tree BEFORE this amend even starts —\r\nso never stash the app dir as the safety step. If the app dir is fully clean in\r\ngit at A3, a stash adds nothing; if it is dirty, a stash is destructive. The\r\ncontent snapshot is correct in both cases.\r\n\r\nIf A4 turns out to need a file NOT in the A2 plan, snapshot that file first\r\n(extend the backup), then edit it.\r\n\r\n### A4 — Apply the change\r\n\r\nThree sub-cases, chosen by the LLM per the request:\r\n\r\n1. **Catalog-renderable** (chart, F4 value-help) → `fiori_apply` with the entry's\r\n params (read from `fiori_list` first). Deterministic render + merge + i18n with\r\n `resolveSafePath` containment — reused outright. **Note:** because the target is\r\n a hand-authored app (not a fresh scaffold), `fiori_apply`'s `mergeManifest` *can*\r\n hit a genuine conflict (a fragment id or target name colliding with existing app\r\n structure). That loud error is an **expected outcome in amend, not a CSPeach\r\n bug** — surface it as \"this catalog entry collides with your app; resolve by\r\n hand,\" do not present it as a tool failure.\r\n2. **Recipe-shaped edit** → follow the relevant Common Pattern Recipe below, adapted\r\n to the real entity from A1. Two flavors with different manifest risk:\r\n - *Edit existing recipe output* (add a column to a recipe-1 list view, add a\r\n field to a recipe-2 detail form) — view/controller edits only; usually no\r\n manifest change.\r\n - *Add new recipe output* (a confirm dialog, a toast, a new page) — writes new\r\n files **and** wires the manifest; apply the parse-modify-write + no-overwrite\r\n assertion below.\r\n3. **Freeform** (anything else — a validation, an upload control, a bespoke section,\r\n a chart drilling into a second entity, inline editing) → the LLM authors the UI5\r\n from its knowledge + the recipes' control vocabulary, editing the existing files\r\n in place. For an unfamiliar control, ground it in a real OpenUI5 sample first —\r\n `fiori_sample_search` then `fiori_sample_get`, as create Step 6.5 does — and use\r\n the `sap-docs` MCP world (UI5 control APIs) only as the fallback when no sample\r\n matches. Freeform's *correctness is not build-guaranteed* — it\r\n rests on **A4-check + the A6 hard preview**; say so to the user for novel\r\n controls. **Wiring beyond routes/targets:** some freeform changes touch a *second\r\n OData model* (drilling into another entity), an *upload model*, or edit-mode.\r\n These need a new `dataSources` + `models` entry in `manifest.json` AND a matching\r\n per-service `backend:` entry in `ui5.yaml` — see the **Multi-service apps**\r\n footgun (one `backend:` entry per service, each with its own `path` prefix). The\r\n no-overwrite rule below applies to these manifest additions too.\r\n\r\n**Manifest edits — parse-modify-write + no-overwrite (never blind-append).** Amend\r\nis *not* symmetric with create: in create you wire a route into a manifest you just\r\nscaffolded and fully control; in amend you merge into a manifest with pre-existing\r\nroutes/targets/models you did not author — exactly where silent last-wins clobbering\r\nand duplicate-route collisions happen. For any hand-authored manifest edit: **parse\r\nthe existing `manifest.json` as JSON, add the new route/target/lib/dataSource/model,\r\nand assert no existing `routes[].name` or `targets` key (or `dataSources`/`models`\r\nkey) is being overwritten** before writing. Where a catalog entry covers the change\r\n(chart, F4), use `fiori_apply` so the deterministic `mergeManifest`/containment is\r\nreused outright (A4 case 1).\r\n\r\n### A4-check — Pre-build cross-check (closes the build's runtime blind spot)\r\n\r\nBefore building, verify what the build cannot:\r\n\r\n- every `.onX` press/event handler resolves to a method in the controller;\r\n- every `.formatter.X` binding resolves to a function in `formatter.js`;\r\n- every binding path (`{/EntitySet}`, `{Property}`) matches the A1 metadata;\r\n- every `navTo`/route name matches a real route.\r\n\r\nThese are exactly the errors that build clean and fail at runtime.\r\n\r\n### A5 — Re-validate\r\n\r\n```bash\r\ncd <appDir> && npm run build\r\n```\r\n\r\nRead the real stderr on failure, fix, re-run. **Not done until the build exits 0.**\r\nBound the fix loop: **after 3 failed fix attempts, stop** and go to the failure\r\nterminus below — do not loop indefinitely on the user's app.\r\n\r\n### A6 — Preview and Render Smoke (HARD gate, not a courtesy)\r\n\r\nFirst **detect** whether a preview is already serving this app — request the\r\napp's configured URL (the port from `ui5.yaml` `server.port` / the start script,\r\ndefault `http://localhost:8080`) and check for an HTTP answer. If one responds,\r\n**reuse it** — never spawn a second server (the duplicate lands on :8081 and\r\nyou end up smoking the wrong instance). Only when nothing is serving:\r\n\r\n```bash\r\nnpm run start\r\n```\r\n\r\nThen run the render smoke against it as the amend gate — the same freestyle gate\r\nas create Step 9:\r\n\r\n```\r\nfiori_render_smoke\r\n appDir: <appDir> (derives the spec from the app's OWN views / i18n /\r\n manifest — including press exercises via real DOM\r\n selectors and route exercises via router.navTo)\r\n appUrl: <LOCAL AUTHENTICATED PREVIEW url>\r\n```\r\n\r\nThe smoke exercises the amended app's real handlers and routes for you: a press\r\nhandler that throws or a `navTo` that fails is a **check failure**. It returns\r\n`warnings[]` for anything skipped (an id-less press, a parameterized route) —\r\nsurface them and confirm the skipped element by hand. Confirm the smoke renders\r\n**the specific changed element** with real rows from the live service.\r\n\r\n**Manual fallback.** If the smoke returns `verification: 'manual'` (no browser,\r\nflag off, or preview unreachable) — identical semantics to FE and create-mode — the\r\ngate did NOT run and is NEVER a pass. Fall back to confirming by hand: render the\r\nchanged element and, for an added interactive control (button/handler/dialog),\r\n**exercise it once** to prove the runtime wiring the build could not check. The\r\namend is not done until the changed element is seen working. If it cannot be made to\r\nrender, go to the failure terminus.\r\n\r\n### Failure terminus & rollback\r\n\r\nThe A3 content snapshot is the recovery boundary, and it must actually be\r\n**used** — not just provisioned. On any unrecoverable failure (A4 / A4-check /\r\nA5 after 3 attempts / A6 cannot render), or on a partial apply where some files\r\nwere edited and the app is now broken:\r\n\r\n1. **Restore the app to its pre-amend state from the snapshot** — copy every\r\n backed-up file from `~/.cspeach/amend-backup/<appId>/` back over its target,\r\n and DELETE any file the amend newly created (recorded at A3). Do NOT restore\r\n via `git stash` / `git checkout -- <appDir>` — on a tree with earlier\r\n uncommitted amends that reverts to the last commit and destroys them; the\r\n snapshot restores exactly the pre-amend bytes regardless of git state.\r\n2. **Report** the exact failing error (real stderr / the unresolved binding), what\r\n was attempted, and that the app **was reverted** — so the user is never left with\r\n a half-amended, broken app.\r\n\r\n### A7 — Deploy (optional, gated)\r\n\r\nUnchanged **Step 10** — an amended app deploys to BSP on a transport exactly as a\r\ncreated one does.\r\n\r\n### Amend Output Structure\r\n\r\nAmend inherits the **mandatory TL;DR** (`<!-- csforge:tldr-required -->`) unchanged.\r\nBut the body answers a different question — *\"what did you just do to my existing\r\napp?\"* — so create's \"Pages Built\" is replaced with **Files Changed**:\r\n\r\n```\r\n## abap-fiori-build (amend) — {AppId}\r\n\r\n## TL;DR\r\n...\r\n\r\n---\r\n\r\n## Amend Plan (the A2 plan, as confirmed)\r\n\r\n## Files Changed\r\n| File | Edit | Why this file (from A1 route map) |\r\n|------|------|-----------------------------------|\r\n| view/SalesOrderList.view.xml | +<Column> Priority + cell | RouteList → target List → this view |\r\n| controller/... | +onApprove handler | controllerName matches sap.app.id |\r\n\r\n## Pre-build cross-check (A4-check)\r\nhandlers / formatters / binding paths / routes — all resolve: confirmed\r\n\r\n## Build Validation\r\nnpm run build → OK (or: FAILED — error; app reverted from snapshot)\r\n\r\n## Preview\r\nnpm run start → http://localhost:8080 — Priority column renders; Approve exercised: confirmed\r\n\r\n## Rollback status\r\nsnapshot: restored / not needed (build green)\r\n```\r\n\r\n### Amend Example Prompt\r\n\r\n```\r\n/abap-fiori-build\r\n\r\nAmend my existing app at ./zso.salesorderapp:\r\nadd a Priority column to the sales-order list, rendered as a coloured label\r\n(the backend now exposes a Priority code on the SalesOrders entity).\r\n```\r\n\r\n### Amend Example Output Outline\r\n\r\n```\r\n## abap-fiori-build (amend) — zso.salesorderapp\r\n\r\n## TL;DR\r\n\r\n**Headline:** Added a coloured Priority column to the existing SalesOrderList page.\r\n\r\n**Top 3:**\r\n1. view/SalesOrderList.view.xml — new <Column> + ObjectStatus cell on SalesOrders.\r\n2. model/formatter.js — formatPriorityText/State added for the Priority code.\r\n3. A4-check + preview confirm the column renders with real Priority values.\r\n\r\n**Verdict:** Build green, Priority column visible with live rows. App was not reverted.\r\n\r\n---\r\n\r\n## Amend Plan\r\n\r\n| # | File | Edit | Why this file |\r\n|---|------|------|---------------|\r\n| 1 | view/SalesOrderList.view.xml | +Priority column + ObjectStatus cell | RouteSalesOrderList → target → this view |\r\n| 2 | model/formatter.js | +formatPriorityText / formatPriorityState | coded field → label + state |\r\n| (no manifest change — view/controller edits only)\r\n\r\nPlan complete. This will modify 2 files. Proceed? [y/n]\r\ny — proceeding.\r\n\r\n## Files Changed\r\n...\r\n\r\n## Pre-build cross-check (A4-check)\r\n.formatter.formatPriorityText / formatPriorityState → present in formatter.js;\r\n{Priority} → exists on SalesOrders ($metadata); no new route/handler: confirmed\r\n\r\n## Build Validation\r\nnpm run build → OK\r\n\r\n## Preview\r\nnpm run start → http://localhost:8080 — Priority column renders coloured labels with real rows: confirmed\r\n\r\n## Rollback status\r\nsnapshot: not needed (build green)\r\n```\r\n\r\n---\r\n\r\n## Common Pattern Recipes\r\n\r\nThese patterns are generated fresh each run. They are NOT catalog entries.\r\nWrite the view and controller files using Write/Edit, then wire `manifest.json`\r\nper the anatomy section. Adapt all field/column names to the actual entity.\r\n\r\n> **Coded fields are not optional polish.** RAP/SEGW services constantly expose\r\n> short codes (status `N`/`A`/`R`, priority `1`–`4`, type codes, fixed-value\r\n> domains). Binding the raw code (`<Text text=\"{Status}\"/>`) ships an app that\r\n> shows `N` instead of `New`. Whenever a column or field is a coded/status/domain\r\n> field, use the **Coded / Status Fields** recipe below — both the list page\r\n> (Recipe 1) and the detail page (Recipe 2) already demonstrate the pattern.\r\n\r\n---\r\n\r\n### Recipe 1 — List/Table Page\r\n\r\n**Controls:** `sap.m.Page` > `sap.m.Table` bound to an EntitySet. One\r\n`sap.m.Column` per displayed property. A `sap.m.SearchField` in the header\r\ntoolbar for client-side filtering.\r\n\r\n**View — `webapp/view/<Page>.view.xml`:**\r\n\r\n```xml\r\n<mvc:View\r\n controllerName=\"<appId>.controller.<Page>\"\r\n xmlns:mvc=\"sap.ui.core.mvc\"\r\n xmlns=\"sap.m\"\r\n displayBlock=\"true\">\r\n <Page title=\"{i18n><page>.title}\">\r\n <content>\r\n <Table id=\"table\"\r\n items=\"{/<EntitySet>}\"\r\n growing=\"true\"\r\n growingThreshold=\"30\">\r\n <headerToolbar>\r\n <OverflowToolbar>\r\n <Title text=\"{i18n><page>.title}\" level=\"H2\"/>\r\n <ToolbarSpacer/>\r\n <SearchField width=\"20rem\" search=\".onSearch\"/>\r\n </OverflowToolbar>\r\n </headerToolbar>\r\n <columns>\r\n <Column><Text text=\"{i18n><page>.col.<field1>}\"/></Column>\r\n <Column><Text text=\"{i18n><page>.col.<statusField>}\"/></Column>\r\n </columns>\r\n <items>\r\n <ColumnListItem press=\".onItemPress\" type=\"Navigation\">\r\n <cells>\r\n <!-- plain Text for an ordinary (non-coded) field -->\r\n <Text text=\"{<field1>}\"/>\r\n <!-- coded field: friendly label + semantic colour, NOT the raw code.\r\n formatStatusText / formatStatusState live in formatter.js -->\r\n <ObjectStatus\r\n text=\"{ path: '<statusField>', formatter: '.formatter.formatStatusText' }\"\r\n state=\"{ path: '<statusField>', formatter: '.formatter.formatStatusState' }\"/>\r\n </cells>\r\n </ColumnListItem>\r\n </items>\r\n </Table>\r\n </content>\r\n </Page>\r\n</mvc:View>\r\n```\r\n\r\n**Controller — `webapp/controller/<Page>.controller.js`:**\r\n\r\n```javascript\r\nsap.ui.define([\r\n \"sap/ui/core/mvc/Controller\",\r\n \"sap/ui/model/Filter\",\r\n \"sap/ui/model/FilterOperator\",\r\n \"<appId>/model/formatter\"\r\n], function (Controller, Filter, FilterOperator, formatter) {\r\n \"use strict\";\r\n return Controller.extend(\"<appId>.controller.<Page>\", {\r\n formatter: formatter, // expose for {formatter: '.formatter.xxx'} bindings in the view\r\n onInit: function () {\r\n var oRouter = this.getOwnerComponent().getRouter();\r\n oRouter.getRoute(\"Route<Page>\").attachPatternMatched(this._onRouteMatched, this);\r\n },\r\n _onRouteMatched: function () {\r\n this.getView().getModel().refresh();\r\n },\r\n onSearch: function (oEvent) {\r\n var sQuery = oEvent.getParameter(\"query\");\r\n var oTable = this.byId(\"table\");\r\n var oBinding = oTable.getBinding(\"items\");\r\n oBinding.filter(\r\n sQuery\r\n ? [new Filter(\"<field1>\", FilterOperator.Contains, sQuery)]\r\n : []\r\n );\r\n },\r\n onItemPress: function (oEvent) {\r\n var oItem = oEvent.getSource().getBindingContext();\r\n this.getOwnerComponent().getRouter().navTo(\"Route<DetailPage>\", {\r\n key: encodeURIComponent(oItem.getProperty(\"<KeyProperty>\"))\r\n });\r\n }\r\n });\r\n});\r\n```\r\n\r\n**manifest.json wiring** (add inside `sap.ui5.routing`):\r\n\r\n```json\r\n\"targets\": {\r\n \"<Page>\": { \"viewName\": \"<Page>\", \"viewType\": \"XML\", \"viewLevel\": 1 }\r\n},\r\n\"routes\": [\r\n { \"name\": \"Route<Page>\", \"pattern\": \":?query:\", \"target\": [\"<Page>\"] }\r\n]\r\n```\r\n\r\n---\r\n\r\n### Recipe 2 — Object/Detail Page\r\n\r\n**Controls:** `sap.uxap.ObjectPageLayout` for rich detail with sections; use\r\n`sap.m.Page` + `sap.ui.layout.form.SimpleForm` for simpler cases. Bound to a\r\nsingle entity by its key property. Route pattern includes `/{key}`.\r\n\r\n**View — `webapp/view/<Page>.view.xml`:**\r\n\r\n```xml\r\n<mvc:View\r\n controllerName=\"<appId>.controller.<Page>\"\r\n xmlns:mvc=\"sap.ui.core.mvc\"\r\n xmlns=\"sap.m\"\r\n xmlns:uxap=\"sap.uxap\"\r\n xmlns:layout=\"sap.ui.layout.form\"\r\n displayBlock=\"true\">\r\n <uxap:ObjectPageLayout id=\"objectPage\">\r\n <uxap:headerTitle>\r\n <uxap:ObjectPageHeader\r\n objectTitle=\"{<TitleField>}\"\r\n objectSubtitle=\"{<SubtitleField>}\"/>\r\n </uxap:headerTitle>\r\n <uxap:sections>\r\n <uxap:ObjectPageSection title=\"{i18n><page>.detailsSection}\">\r\n <uxap:subSections>\r\n <uxap:ObjectPageSubSection>\r\n <uxap:blocks>\r\n <layout:SimpleForm editable=\"false\" layout=\"ResponsiveGridLayout\">\r\n <layout:content>\r\n <!-- ordinary field: plain Text -->\r\n <Label text=\"{i18n><page>.col.<field1>}\"/>\r\n <Text text=\"{<field1>}\"/>\r\n <!-- coded field: friendly label + semantic colour, NOT the raw code -->\r\n <Label text=\"{i18n><page>.col.<statusField>}\"/>\r\n <ObjectStatus\r\n text=\"{ path: '<statusField>', formatter: '.formatter.formatStatusText' }\"\r\n state=\"{ path: '<statusField>', formatter: '.formatter.formatStatusState' }\"/>\r\n </layout:content>\r\n </layout:SimpleForm>\r\n </uxap:blocks>\r\n </uxap:ObjectPageSubSection>\r\n </uxap:subSections>\r\n </uxap:ObjectPageSection>\r\n </uxap:sections>\r\n </uxap:ObjectPageLayout>\r\n</mvc:View>\r\n```\r\n\r\n**Controller — `webapp/controller/<Page>.controller.js`:**\r\n\r\n```javascript\r\nsap.ui.define([\r\n \"sap/ui/core/mvc/Controller\",\r\n \"<appId>/model/formatter\"\r\n], function (Controller, formatter) {\r\n \"use strict\";\r\n return Controller.extend(\"<appId>.controller.<Page>\", {\r\n formatter: formatter, // expose for {formatter: '.formatter.xxx'} bindings in the view\r\n onInit: function () {\r\n var oRouter = this.getOwnerComponent().getRouter();\r\n oRouter.getRoute(\"Route<Page>\").attachPatternMatched(this._onRouteMatched, this);\r\n },\r\n _onRouteMatched: function (oEvent) {\r\n var sKey = decodeURIComponent(oEvent.getParameter(\"arguments\").key);\r\n this.getView().bindElement({\r\n path: \"/<EntitySet>(<KeyProperty>='\" + sKey + \"')\",\r\n parameters: { expand: \"\" }\r\n });\r\n }\r\n });\r\n});\r\n```\r\n\r\n**manifest.json wiring** — adds to `sap.ui5.dependencies.libs`:\r\n\r\n```json\r\n\"sap.uxap\": {},\r\n\"sap.ui.layout\": {}\r\n```\r\n\r\nAnd to `sap.ui5.routing`:\r\n\r\n```json\r\n\"targets\": {\r\n \"<Page>\": { \"viewName\": \"<Page>\", \"viewType\": \"XML\", \"viewLevel\": 2 }\r\n},\r\n\"routes\": [\r\n { \"name\": \"Route<Page>\", \"pattern\": \"<page>/{key}\", \"target\": [\"<Page>\"] }\r\n]\r\n```\r\n\r\n---\r\n\r\n### Recipe — Coded / Status Fields (apply to every coded field)\r\n\r\n**Problem:** RAP/SEGW services expose coded fields — a status as `N`/`A`/`R`, a\r\npriority as `1`–`4`, type codes, fixed-value domains. Bound raw, the app shows\r\nthe bare code. Every coded field must render as a friendly **label** with a\r\nsemantic **colour**.\r\n\r\n**Control:** `sap.m.ObjectStatus`, which carries both `text` (the label) and\r\n`state` (the colour). Valid `state` values are exactly: `Success` (green),\r\n`Warning` (amber), `Error` (red), `Information` (blue), `None` (neutral).\r\n\r\n```xml\r\n<ObjectStatus\r\n text=\"{ path: '<statusField>', formatter: '.formatter.formatStatusText' }\"\r\n state=\"{ path: '<statusField>', formatter: '.formatter.formatStatusState' }\"/>\r\n```\r\n\r\n**Where the labels come from — metadata first, hardcode last:**\r\n\r\n1. **PREFER the service's own text.** If `$metadata`/CDS exposes a text for the\r\n code (a text association, `@ObjectModel.text.element`, or a value-help/text\r\n entity — captured in Step 2), **bind that text directly** and skip the\r\n formatter for the label:\r\n ```xml\r\n <ObjectStatus\r\n text=\"{<statusField>_Text}\"\r\n state=\"{ path: '<statusField>', formatter: '.formatter.formatStatusState' }\"/>\r\n ```\r\n Only the colour then needs a formatter; the label is real, language-dependent\r\n text from the backend.\r\n\r\n2. **Otherwise build a code→{text,state} map** in `formatter.js`. Derive the\r\n actual codes, labels, and sensible colours from the fixed-value domain or the\r\n service metadata captured in Step 2 — the example below is ILLUSTRATIVE, not a\r\n spec. If you could not find the labels in metadata, hardcode them AND leave the\r\n warning comment so the developer knows to verify.\r\n\r\n**Formatter — `webapp/model/formatter.js`:**\r\n\r\n```javascript\r\nsap.ui.define([], function () {\r\n \"use strict\";\r\n\r\n // WARNING: codes/labels below were NOT found in the service $metadata\r\n // (no text association / value-help / fixed-value domain exposed).\r\n // They were hardcoded from the requirement — VERIFY against the domain\r\n // (transaction SE11 / the CDS source) before shipping.\r\n // Derived from metadata where available; see Step 2 of abap-fiori-build.\r\n var STATUS = {\r\n \"N\": { text: \"New\", state: \"Information\" },\r\n \"A\": { text: \"Approved\", state: \"Success\" },\r\n \"R\": { text: \"Rejected\", state: \"Error\" }\r\n };\r\n\r\n var PRIORITY = {\r\n \"1\": { text: \"Very High\", state: \"Error\" },\r\n \"2\": { text: \"High\", state: \"Warning\" },\r\n \"3\": { text: \"Medium\", state: \"Information\" },\r\n \"4\": { text: \"Low\", state: \"None\" }\r\n };\r\n\r\n return {\r\n formatStatusText: function (sCode) {\r\n var o = STATUS[sCode];\r\n return o ? o.text : (sCode || \"\"); // fall back to the raw code, never blank\r\n },\r\n formatStatusState: function (sCode) {\r\n var o = STATUS[sCode];\r\n return o ? o.state : \"None\";\r\n },\r\n formatPriorityText: function (sCode) {\r\n var o = PRIORITY[sCode];\r\n return o ? o.text : (sCode || \"\");\r\n },\r\n formatPriorityState: function (sCode) {\r\n var o = PRIORITY[sCode];\r\n return o ? o.state : \"None\";\r\n }\r\n };\r\n});\r\n```\r\n\r\n**Controller wiring** — import the formatter and expose it on the controller so\r\n`{ formatter: '.formatter.xxx' }` bindings resolve (already shown in Recipes 1\r\nand 2):\r\n\r\n```javascript\r\nsap.ui.define([\r\n \"sap/ui/core/mvc/Controller\",\r\n \"<appId>/model/formatter\"\r\n], function (Controller, formatter) {\r\n \"use strict\";\r\n return Controller.extend(\"<appId>.controller.<Page>\", {\r\n formatter: formatter,\r\n // ...\r\n });\r\n});\r\n```\r\n\r\n**manifest.json wiring** — none (no route/target). `formatter.js` is a plain\r\nmodule loaded by the controllers; ensure it is written to `webapp/model/formatter.js`.\r\n`sap.m` (which provides `ObjectStatus`) is already declared by the scaffold.\r\n\r\n**i18n keys** — none required if labels come from the formatter map or a bound\r\nbackend text. If you instead key labels off i18n, add one entry per code.\r\n\r\n---\r\n\r\n### Recipe 3 — Confirm Dialog\r\n\r\n**Controls:** `sap.m.Dialog` (type=\"Message\") with a `sap.m.TextArea` for a\r\nreason field and begin/end `sap.m.Button` elements. Loaded lazily via\r\n`sap.ui.core.Fragment.load` from a controller mixin. The host controller calls\r\n`openConfirmDialog()` and receives the result (reason string or `null`) via a\r\ncallback method.\r\n\r\n**Fragment — `webapp/ext/fragment/<Ns>ConfirmDialog.fragment.xml`:**\r\n\r\n```xml\r\n<core:FragmentDefinition\r\n xmlns=\"sap.m\"\r\n xmlns:core=\"sap.ui.core\">\r\n <Dialog\r\n id=\"<Ns>ConfirmDialog\"\r\n title=\"{i18n><Ns>.confirmTitle}\"\r\n type=\"Message\">\r\n <content>\r\n <Text\r\n text=\"{i18n><Ns>.confirmPrompt}\"\r\n class=\"sapUiSmallMarginBottom\"/>\r\n <TextArea\r\n id=\"<Ns>ConfirmReason\"\r\n rows=\"4\"\r\n width=\"100%\"\r\n placeholder=\"{i18n><Ns>.reasonPlaceholder}\"/>\r\n </content>\r\n <beginButton>\r\n <Button\r\n text=\"{i18n><Ns>.confirm}\"\r\n type=\"Emphasized\"\r\n press=\".on<Ns>Confirm\"/>\r\n </beginButton>\r\n <endButton>\r\n <Button\r\n text=\"{i18n><Ns>.cancel}\"\r\n press=\".on<Ns>Cancel\"/>\r\n </endButton>\r\n </Dialog>\r\n</core:FragmentDefinition>\r\n```\r\n\r\n**Controller mixin — `webapp/controller/ext/<Ns>ConfirmDialog.js`:**\r\n\r\n```javascript\r\nsap.ui.define([\"sap/ui/core/Fragment\"], function (Fragment) {\r\n \"use strict\";\r\n return {\r\n open<Ns>ConfirmDialog: function () {\r\n var oView = this.getView();\r\n if (!this._<Ns>Dlg) {\r\n this._<Ns>Dlg = Fragment.load({\r\n id: oView.getId(),\r\n name: \"<appId>.ext.fragment.<Ns>ConfirmDialog\",\r\n controller: this\r\n }).then(function (o) { oView.addDependent(o); return o; });\r\n }\r\n this._<Ns>Dlg.then(function (o) { o.open(); });\r\n },\r\n on<Ns>Confirm: function () {\r\n var sReason = this.byId(\"<Ns>ConfirmReason\").getValue();\r\n this._<Ns>Dlg.then(function (o) { o.close(); });\r\n if (this[\"<callback>\"]) { this[\"<callback>\"](sReason); }\r\n },\r\n on<Ns>Cancel: function () {\r\n this._<Ns>Dlg.then(function (o) { o.close(); });\r\n if (this[\"<callback>\"]) { this[\"<callback>\"](null); }\r\n }\r\n };\r\n});\r\n```\r\n\r\nIn the host controller, mix these methods in via `Object.assign(this, <Ns>ConfirmDialogMixin)`\r\nin `onInit`, then call `this.open<Ns>ConfirmDialog()` from a button press handler.\r\n\r\n**manifest.json wiring** — no route or target. Only ensure `sap.m` is in libs\r\n(the scaffold already includes it).\r\n\r\n**i18n keys to add to `i18n/i18n.properties`:**\r\n\r\n```\r\n<Ns>.confirmTitle=Confirm <ActionLabel>\r\n<Ns>.confirmPrompt=Please provide a reason:\r\n<Ns>.reasonPlaceholder=Enter reason…\r\n<Ns>.confirm=<ActionLabel>\r\n<Ns>.cancel=Cancel\r\n```\r\n\r\n---\r\n\r\n### Recipe 4 — Message Toast\r\n\r\n**Controls:** `sap/m/MessageToast`. No view changes, no route. Import\r\n`MessageToast` in the controller and call `MessageToast.show(...)`.\r\n\r\n**Add to any existing controller:**\r\n\r\n```javascript\r\nsap.ui.define([\r\n \"sap/ui/core/mvc/Controller\",\r\n \"sap/m/MessageToast\"\r\n], function (Controller, MessageToast) {\r\n \"use strict\";\r\n return Controller.extend(\"<appId>.controller.<Page>\", {\r\n // ... existing methods ...\r\n\r\n show<Ns>Toast: function (sMessage) {\r\n MessageToast.show(sMessage || this.getView().getModel(\"i18n\")\r\n .getResourceBundle().getText(\"<Ns>.toast\"));\r\n }\r\n });\r\n});\r\n```\r\n\r\n**i18n keys:**\r\n\r\n```\r\n<Ns>.toast=Operation completed successfully\r\n```\r\n\r\n**manifest.json wiring** — none. `sap.m` is already declared by the scaffold.\r\n\r\n---\r\n\r\n## manifest.json Anatomy (for Bespoke Wiring)\r\n\r\nAfter `fiori_scaffold`, `manifest.json` has the primary dataSource and model\r\nwired already. When adding pages, extra models, or library dependencies by hand,\r\nuse this reference:\r\n\r\n### dataSources\r\n\r\n```json\r\n\"sap.app\": {\r\n \"dataSources\": {\r\n \"<name>\": {\r\n \"uri\": \"/sap/opu/odata4/<srv>/\",\r\n \"type\": \"OData\",\r\n \"settings\": { \"odataVersion\": \"4.0\" }\r\n }\r\n }\r\n}\r\n```\r\n\r\nThe `uri` must be the **relative path** (no host) so it falls under a\r\n`backend.path` prefix entry in `ui5.yaml`. Supplying the full host here breaks\r\nthe proxy middleware.\r\n\r\n### Models\r\n\r\nOData-backed model:\r\n\r\n```json\r\n\"sap.ui5\": {\r\n \"models\": {\r\n \"<name>\": { \"dataSource\": \"<name>\" }\r\n }\r\n}\r\n```\r\n\r\nClient-side JSON model (UI state, no backend):\r\n\r\n```json\r\n\"<name>\": { \"type\": \"sap.ui.model.json.JSONModel\" }\r\n```\r\n\r\n### Routing — Routes and Targets\r\n\r\n```json\r\n\"sap.ui5\": {\r\n \"routing\": {\r\n \"routes\": [\r\n { \"name\": \"Route<Page>\", \"pattern\": \":?query:\", \"target\": [\"<Page>\"] }\r\n ],\r\n \"targets\": {\r\n \"<Page>\": {\r\n \"viewName\": \"<Page>\",\r\n \"viewType\": \"XML\",\r\n \"viewLevel\": 1\r\n }\r\n }\r\n }\r\n}\r\n```\r\n\r\n`viewLevel` drives the back-navigation animation: 1 for list pages, 2 for detail\r\npages.\r\n\r\n### Library Dependencies\r\n\r\n```json\r\n\"sap.ui5\": {\r\n \"dependencies\": {\r\n \"libs\": {\r\n \"sap.m\": {},\r\n \"sap.uxap\": {},\r\n \"sap.ui.layout\": {},\r\n \"sap.viz\": {},\r\n \"sap.ui.comp\": {}\r\n }\r\n }\r\n}\r\n```\r\n\r\nDeclare every library that any view or controller requires. Missing entries cause\r\nruntime 404s even when the CDN has the library.\r\n\r\n---\r\n\r\n## Preview Footguns\r\n\r\nThese are the most common reasons the preview fails locally. Fix them before\r\nreporting a preview issue.\r\n\r\n**Proxy middleware key:** the middleware entry in `ui5.yaml` must use the key\r\n`fiori-tools-proxy` (the package name), not `fiori-proxy` or `proxy`.\r\n\r\n**Certificates — trust the CA, don't disable verification (preview proxy).** The\r\nproduction-grade path is to trust the corporate/internal CA so the preview proxy\r\nvalidates the SAP cert with verification ON. Point Node at the CA for the\r\npreview process:\r\n\r\n```bash\r\nNODE_EXTRA_CA_CERTS=/etc/ssl/corp-ca.pem npm run start\r\n```\r\n\r\nThis keeps TLS verification on. Do NOT reach for `NODE_TLS_REJECT_UNAUTHORIZED=0`\r\n— it disables verification process-wide and silently. Only on a dev box with no\r\nCA available, set `ignoreCertErrors: true` on the proxy `backend` entry as an\r\nexplicit dev-only fallback (and add a `# dev only` comment).\r\n\r\n**Certificate flag spelling (dev-only fallback):** if you do use it, the flag is\r\n`ignoreCertErrors: true` — PLURAL (`ignoreCertErrors`, not `ignoreCertError`).\r\nOne missing `s` silently bypasses nothing.\r\n\r\n**Client must be quoted:** SAP client must be written as the string `\"100\"`, not\r\nthe number `100`. Example (CA-trusted — verification on, no ignoreCertErrors):\r\n\r\n```yaml\r\nbackend:\r\n - url: https://host:44300\r\n path: /sap\r\n client: \"100\"\r\n # dev only — for a dev box with no CA, add: ignoreCertErrors: true\r\n```\r\n\r\n**Multi-service apps:** each additional service needs its own `backend:` entry in\r\n`ui5.yaml` with its own `path` prefix. One entry per service — they cannot share\r\na prefix.\r\n\r\n**UI5 CDN:** use `ui5.url: https://ui5.sap.com` (SAPUI5 distribution — includes\r\n`sap.viz` and `sap.ui.comp`). Do NOT switch to `sdk.openui5.org` — that is the\r\nOpenUI5 distribution and does not include `sap.viz` or `sap.ui.comp`. The chart\r\nand value-help entries will 404 at runtime if the wrong CDN is used.\r\n\r\n**dataSource uri must be relative:** the `uri` in `sap.app.dataSources` is the\r\npath segment only (e.g. `/sap/opu/odata4/zso/srv/`). The `backend.url` in\r\n`ui5.yaml` supplies the host. If you put the full URL in `uri`, the proxy\r\nmiddleware cannot intercept it and the browser sees CORS errors.\r\n\r\n**Corrupted node_modules:** `npm run build` / `npm run start` failing with\r\n`Cannot find module './…'` thrown from *inside a nested dependency* (a require\r\ndeep under `node_modules/`, not from app code) means the install is corrupted,\r\nnot the app. Clean reinstall:\r\n`rm -rf node_modules package-lock.json && npm install`.\r\n\r\n---\r\n\r\n## Scope\r\n\r\n- **Two modes: freestyle (primary) and FE mode.** Freestyle authors arbitrary\r\n hand-written UI5. **FE mode** (the FE Mode section above) scaffolds Fiori\r\n Elements apps AND authors their `@UI.lineItem`/`@UI.headerInfo` annotations — in\r\n the backend metadata extension via `/abap-extend-model`, or as local EDMX for a\r\n foreign service — and reaches beyond annotations via `fiori_fe_extend` (V4 FE\r\n only). What this skill does NOT build is the RAP backend itself (tables, CDS\r\n views, BDEFs) — run `/abap-rap` for that, or `/abap-enhance` to extend\r\n SAP-standard objects. **Amend Mode** (below) applies *any bounded change to an\r\n existing freestyle app* in place — a column, a field, a control, a chart, a\r\n validation, a button — build-validated, not via a fixed change-type menu. FE\r\n Elements apps are not amended via Amend Mode (they re-render from annotations —\r\n edit those with `/abap-extend-model` or the FE mode local EDMX / extension path).\r\n- **Built and previewed locally; deploys to the ABAP repository.** Step 10\r\n ships the app as a BSP application on a transport (gated, opt-in). BTP /\r\n Cloud Foundry deploy is out of scope.\r\n- **No ADT writes.** This skill does not call `sap_set_source`,\r\n `sap_create_object`, or any write MCP tool. MCP is read-only here (metadata\r\n reads only).\r\n- **Requires a published backend.** If the OData service is not yet published,\r\n run `/abap-rap` first.\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## abap-fiori-build — [App Id / Service]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was built or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding or action — name the specific page, entity, or issue>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line conclusion or next step>\r\n\r\n---\r\n\r\n<then the full build log below>\r\n```\r\n\r\n## Output Structure\r\n\r\n```\r\n## abap-fiori-build — {AppId}\r\n\r\n## TL;DR\r\n...\r\n\r\n---\r\n\r\n## Build Plan\r\n\r\n| Step | Artifact | Notes |\r\n|------|----------|-------|\r\n| ... | ... | ... |\r\n\r\n---\r\n\r\n## Scaffold\r\n\r\nfiori_scaffold ... [tool call echoed]\r\n\r\n---\r\n\r\n## Pages Built\r\n\r\n### {Page1}\r\n- View: webapp/view/{Page1}.view.xml — written\r\n- Controller: webapp/controller/{Page1}.controller.js — written\r\n- manifest.json: route Route{Page1} + target {Page1} — wired\r\n\r\n### {Page2}\r\n...\r\n\r\n## Catalog Entries Applied\r\n\r\n| Entry | Params | Result |\r\n|-------|--------|--------|\r\n| viz-chart | {...} | written |\r\n\r\n## Build Validation\r\n\r\nnpm install → OK\r\nnpm run build → OK (or: FAILED — error text here)\r\n\r\n## Preview\r\n\r\nnpm run start → http://localhost:8080\r\nReal rows from {EntitySet}: confirmed / not confirmed\r\n\r\n## Manual Steps Remaining\r\n1. ...\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER scaffold or write files before presenting and confirming the plan (Forge Rule 8).\r\n- NEVER use `fiori_apply` for the simple patterns (list page, detail page,\r\n confirm dialog, message toast) — these are generated from recipes, not the catalog.\r\n- NEVER change `ui5.url` to `sdk.openui5.org` — chart and value-help entries\r\n require SAPUI5 CDN.\r\n- NEVER put the full backend URL in `sap.app.dataSources[].uri` — it must be the\r\n relative path.\r\n- NEVER bind a coded/status/domain field as a raw `<Text text=\"{Code}\"/>` — render\r\n it with `sap.m.ObjectStatus` (label + semantic state) via `webapp/model/formatter.js`\r\n or a backend text association. See the Coded / Status Fields recipe.\r\n- NEVER claim the preview is working without confirming real rows are visible in\r\n the browser.\r\n- NEVER call any MCP write tool from this skill — only reads are permitted.\r\n- If `$metadata` does not return 200 for a service, stop and report before building\r\n any pages against that service.\r\n- FE mode: NEVER fetch `$metadata` before calling `fiori_scaffold_fe` — the\r\n tool does not need it and the running app reads it live. Only pass the optional\r\n `metadata` arg if the developer explicitly provides an EDMX string.\r\n- FE mode annotation order of preference: author backend `@UI` annotations FIRST\r\n (MDE via `/abap-extend-model`); use skill-authored LOCAL EDMX (`localAnnotations`\r\n on `fiori_scaffold_fe`) only for a foreign service you cannot modify; use\r\n `fiori_fe_extend` (V4 FE apps ONLY — not OVP/V2) only for beyond-annotation\r\n needs, annotations first and extension second. See the FE Mode section.\r\n- FE mode: NEVER scaffold FE mode unless the service is confirmed live\r\n (Step FE-1). A 404 on the service root means the binding is unpublished — stop.\r\n- Amend mode: NEVER `fiori_scaffold` into a populated directory — if the target dir\r\n is non-empty without `webapp/manifest.json`, STOP and report (A0).\r\n- Amend mode: NEVER leave a half-amended, broken app — on any unrecoverable failure\r\n (A4 / A4-check / A5 after 3 attempts / A6 cannot render) or partial apply, restore\r\n the app from the A3 content snapshot (`~/.cspeach/amend-backup/<appId>/`, delete\r\n amend-created files) and report it was reverted (Failure terminus).\r\n- Amend mode: NEVER blind-append to `manifest.json` — parse-modify-write and assert\r\n no existing route/target/dataSource/model key is overwritten (A4).\r\n- Amend mode: the A6 preview is a HARD gate — render the changed element and exercise\r\n any added interactive control once; not done until it is seen working.\r\n- Amend mode: freeform correctness rests on A4-check + A6, NOT on the build —\r\n `npm run build` is blind to binding paths, handler names, routes, and i18n keys.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-fiori-build\r\n\r\nService: https://dev-sap.mycompany.com:44300, path /sap/opu/odata4/zso/salesorder_srv/, version 4.0\r\nApp: zso.salesorderapp, \"Sales Order App\"\r\nPages:\r\n - List of sales orders (SalesOrders entity, columns: SalesOrderId, CustomerName, NetAmount, Status)\r\n - Detail for one order (SalesOrder entity by key, all fields in a form)\r\n - Approval dialog with a reason field, triggered from the detail page\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## abap-fiori-build — zso.salesorderapp\r\n\r\n## TL;DR\r\n\r\n**Headline:** Scaffolded 3-page freestyle UI5 app on SalesOrder OData V4 service with confirm dialog.\r\n\r\n**Top 3:**\r\n1. List page (SalesOrderList) bound to SalesOrders with search on CustomerName — ready.\r\n2. Detail page (SalesOrderDetail) binds by SalesOrderId key — ObjectPageLayout, all fields.\r\n3. Confirm dialog (ApproveConfirmDialog) wired from detail page — reason TextArea + callback.\r\n\r\n**Verdict:** Build passes, preview shows real rows. Run npm run start from zso.salesorderapp/.\r\n\r\n---\r\n\r\n## Build Plan\r\n\r\n| Step | Artifact | Notes |\r\n|------|----------|-------|\r\n| 1 | zso.salesorderapp/ scaffold | fiori_scaffold, basic template |\r\n| 2 | view/SalesOrderList.view.xml | list page, sap.m.Table |\r\n| 3 | controller/SalesOrderList.controller.js | list controller |\r\n| 4 | manifest.json — Route/target SalesOrderList | wired |\r\n| 5 | view/SalesOrderDetail.view.xml | detail page, ObjectPageLayout |\r\n| 6 | controller/SalesOrderDetail.controller.js | detail controller |\r\n| 7 | manifest.json — Route/target SalesOrderDetail | wired |\r\n| 8 | ext/fragment/ApproveConfirmDialog.fragment.xml | confirm dialog fragment |\r\n| 9 | controller/ext/ApproveConfirmDialog.js | dialog mixin |\r\n\r\nConfirmed — proceeding to scaffold.\r\n\r\n...\r\n\r\n## Build Validation\r\n\r\nnpm install → OK\r\nnpm run build → OK\r\n\r\n## Preview\r\n\r\nnpm run start → http://localhost:8080/index.html\r\nSalesOrders entity: 12 real rows rendered in list page.\r\n```\r\n",
107
114
  "sha256": "39034655307b20e41af37e39db52b666ab76854b94e8b81d876eef13ff26cc25",
108
115
  "signature": "",
109
- "signedAt": "2026-09-17T20:19:04.876Z"
116
+ "signedAt": "2026-09-28T14:37:04.587Z"
110
117
  },
111
118
  "abap-generate": {
112
119
  "name": "abap-generate",
113
120
  "body": "---\r\nname: abap-generate\r\ndescription: >\r\n Generate NEW ABAP code/objects after asking the right questions first.\r\n Follows Forge Rule 1 — never generates blindly. Confirms scope\r\n before writing a single line, then produces Clean ABAP compliant\r\n output. When CSPeach is connected to a SAP system, can create the\r\n object directly via ADT after explicit user approval. Use for NEW\r\n objects; to modify or add a feature to EXISTING custom code in place,\r\n use abap-refactor instead.\r\nphase: BUILD\r\nrequires_mcp: optional\r\nforge_rules: [1, 2, 6, 7, 10]\r\nversion: \"1.2\"\r\nmin_cli_version: \"0.3.1\"\r\n---\r\n\r\n# abap-generate\r\n\r\n## Purpose\r\n\r\nGenerate production-quality ABAP code for a new program, class, or include. Gather required scope before writing a line. Produce Clean ABAP output and (with approval) create the objects in SAP.\r\n\r\nDo NOT use for RAP stacks (use `/abap-rap`) or refactoring existing code (use `/abap-refactor`).\r\n\r\n## When to Use\r\n\r\n- Use for ONE new class/program/include from a clear scope — and for daily edits to YOUR Z code (\"add a validation to ZCL_X\", \"change/fix ZFOO_REPORT\").\r\n- NOT for a full RAP stack — use `/abap-rap`; NOT for DDIC objects only — use `/abap-data-model`; NOT for hooking SAP standard — use `/abap-enhance`.\r\n\r\n## Session context\r\n\r\nThe user's opening prompt is parsed deterministically before you see it. Any fact they stated — package, transport, object names, environment hint, create-intent — appears in a `<session_context>` XML block at the top of their message. **Read that block first. Do NOT ask the user about any field present in it.** If a field is absent, it's genuinely unstated and you may ask.\r\n\r\n- If the source requirement is a `.pdf`/`.docx`, read it with `read_document(path)` first.\r\n\r\n## Required behavior\r\n\r\n### 0. Detect RAP-shaped designs and offer hand-off to `/abap-rap`\r\n\r\nIf the user's prompt begins with `Promoted from design (v...) — \"...\"` (the user invoked `/abap-generate --from @<design-file>`), read the object list that follows. Each line has the form:\r\n\r\n> `obj-NNN: ZNAME (Object Type) → keep`\r\n\r\nIf ANY object's parenthesized type contains one of:\r\n\r\n- **Behavior Definition** / **BDEF**\r\n- **Behavior Implementation** / **Behavior Pool**\r\n- **Service Binding** / **SRVB**\r\n- **Service Definition** / **SRVD**\r\n\r\nthe design describes a RAP stack. `/abap-generate` builds classic ABAP (reports, function modules, utility classes); RAP stacks belong in `/abap-rap`. Use `ask_question` ONCE before any tool probe:\r\n\r\n> \"This design contains RAP objects (Behavior Definition / Service Binding / Behavior Pool). `/abap-generate` builds classic ABAP — `/abap-rap` is the right skill for a RAP stack. Switch, or continue with `/abap-generate` anyway?\"\r\n>\r\n> Options:\r\n> - **Switch to /abap-rap** (recommended — the design is RAP-shaped)\r\n> - **Continue with /abap-generate** (build only the non-RAP objects from this design)\r\n> - Cancel — exit the skill\r\n\r\nIf \"Switch\" — call `dispatch_skill` with the `/abap-rap` command, then end the turn.\r\n\r\n**Filename rule (critical):** the user's prompt arrived with a `Source file: @<filename>` line at the very top — that line carries the *exact* @<filename> token to use. Read it from the prompt verbatim. Do NOT abbreviate, drop ID segments, or guess. The full id-bearing form (e.g. `@order-approval-system-design-d515-v1.cspeach.json`) is what the workspace picker can resolve; a shortened form will fail with \"no file matching\".\r\n\r\n```\r\ndispatch_skill({ command: \"/abap-rap --from <copy the @<filename> token verbatim from the Source file: line>\" })\r\n```\r\n\r\nThe harness consumes this slot and auto-runs the command as the next user submission. The user's consent was the picker pick — no Enter press required, no \"Type X at the next prompt\" hint. End the turn after the dispatch_skill tool returns; do NOT also print a continuation hint.\r\n\r\nIf \"Continue\" — proceed to step 1, but in the generation plan EXCLUDE RAP objects from \"Objects to create\" and add a \"RAP objects deferred to /abap-rap\" line in \"Scope Boundary (OUT)\".\r\n\r\nIf \"Cancel\" — end the turn cleanly.\r\n\r\nIf the prompt has no `Promoted from design` prefix, OR the design has no RAP markers, skip this step entirely and proceed to step 1.\r\n\r\n**Why this matters:** running `/abap-generate` on a RAP design produces broken output — the skill doesn't know how to scaffold a managed business object, behavior pool, projection-with-MDE, or service binding. Detecting the shape mismatch up front and offering a clean hand-off (auto-routed via dispatch_skill, no retype) prevents wasted turns and a forced re-design.\r\n\r\n### 1. Investigate the system (tool calls, no user prompts)\r\n\r\nBefore any question, use read tools to learn what you can:\r\n\r\n- **Release probe.** Call `sap_search_object` for `CL_BEHAVIOR_ROOT` (exists → S/4HANA RAP-capable) or `CL_SOAP_WSDL` (ECC/legacy). Skip if `<environment_hint>` is already populated.\r\n- **Package check.** If `<package>` is populated, call `sap_search_object` on it to confirm it exists. If unpopulated, probe the customer namespace (e.g. `ZSD*`, `ZFI*`) for naming-convention hints.\r\n- **Object context.** If the user referenced an existing table / class / CDS view, call `sap_object_structure` on it so your plan uses the actual field names, not guesses.\r\n- **Inactive-version check (Bug 12).** Before consuming any class / interface source as the basis for dependent code generation, call `sap_inactive_objects` once. If the source-providing object appears in the list, the active source you would read does NOT include the user's pending edits — warn the user before continuing, and prefer either (a) asking them to activate or discard the inactive version, or (b) reading `sap_get_source` with `version: \"inactive\"` so your plan reflects the real current state. The `sap_get_source` response also carries a `version` field and a `split_state_warning` when the probe spots a pending inactive copy — read both before generating dependent code.\r\n\r\nState what you found in ONE short paragraph. Do not narrate every probe.\r\n\r\n### 2. Ask only about genuinely unresolved decisions\r\n\r\nUse the `ask_question` tool for anything the user didn't state AND the system can't tell you.\r\n\r\n**MUST-ASK items — never invent these.** If any of the following are absent from `<session_context>` AND not derivable from tool probes, you MUST ask before producing the plan. Inventing names is a correctness failure: the developer has a naming standard, you do not.\r\n\r\n| Field | Required when missing |\r\n|-------|----------------------|\r\n| `<object_names>` | **Always ask.** Never fabricate a program/class/interface/table name. Single `ask_question` with `kind: \"text\"`, example \"What should the program and utility class be named? (e.g. ZR_OPEN_SO, ZCL_OPEN_SO_UTIL)\". |\r\n| `<package>` | Ask if absent. |\r\n| `<transport>` | Ask only when `<create_intent>` is true or the user asked for creation. |\r\n\r\n**Legitimate design questions** (ask only the ones that actually apply):\r\n\r\n- Business-logic semantics (\"open order\" = `FKSTK <> 'C'` vs remaining-qty > 0)\r\n- Output style (ALV vs WRITE)\r\n- Field selection when multiple obvious options exist\r\n- Error-handling strategy (exception class vs `sy-subrc` flag)\r\n\r\nBatch related questions into ONE `ask_question` call via the `questions` array — up to 4 per call. The user answers them in a single form; one form beats a chain of sequential round-trips (names + package + transport is a natural batch, so is output style + error handling). Keep each question's `header` ≤12 chars. Cap at 6 questions total — if you're on question 7, go straight to the plan.\r\n\r\n**Never ask** about items already in `<session_context>`. Never ask about things tool probes already answered. Never skip a MUST-ASK because you can \"reasonably guess\" a name — you can't.\r\n\r\n### 3. Show the generation plan\r\n\r\nOnce scope is clear, produce a plan block:\r\n\r\n- Objects to create (name, type, role)\r\n- Methods / sections per object with signatures\r\n- Dependencies (tables, released APIs, exception classes)\r\n- Clean ABAP features applied\r\n- Explicit OUT-OF-SCOPE section\r\n\r\nEnd with: \"Does this plan look correct? Reply yes to proceed.\" Stop and wait.\r\n\r\n### 4. Create on approval (only if `<create_intent>` OR user says yes)\r\n\r\nPer Forge Rule 8, the `request_approval` tool gates the writes. On approval:\r\n\r\n1. Lock → set source → syntax check → activate per object, one at a time (Rule 7a: use `sap_update_method` for method-only edits).\r\n2. Report syntax/activation results; don't release the transport.\r\n\r\nIf syntax check fails, stop and show the error. Never silent-retry.\r\n\r\n## TL;DR (mandatory first section of every response)\r\n\r\nEvery response opens with this block, before any prose or tool calls:\r\n\r\n```markdown\r\n## [abap-generate] — [Target: object name OR \"scoping\"]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of this turn's decision or action>\r\n\r\n**Next:** <what you're about to do / what you want from the user>\r\n```\r\n\r\nShort. No padding. If this is turn 2+ and the TL;DR repeats turn 1's, drop the headline line.\r\n\r\n## Output structure (when producing the generation plan)\r\n\r\n```markdown\r\n## Generation Plan\r\n\r\n**Package:** {package} | **Transport:** {transport} | **Environment:** {env}\r\n\r\n### Objects\r\n| Object | Type | Role |\r\n|--------|------|------|\r\n\r\n### <object_1>\r\n| Method | Signature | Purpose |\r\n\r\n### Dependencies\r\n- ...\r\n\r\n### Scope Boundary (OUT)\r\n- ...\r\n\r\n### Clean ABAP Applied\r\n- ...\r\n\r\nProceed? (yes/no)\r\n```\r\n\r\nGenerated code follows the plan as a fenced `abap` block after the user approves.\r\n\r\n## Examples\r\n\r\n### Example A — user provides full scope\r\n\r\nUser prompt (with `<session_context>` the harness prepends):\r\n```\r\n<session_context>\r\n <package>ZTEST_LAS</package>\r\n <transport>S4HK903342</transport>\r\n <create_intent>true</create_intent>\r\n</session_context>\r\nWe need a program with a utility class that prints open sales orders (not fully invoiced). Name them ZR_OPEN_SO and ZCL_OPEN_SO_UTIL.\r\n```\r\n\r\nCorrect skill flow:\r\n1. Probe system (CL_BEHAVIOR_ROOT, VBAK, ZTEST_LAS) — 3 tool calls.\r\n2. One prose paragraph: \"S/4HANA confirmed, package ZTEST_LAS exists, VBAK has FKSTK/GBSTK.\"\r\n3. **One** `ask_question` call — the ONLY unresolved decision is the \"open\" definition (FKSTK<>'C' vs item-level). Everything else is in `<session_context>` or probed.\r\n4. User picks → produce plan.\r\n5. User says yes → `request_approval` → create.\r\n\r\n### Example B — user provides minimal scope\r\n\r\nUser prompt:\r\n```\r\nI need a small utility class for converting currency.\r\n```\r\n(no `<session_context>` block — nothing extracted)\r\n\r\nCorrect skill flow:\r\n1. Probe release (detect S/4 or ECC).\r\n2. **One batched** `ask_question` call — `questions`: package, class name, \"create in SAP or just show code?\" (no transport implies ambiguous intent). One form, not three round-trips.\r\n3. `ask_question` for any remaining design choice the answers surface.\r\n4. Plan → approval → create.\r\n\r\n### Example C — contradiction between user and system\r\n\r\nUser says \"use package ZFOO\" but probe finds no ZFOO. Don't invent one — call `ask_question` with context \"ZFOO doesn't exist on this system; create it or use a different one?\" The user decides.\r\n\r\n## Guardrails\r\n\r\n- Follow Forge Rules 1, 2, 6, 7, 10 (referenced in `.claude/rules/safety.md`).\r\n- Method-only edits → `sap_update_method` per method (Rule 7a). Never `sap_set_source` just to tweak one method body.\r\n- No `SELECT *` — always list fields.\r\n- No obsolete statements (MOVE, CONCATENATE, COMPUTE) — use `:=`, string templates, arithmetic.\r\n- Every public method gets ABAP Doc.\r\n- Clean ABAP: inline DATA(...), early returns, no FORMs, typed field-symbols.\r\n- If on ABAP Cloud, flag any statement that may not be released (check `sap_api_state`).\r\n\r\n## Example prompt\r\n\r\n```\r\n/abap-generate\r\nI need a helper class to convert amounts between currencies via CONVERT_TO_LOCAL_CURRENCY.\r\nPackage ZFIN_UTILS, transport S4HK900123. Name it ZCL_FIN_CURRENCY_CONV.\r\n```\r\n\r\nExpected behaviour: `<session_context>` gets `package`, `transport`, `objectNames=['ZCL_FIN_CURRENCY_CONV']`, `create_intent=true` injected. Skill probes release, asks only about rate-type handling and exception strategy, produces plan, creates after approval.\r\n",
114
121
  "sha256": "3b7942fedb0681681506f3ff8fa41d56a9d5f9d3bf0bfcb92edd9fc14b25b2a2",
115
122
  "signature": "",
116
- "signedAt": "2026-09-17T20:19:04.979Z"
123
+ "signedAt": "2026-09-28T14:37:04.624Z"
117
124
  },
118
125
  "abap-handover": {
119
126
  "name": "abap-handover",
120
- "body": "---\nname: abap-handover\ndescription: >\n Generate documentation for an ABAP object or a whole development package. `object` scope produces a single-object technical document (purpose, logic, interfaces, dependencies); `package` scope produces a full handover deliverable (object catalog, architecture, config, authorizations, test plan, ops notes). Read-only; derives from code, never invents behavior.\nphase: SUPPORT\nrequires_mcp: optional\nforge_rules: [6]\nversion: \"1.0\"\n---\n\n# abap-handover\n\n## Purpose\n\nProduce professional technical documentation for an ABAP development item —\nwhether a single class, a complete RAP stack, a custom workflow, or an entire\ndevelopment package. Documentation is derived from code analysis, not invented.\nWhen information is not present in the code, the skill says so rather than\nfilling in gaps with assumptions.\n\n## When to Use\n\n- Documenting a single ABAP object — purpose, logic flow, interfaces, dependencies, data access (use `object` scope)\n- Handing a development over to another team (use `package` scope)\n- Creating documentation for a go-live package\n- Documenting a custom enhancement for an audit or compliance review\n- Creating onboarding material for a new developer joining the project\n- As part of a pre-release checklist (Forge Rule 5)\n\nThis skill covers BOTH a single-object reference doc (`--scope object`) and a whole-package handover dossier (`--scope package`). See the Scope Mode section below.\n\nWhen NOT to use: teaching code to a human in plain language — `/abap-explain`; a release go/no-go package — `/abap-preflight`.\n\n## Scope Mode\n\nThis skill runs in one of two scopes. **Infer the default from the target:** a single ABAP object → `object`; a development package or multi-object stack → `package`. The user can override with `--scope object` or `--scope package`.\n\n| Scope | Target | Deliverable |\n|-------|--------|-------------|\n| `object` | ONE ABAP object (PROG, CLAS, FUGR, DDLS, …) | A single-object technical document — purpose, logic flow, interfaces, dependencies, data access |\n| `package` | A development package or full stack | The full handover deliverable — object catalog, architecture, config, authorizations, test plan, ops notes |\n\n### `object` scope — single-object technical document\n\nProduce a developer reference for ONE ABAP object: enough that a new team member can understand it in minutes without reading the source. Use for onboarding, pre-change assessment, audit readiness, and contractor handover of an individual object.\n\n**Required behavior (object scope):**\n\n#### O1 — Read the object\n\n```\nsap_get_source (objectType, objectName)\nsap_object_structure (objectType, objectName) — if available\n```\n\nFor classes: also read the class definition to understand the public interface.\nFor function groups: identify all function modules in the group.\n\n#### O2 — Analyze the code\n\nExtract from source:\n- **Purpose** — what does this program/class/FM do? (from comments, naming, logic)\n- **Inputs/Outputs** — parameters, selection screen, importing/exporting\n- **Business logic** — key decisions, calculations, validations\n- **Data access** — which tables are read/written, which CDS views used\n- **External calls** — function modules called, classes used, BAPIs, RFCs\n- **Error handling** — exceptions raised, messages used\n- **Authorization** — authority checks performed\n\n#### O3 — Trace dependencies\n\n```\nsap_usage_references (objectType, objectName)\n```\n\nBuild two lists:\n- **Calls out** — what does this object depend on?\n- **Called by** — what depends on this object?\n\n#### O4 — Produce the document\n\nFormat the output as a structured single-object document:\n\n```\n## {OBJECT_NAME} ({OBJECT_TYPE})\n\n### Overview\nPackage: {package} | Author: {author} | Last changed: {date}\n{1-3 sentence summary of what this object does}\n\n### Purpose\n{Detailed description of the business purpose. What problem does it solve?\nWhat process does it support?}\n\n### Interface\n{For programs: selection screen parameters}\n{For classes: public methods with signatures}\n{For FMs: importing/exporting/changing/tables parameters}\n\n### Logic Flow\n{Step-by-step description of what the code does, in business terms.\nNot a line-by-line translation — a developer-readable summary.}\n\n1. Read input data from {tables}\n2. Validate {conditions}\n3. Process {business logic}\n4. Write results to {output}\n\n### Data Access\n| Table/View | Operation | Purpose |\n|------------|-----------|---------|\n| {table} | READ | {why} |\n| {table} | WRITE | {why} |\n\n### Dependencies\nCalled by: {list of objects that reference this one}\nCalls: {list of objects this one references}\n\n### Error Handling\n{Exceptions, messages, return codes}\n\n### Notes\n{Anything unusual, workarounds, known issues, TODOs in comments}\n```\n\n**Object-scope guardrails:**\n- If an object is very large (>1000 lines), summarize by method/section rather than documenting every line.\n- For classes with many methods, document the public interface first. Private methods only if specifically requested.\n- If where-used returns many results (>50), show the top 10 most relevant and note the total count.\n- Do not invent business context. If the code doesn't make the purpose clear, say \"purpose not clear from code — confirm with business team.\"\n\n#### Worked example — `object` scope\n\nPrompt:\n\n```\n/abap-handover --scope object\n\nDocument the class ZCL_ORDER_APPROVAL for the support team. Audience: developer.\n```\n\nOutput:\n\n```\n## ZCL_ORDER_APPROVAL (CLAS)\n\n### Overview\nPackage: ZORD_APPROVAL | Author: JSMITH | Last changed: 2026-03-29\nCentral approval decision engine for sales orders above the configured\nthreshold. Called from the SAP workflow approval step.\n\n### Purpose\nValidates a sales order's state and executes the approve or reject action,\nthen triggers an email notification. Solves the requirement that sales orders\nabove EUR 10,000 must be approved by a manager before delivery creation.\n\n### Interface\nPublic methods:\n| Method | Signature | Description |\n|-------------|--------------------------------------------------------|--------------------------------------|\n| CONSTRUCTOR | io_email_sender TYPE REF TO zif_email_sender | Inject email dependency |\n| APPROVE | iv_order_id TYPE vbeln; RAISING zcx_approval_error | Approve the order; send notification |\n| REJECT | iv_order_id TYPE vbeln; iv_reason TYPE string; | Reject the order; send notification |\n| | RAISING zcx_approval_error | |\n\n### Logic Flow\n1. Read order header from I_SalesOrder (released CDS VDM)\n2. Validate the current status allows the requested transition\n3. Apply the approve/reject state change\n4. Delegate the notification to ZIF_EMAIL_SENDER (email failure is logged, not blocking)\n\n### Data Access\n| Table/View | Operation | Purpose |\n|--------------|-----------|----------------------------------|\n| I_SalesOrder | READ | Read order header for validation |\n| ZORD_CONFIG | READ | Read the approval threshold |\n\n### Dependencies\nCalled by: ZORDER_WORKFLOW (workflow task exit)\nCalls: ZIF_EMAIL_SENDER (injected), I_SalesOrder, ZCX_APPROVAL_ERROR\n\n### Error Handling\n- ZCX_APPROVAL_ERROR raised on invalid state transitions and empty reject reason\n- CX_BCS_EXCEPTION from the email sender is caught and logged to SLG1 (object ZORD); does NOT block approval\n\n### Notes\n- No partial approval — full order only.\n- No multi-level approval chain in this version.\n- Email template is hardcoded in ZCL_BCS_EMAIL_SENDER.\n```\n\n### `package` scope — full handover deliverable\n\nThe remainder of this skill (steps 1–7 below and the full Output Structure) is the `package` scope: the complete handover dossier for a development package or full stack. It is unchanged.\n\n## Inputs Expected\n\nRequired:\n- The ABAP source code (paste) or object/package name (if MCP connected)\n- Scope: single object / full stack / package\n\nOptional (will improve output):\n- Business context and purpose\n- Target audience: developer / functional consultant / Basis administrator\n- Whether configuration documentation is needed\n- Known authorization objects to document\n\n## Required Behavior\n\n### Step 1 — Inventory the Objects\n\nCollect all objects to be documented. If MCP is connected, use\n`sap_object_structure` or `sap_search_object` to retrieve the full list.\nOtherwise work from provided source code.\n\nBuild an object catalog: each object's name, type, purpose (derived from\nABAP Doc or from the code's apparent intent).\n\n### Step 2 — Architecture Overview\n\nDescribe how the objects relate to each other:\n- Which classes call which\n- Which CDS views depend on which tables\n- Which RAP layers exist (table → CDS → BDEF → service)\n- Which BAdI implementations are registered\n\nDraw a simple text-based dependency diagram if the structure is non-trivial.\n\n### Step 3 — Object Documentation\n\nFor each object, document:\n- Purpose\n- Key methods / fields / parameters\n- Dependencies\n- Constraints and limitations (where visible from the code)\n\n### Step 4 — Configuration Requirements\n\nList any Customizing or configuration steps needed:\n- IMG paths (if identifiable)\n- Feature flags or program parameters\n- Communication arrangements (BTP)\n- SMTP / output type configuration (email, forms)\n- RFC destinations\n\n### Step 5 — Authorization Model\n\nDocument:\n- Authorization objects checked in the code\n- Which roles typically need these\n- Any implicit authorization (via standard FM/BAPI calls)\n\n### Step 6 — Test Plan\n\nProduce a test plan outline:\n- Functional test scenarios\n- Edge cases to test\n- Negative scenarios\n- Any performance test requirements\n\n### Step 7 — Operational Notes\n\nDocument:\n- Background job requirements\n- Monitoring requirements (where to check errors)\n- Known limitations\n- Upgrade considerations\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\n```\n# Technical Documentation — {title}\n\n**Package:** {package}\n**Prepared:** {date}\n**Audience:** {developer | functional | basis | all}\n**Status:** Draft / Final\n\n---\n\n## 1. Object Catalog\n\n| Object | Type | Package | Purpose |\n|--------|------|---------|---------|\n| {name} | {type} | {package} | {purpose} |\n\n---\n\n## 2. Architecture Overview\n\n### Layered Structure\n```\n{text_diagram or layer description}\n```\n\n### Key Relationships\n- {object_a} depends on {object_b}: {reason}\n- {object_c} is called by {object_d} via {mechanism}\n\n---\n\n## 3. Object Details\n\n### {Object_Name} ({type})\n\n**Purpose:** {purpose}\n\n**Public Interface:**\n| Method / Field | Signature / Type | Description |\n|----------------|-----------------|-------------|\n| {name} | {sig} | {desc} |\n\n**Dependencies:**\n- {dependency_1}\n- {dependency_2}\n\n**Constraints:** {constraints}\n\n---\n\n## 4. Configuration Requirements\n\n| Step | Where | What to Configure | Default / Expected Value |\n|------|-------|-------------------|--------------------------|\n| {step} | {IMG path or tx} | {config_item} | {value} |\n\n---\n\n## 5. Authorization Model\n\n| Auth Object | Fields | Typical Role | Checked In |\n|-------------|--------|-------------|------------|\n| {obj} | {fields} | {role} | {location} |\n\n---\n\n## 6. Test Plan\n\n### Functional Tests\n| Test Case | Precondition | Steps | Expected Result |\n|-----------|--------------|-------|-----------------|\n| {tc} | {precond} | {steps} | {expected} |\n\n### Edge Cases\n- {edge_case_1}\n- {edge_case_2}\n\n### Negative Tests\n- {negative_1}\n\n---\n\n## 7. Operational Notes\n\n**Background Jobs:** {job_name, schedule, monitoring}\n**Error Logs:** {SLG1 object, SM21, other}\n**Known Limitations:** {limitations}\n**Upgrade Considerations:** {notes}\n```\n\n## Guardrails\n\n- NEVER document behavior that cannot be derived from the provided code or context\n- If a method's purpose is unclear from code, mark it as \"Purpose unknown — review\n required\" rather than inventing a description\n- Do not document internal private methods unless the audience is developers\n- If configuration steps are not visible in the code, note that they must be\n confirmed with the functional team\n- Authorization objects: only document what is AUTHORITY-CHECK'd in the code;\n do not speculate about what roles are needed\n\n## Example Prompt\n\n```\n/abap-handover\n\nDocument the order approval package ZORD_APPROVAL for handover to the support\nteam. Audience: developer + functional consultant. Include test plan.\n\nObjects:\n- ZCL_ORDER_APPROVAL (class) — main approval engine\n- ZCL_ORDER_APPROVAL_TEST (test class)\n- ZIF_EMAIL_SENDER (interface) — email abstraction\n- ZCL_BCS_EMAIL_SENDER (class) — CL_BCS implementation\n- ZORDER_WORKFLOW (workflow) — approval workflow\n\nBusiness context: Sales orders above EUR 10,000 require manager approval via\nSAP workflow before delivery can be created. The approval class checks the\norder status and sends email notifications via CL_BCS.\n```\n\n## Example Output Outline\n\n```\n# Technical Documentation — Order Approval Package\n\n**Package:** ZORD_APPROVAL\n**Prepared:** 2026-03-29\n**Audience:** Developer + Functional Consultant\n**Status:** Draft\n\n---\n\n## 1. Object Catalog\n\n| Object | Type | Package | Purpose |\n|---------------------------|-----------|---------------|----------------------------------------------------|\n| ZCL_ORDER_APPROVAL | Class | ZORD_APPROVAL | Main approval engine — approve/reject orders |\n| ZIF_EMAIL_SENDER | Interface | ZORD_APPROVAL | Email sender abstraction for testability |\n| ZCL_BCS_EMAIL_SENDER | Class | ZORD_APPROVAL | CL_BCS implementation of ZIF_EMAIL_SENDER |\n| ZCL_ORDER_APPROVAL_TEST | Test Class| ZORD_APPROVAL | Unit tests for ZCL_ORDER_APPROVAL |\n| ZORDER_WORKFLOW | Workflow | ZORD_WORKFLOW | SAP Workflow routing sales orders to manager approval |\n| ZCX_APPROVAL_ERROR | Exception | ZORD_APPROVAL | Typed exception for approval failures |\n\n---\n\n## 2. Architecture Overview\n\n### Layered Structure\n```\n[ZORDER_WORKFLOW] — workflow task exit\n |\n v\n[ZCL_ORDER_APPROVAL]\n |\n ├── reads VBAK / VBAP (via released VDM: I_SalesOrder)\n |\n └── delegates email to [ZIF_EMAIL_SENDER]\n |\n └── implemented by [ZCL_BCS_EMAIL_SENDER]\n |\n └── uses CL_BCS (SAP standard)\n```\n\n### Key Relationships\n- ZORDER_WORKFLOW calls ZCL_ORDER_APPROVAL via a workflow task exit (TS type)\n- ZCL_ORDER_APPROVAL receives ZIF_EMAIL_SENDER via constructor injection\n- ZCL_BCS_EMAIL_SENDER is the production implementation; test doubles replace it in unit tests\n\n---\n\n## 3. Object Details\n\n### ZCL_ORDER_APPROVAL (Class)\n\n**Purpose:** Central approval decision engine. Called from the SAP workflow\nwhen a sales order reaches the approval step. Validates the order state,\nexecutes the approve or reject action, and triggers an email notification.\n\n**Public Interface:**\n| Method | Signature | Description |\n|------------------|--------------------------------------------------------|------------------------------------|\n| CONSTRUCTOR | io_email_sender TYPE REF TO zif_email_sender | Inject email dependency |\n| APPROVE | iv_order_id TYPE vbeln; RAISING zcx_approval_error | Approve the order; send notification |\n| REJECT | iv_order_id TYPE vbeln; iv_reason TYPE string; | Reject the order; send notification |\n| | RAISING zcx_approval_error | |\n\n**Dependencies:**\n- ZIF_EMAIL_SENDER (injected) — sends email notifications\n- I_SalesOrder (released CDS VDM) — reads order header data\n- ZCX_APPROVAL_ERROR — raised on invalid state transitions\n\n**Constraints:**\n- Does not support partial approval (full order only)\n- No support for multi-level approval chains in this version\n- Email failure (CX_BCS_EXCEPTION) is caught and logged; does NOT block approval\n\n---\n\n## 4. Configuration Requirements\n\n| Step | Where | What to Configure | Expected Value |\n|------|---------------------------|--------------------------------------|------------------------------|\n| 1 | SCOT (Email config) | SMTP server and route | Active SMTP route to mail server |\n| 2 | SCOT | Default sender address | noreply@company.com |\n| 3 | SWI5 / SWDD | ZORDER_WORKFLOW activation | Workflow must be active in target system |\n| 4 | IMG: SD Order Management | Approval threshold (if configurable) | EUR 10,000 (stored in ZORD_CONFIG table) |\n\n---\n\n## 5. Authorization Model\n\n| Auth Object | Fields | Typical Role | Checked In |\n|---------------|---------------------|-----------------------|-----------------------|\n| V_VBAK_AAT | AUART (order type) | SD Sales Order Approver | ZCL_ORDER_APPROVAL=>APPROVE |\n| V_VBAK_VKO | VKORG (sales org) | SD Sales Order Approver | ZCL_ORDER_APPROVAL=>APPROVE |\n\nNote: The email notification does not require separate authorization. CL_BCS\nuses the technical system user's mail configuration.\n\n---\n\n## 6. Test Plan\n\n### Functional Tests\n| Test Case | Precondition | Steps | Expected Result |\n|-----------------------------------|---------------------------------|---------------------------------------|----------------------------------|\n| Approve valid order | Sales order >10K in status 'Open'| Call APPROVE with valid order ID | Status → 'Approved'; email sent |\n| Reject with reason | Sales order in status 'Open' | Call REJECT with reason text | Status → 'Rejected'; email sent |\n| Approve already-approved order | Order in status 'Approved' | Call APPROVE again | ZCX_APPROVAL_ERROR raised |\n| Reject with empty reason | Any open order | Call REJECT with iv_reason = '' | ZCX_APPROVAL_ERROR raised |\n| Email server down | SCOT SMTP inactive | Call APPROVE | Approval succeeds; SLG1 log entry |\n\n### Edge Cases\n- Order with no requester email address — should log warning, not dump\n- Order ID not found in I_SalesOrder — ZCX_APPROVAL_ERROR with meaningful text\n- Concurrent approval by two managers — second approval should raise error gracefully\n\n### Negative Tests\n- Call APPROVE without V_VBAK_AAT authorization → NO_AUTHORITY exception expected\n\n---\n\n## 7. Operational Notes\n\n**Background Jobs:** None — approval is triggered synchronously from workflow.\n\n**Error Logs:** Check SLG1 with log object ZORD for email notification errors.\nEmail failures do not block the approval outcome but are logged for monitoring.\n\n**Known Limitations:**\n- Only single-level approval supported. Multi-level approval requires workflow redesign.\n- Email template is hardcoded in ZCL_BCS_EMAIL_SENDER — not configurable via Customizing.\n\n**Upgrade Considerations:**\n- Uses I_SalesOrder (C1 Released) — safe for S/4HANA upgrades.\n- CL_BCS is C1 Released — safe for BTP migration.\n- ZORDER_WORKFLOW uses classic SAP Workflow (BC-BMT-WFM) — consider migration\n to SAP Build Process Automation for BTP environments.\n```\n",
121
- "sha256": "703fb9efb56fc39549ac5d3947154b20af00503b20a8a3ccae1fe259214b82da",
127
+ "body": "---\r\nname: abap-handover\r\ndescription: >\r\n Generate documentation for an ABAP object or a whole development package. `object` scope produces a single-object technical document (purpose, logic, interfaces, dependencies); `package` scope produces a full handover deliverable (object catalog, architecture, config, authorizations, test plan, ops notes). Read-only; derives from code, never invents behavior.\r\nphase: SUPPORT\r\nrequires_mcp: optional\r\nforge_rules: [6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-handover\r\n\r\n## Purpose\r\n\r\nProduce professional technical documentation for an ABAP development item —\r\nwhether a single class, a complete RAP stack, a custom workflow, or an entire\r\ndevelopment package. Documentation is derived from code analysis, not invented.\r\nWhen information is not present in the code, the skill says so rather than\r\nfilling in gaps with assumptions.\r\n\r\n## When to Use\r\n\r\n- Documenting a single ABAP object — purpose, logic flow, interfaces, dependencies, data access (use `object` scope)\r\n- Handing a development over to another team (use `package` scope)\r\n- Creating documentation for a go-live package\r\n- Documenting a custom enhancement for an audit or compliance review\r\n- Creating onboarding material for a new developer joining the project\r\n- As part of a pre-release checklist (Forge Rule 5)\r\n\r\nThis skill covers BOTH a single-object reference doc (`--scope object`) and a whole-package handover dossier (`--scope package`). See the Scope Mode section below.\r\n\r\nWhen NOT to use: teaching code to a human in plain language — `/abap-explain`; a release go/no-go package — `/abap-preflight`.\r\n\r\n## Scope Mode\r\n\r\nThis skill runs in one of two scopes. **Infer the default from the target:** a single ABAP object → `object`; a development package or multi-object stack → `package`. The user can override with `--scope object` or `--scope package`.\r\n\r\n| Scope | Target | Deliverable |\r\n|-------|--------|-------------|\r\n| `object` | ONE ABAP object (PROG, CLAS, FUGR, DDLS, …) | A single-object technical document — purpose, logic flow, interfaces, dependencies, data access |\r\n| `package` | A development package or full stack | The full handover deliverable — object catalog, architecture, config, authorizations, test plan, ops notes |\r\n\r\n### `object` scope — single-object technical document\r\n\r\nProduce a developer reference for ONE ABAP object: enough that a new team member can understand it in minutes without reading the source. Use for onboarding, pre-change assessment, audit readiness, and contractor handover of an individual object.\r\n\r\n**Required behavior (object scope):**\r\n\r\n#### O1 — Read the object\r\n\r\n```\r\nsap_get_source (objectType, objectName)\r\nsap_object_structure (objectType, objectName) — if available\r\n```\r\n\r\nFor classes: also read the class definition to understand the public interface.\r\nFor function groups: identify all function modules in the group.\r\n\r\n#### O2 — Analyze the code\r\n\r\nExtract from source:\r\n- **Purpose** — what does this program/class/FM do? (from comments, naming, logic)\r\n- **Inputs/Outputs** — parameters, selection screen, importing/exporting\r\n- **Business logic** — key decisions, calculations, validations\r\n- **Data access** — which tables are read/written, which CDS views used\r\n- **External calls** — function modules called, classes used, BAPIs, RFCs\r\n- **Error handling** — exceptions raised, messages used\r\n- **Authorization** — authority checks performed\r\n\r\n#### O3 — Trace dependencies\r\n\r\n```\r\nsap_usage_references (objectType, objectName)\r\n```\r\n\r\nBuild two lists:\r\n- **Calls out** — what does this object depend on?\r\n- **Called by** — what depends on this object?\r\n\r\n#### O4 — Produce the document\r\n\r\nFormat the output as a structured single-object document:\r\n\r\n```\r\n## {OBJECT_NAME} ({OBJECT_TYPE})\r\n\r\n### Overview\r\nPackage: {package} | Author: {author} | Last changed: {date}\r\n{1-3 sentence summary of what this object does}\r\n\r\n### Purpose\r\n{Detailed description of the business purpose. What problem does it solve?\r\nWhat process does it support?}\r\n\r\n### Interface\r\n{For programs: selection screen parameters}\r\n{For classes: public methods with signatures}\r\n{For FMs: importing/exporting/changing/tables parameters}\r\n\r\n### Logic Flow\r\n{Step-by-step description of what the code does, in business terms.\r\nNot a line-by-line translation — a developer-readable summary.}\r\n\r\n1. Read input data from {tables}\r\n2. Validate {conditions}\r\n3. Process {business logic}\r\n4. Write results to {output}\r\n\r\n### Data Access\r\n| Table/View | Operation | Purpose |\r\n|------------|-----------|---------|\r\n| {table} | READ | {why} |\r\n| {table} | WRITE | {why} |\r\n\r\n### Dependencies\r\nCalled by: {list of objects that reference this one}\r\nCalls: {list of objects this one references}\r\n\r\n### Error Handling\r\n{Exceptions, messages, return codes}\r\n\r\n### Notes\r\n{Anything unusual, workarounds, known issues, TODOs in comments}\r\n```\r\n\r\n**Object-scope guardrails:**\r\n- If an object is very large (>1000 lines), summarize by method/section rather than documenting every line.\r\n- For classes with many methods, document the public interface first. Private methods only if specifically requested.\r\n- If where-used returns many results (>50), show the top 10 most relevant and note the total count.\r\n- Do not invent business context. If the code doesn't make the purpose clear, say \"purpose not clear from code — confirm with business team.\"\r\n\r\n#### Worked example — `object` scope\r\n\r\nPrompt:\r\n\r\n```\r\n/abap-handover --scope object\r\n\r\nDocument the class ZCL_ORDER_APPROVAL for the support team. Audience: developer.\r\n```\r\n\r\nOutput:\r\n\r\n```\r\n## ZCL_ORDER_APPROVAL (CLAS)\r\n\r\n### Overview\r\nPackage: ZORD_APPROVAL | Author: JSMITH | Last changed: 2026-03-29\r\nCentral approval decision engine for sales orders above the configured\r\nthreshold. Called from the SAP workflow approval step.\r\n\r\n### Purpose\r\nValidates a sales order's state and executes the approve or reject action,\r\nthen triggers an email notification. Solves the requirement that sales orders\r\nabove EUR 10,000 must be approved by a manager before delivery creation.\r\n\r\n### Interface\r\nPublic methods:\r\n| Method | Signature | Description |\r\n|-------------|--------------------------------------------------------|--------------------------------------|\r\n| CONSTRUCTOR | io_email_sender TYPE REF TO zif_email_sender | Inject email dependency |\r\n| APPROVE | iv_order_id TYPE vbeln; RAISING zcx_approval_error | Approve the order; send notification |\r\n| REJECT | iv_order_id TYPE vbeln; iv_reason TYPE string; | Reject the order; send notification |\r\n| | RAISING zcx_approval_error | |\r\n\r\n### Logic Flow\r\n1. Read order header from I_SalesOrder (released CDS VDM)\r\n2. Validate the current status allows the requested transition\r\n3. Apply the approve/reject state change\r\n4. Delegate the notification to ZIF_EMAIL_SENDER (email failure is logged, not blocking)\r\n\r\n### Data Access\r\n| Table/View | Operation | Purpose |\r\n|--------------|-----------|----------------------------------|\r\n| I_SalesOrder | READ | Read order header for validation |\r\n| ZORD_CONFIG | READ | Read the approval threshold |\r\n\r\n### Dependencies\r\nCalled by: ZORDER_WORKFLOW (workflow task exit)\r\nCalls: ZIF_EMAIL_SENDER (injected), I_SalesOrder, ZCX_APPROVAL_ERROR\r\n\r\n### Error Handling\r\n- ZCX_APPROVAL_ERROR raised on invalid state transitions and empty reject reason\r\n- CX_BCS_EXCEPTION from the email sender is caught and logged to SLG1 (object ZORD); does NOT block approval\r\n\r\n### Notes\r\n- No partial approval — full order only.\r\n- No multi-level approval chain in this version.\r\n- Email template is hardcoded in ZCL_BCS_EMAIL_SENDER.\r\n```\r\n\r\n### `package` scope — full handover deliverable\r\n\r\nThe remainder of this skill (steps 1–7 below and the full Output Structure) is the `package` scope: the complete handover dossier for a development package or full stack. It is unchanged.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- The ABAP source code (paste) or object/package name (if MCP connected)\r\n- Scope: single object / full stack / package\r\n\r\nOptional (will improve output):\r\n- Business context and purpose\r\n- Target audience: developer / functional consultant / Basis administrator\r\n- Whether configuration documentation is needed\r\n- Known authorization objects to document\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Inventory the Objects\r\n\r\nCollect all objects to be documented. If MCP is connected, use\r\n`sap_object_structure` or `sap_search_object` to retrieve the full list.\r\nOtherwise work from provided source code.\r\n\r\nBuild an object catalog: each object's name, type, purpose (derived from\r\nABAP Doc or from the code's apparent intent).\r\n\r\n### Step 2 — Architecture Overview\r\n\r\nDescribe how the objects relate to each other:\r\n- Which classes call which\r\n- Which CDS views depend on which tables\r\n- Which RAP layers exist (table → CDS → BDEF → service)\r\n- Which BAdI implementations are registered\r\n\r\nDraw a simple text-based dependency diagram if the structure is non-trivial.\r\n\r\n### Step 3 — Object Documentation\r\n\r\nFor each object, document:\r\n- Purpose\r\n- Key methods / fields / parameters\r\n- Dependencies\r\n- Constraints and limitations (where visible from the code)\r\n\r\n### Step 4 — Configuration Requirements\r\n\r\nList any Customizing or configuration steps needed:\r\n- IMG paths (if identifiable)\r\n- Feature flags or program parameters\r\n- Communication arrangements (BTP)\r\n- SMTP / output type configuration (email, forms)\r\n- RFC destinations\r\n\r\n### Step 5 — Authorization Model\r\n\r\nDocument:\r\n- Authorization objects checked in the code\r\n- Which roles typically need these\r\n- Any implicit authorization (via standard FM/BAPI calls)\r\n\r\n### Step 6 — Test Plan\r\n\r\nProduce a test plan outline:\r\n- Functional test scenarios\r\n- Edge cases to test\r\n- Negative scenarios\r\n- Any performance test requirements\r\n\r\n### Step 7 — Operational Notes\r\n\r\nDocument:\r\n- Background job requirements\r\n- Monitoring requirements (where to check errors)\r\n- Known limitations\r\n- Upgrade considerations\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n# Technical Documentation — {title}\r\n\r\n**Package:** {package}\r\n**Prepared:** {date}\r\n**Audience:** {developer | functional | basis | all}\r\n**Status:** Draft / Final\r\n\r\n---\r\n\r\n## 1. Object Catalog\r\n\r\n| Object | Type | Package | Purpose |\r\n|--------|------|---------|---------|\r\n| {name} | {type} | {package} | {purpose} |\r\n\r\n---\r\n\r\n## 2. Architecture Overview\r\n\r\n### Layered Structure\r\n```\r\n{text_diagram or layer description}\r\n```\r\n\r\n### Key Relationships\r\n- {object_a} depends on {object_b}: {reason}\r\n- {object_c} is called by {object_d} via {mechanism}\r\n\r\n---\r\n\r\n## 3. Object Details\r\n\r\n### {Object_Name} ({type})\r\n\r\n**Purpose:** {purpose}\r\n\r\n**Public Interface:**\r\n| Method / Field | Signature / Type | Description |\r\n|----------------|-----------------|-------------|\r\n| {name} | {sig} | {desc} |\r\n\r\n**Dependencies:**\r\n- {dependency_1}\r\n- {dependency_2}\r\n\r\n**Constraints:** {constraints}\r\n\r\n---\r\n\r\n## 4. Configuration Requirements\r\n\r\n| Step | Where | What to Configure | Default / Expected Value |\r\n|------|-------|-------------------|--------------------------|\r\n| {step} | {IMG path or tx} | {config_item} | {value} |\r\n\r\n---\r\n\r\n## 5. Authorization Model\r\n\r\n| Auth Object | Fields | Typical Role | Checked In |\r\n|-------------|--------|-------------|------------|\r\n| {obj} | {fields} | {role} | {location} |\r\n\r\n---\r\n\r\n## 6. Test Plan\r\n\r\n### Functional Tests\r\n| Test Case | Precondition | Steps | Expected Result |\r\n|-----------|--------------|-------|-----------------|\r\n| {tc} | {precond} | {steps} | {expected} |\r\n\r\n### Edge Cases\r\n- {edge_case_1}\r\n- {edge_case_2}\r\n\r\n### Negative Tests\r\n- {negative_1}\r\n\r\n---\r\n\r\n## 7. Operational Notes\r\n\r\n**Background Jobs:** {job_name, schedule, monitoring}\r\n**Error Logs:** {SLG1 object, SM21, other}\r\n**Known Limitations:** {limitations}\r\n**Upgrade Considerations:** {notes}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER document behavior that cannot be derived from the provided code or context\r\n- If a method's purpose is unclear from code, mark it as \"Purpose unknown — review\r\n required\" rather than inventing a description\r\n- Do not document internal private methods unless the audience is developers\r\n- If configuration steps are not visible in the code, note that they must be\r\n confirmed with the functional team\r\n- Authorization objects: only document what is AUTHORITY-CHECK'd in the code;\r\n do not speculate about what roles are needed\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-handover\r\n\r\nDocument the order approval package ZORD_APPROVAL for handover to the support\r\nteam. Audience: developer + functional consultant. Include test plan.\r\n\r\nObjects:\r\n- ZCL_ORDER_APPROVAL (class) — main approval engine\r\n- ZCL_ORDER_APPROVAL_TEST (test class)\r\n- ZIF_EMAIL_SENDER (interface) — email abstraction\r\n- ZCL_BCS_EMAIL_SENDER (class) — CL_BCS implementation\r\n- ZORDER_WORKFLOW (workflow) — approval workflow\r\n\r\nBusiness context: Sales orders above EUR 10,000 require manager approval via\r\nSAP workflow before delivery can be created. The approval class checks the\r\norder status and sends email notifications via CL_BCS.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n# Technical Documentation — Order Approval Package\r\n\r\n**Package:** ZORD_APPROVAL\r\n**Prepared:** 2026-03-29\r\n**Audience:** Developer + Functional Consultant\r\n**Status:** Draft\r\n\r\n---\r\n\r\n## 1. Object Catalog\r\n\r\n| Object | Type | Package | Purpose |\r\n|---------------------------|-----------|---------------|----------------------------------------------------|\r\n| ZCL_ORDER_APPROVAL | Class | ZORD_APPROVAL | Main approval engine — approve/reject orders |\r\n| ZIF_EMAIL_SENDER | Interface | ZORD_APPROVAL | Email sender abstraction for testability |\r\n| ZCL_BCS_EMAIL_SENDER | Class | ZORD_APPROVAL | CL_BCS implementation of ZIF_EMAIL_SENDER |\r\n| ZCL_ORDER_APPROVAL_TEST | Test Class| ZORD_APPROVAL | Unit tests for ZCL_ORDER_APPROVAL |\r\n| ZORDER_WORKFLOW | Workflow | ZORD_WORKFLOW | SAP Workflow routing sales orders to manager approval |\r\n| ZCX_APPROVAL_ERROR | Exception | ZORD_APPROVAL | Typed exception for approval failures |\r\n\r\n---\r\n\r\n## 2. Architecture Overview\r\n\r\n### Layered Structure\r\n```\r\n[ZORDER_WORKFLOW] — workflow task exit\r\n |\r\n v\r\n[ZCL_ORDER_APPROVAL]\r\n |\r\n ├── reads VBAK / VBAP (via released VDM: I_SalesOrder)\r\n |\r\n └── delegates email to [ZIF_EMAIL_SENDER]\r\n |\r\n └── implemented by [ZCL_BCS_EMAIL_SENDER]\r\n |\r\n └── uses CL_BCS (SAP standard)\r\n```\r\n\r\n### Key Relationships\r\n- ZORDER_WORKFLOW calls ZCL_ORDER_APPROVAL via a workflow task exit (TS type)\r\n- ZCL_ORDER_APPROVAL receives ZIF_EMAIL_SENDER via constructor injection\r\n- ZCL_BCS_EMAIL_SENDER is the production implementation; test doubles replace it in unit tests\r\n\r\n---\r\n\r\n## 3. Object Details\r\n\r\n### ZCL_ORDER_APPROVAL (Class)\r\n\r\n**Purpose:** Central approval decision engine. Called from the SAP workflow\r\nwhen a sales order reaches the approval step. Validates the order state,\r\nexecutes the approve or reject action, and triggers an email notification.\r\n\r\n**Public Interface:**\r\n| Method | Signature | Description |\r\n|------------------|--------------------------------------------------------|------------------------------------|\r\n| CONSTRUCTOR | io_email_sender TYPE REF TO zif_email_sender | Inject email dependency |\r\n| APPROVE | iv_order_id TYPE vbeln; RAISING zcx_approval_error | Approve the order; send notification |\r\n| REJECT | iv_order_id TYPE vbeln; iv_reason TYPE string; | Reject the order; send notification |\r\n| | RAISING zcx_approval_error | |\r\n\r\n**Dependencies:**\r\n- ZIF_EMAIL_SENDER (injected) — sends email notifications\r\n- I_SalesOrder (released CDS VDM) — reads order header data\r\n- ZCX_APPROVAL_ERROR — raised on invalid state transitions\r\n\r\n**Constraints:**\r\n- Does not support partial approval (full order only)\r\n- No support for multi-level approval chains in this version\r\n- Email failure (CX_BCS_EXCEPTION) is caught and logged; does NOT block approval\r\n\r\n---\r\n\r\n## 4. Configuration Requirements\r\n\r\n| Step | Where | What to Configure | Expected Value |\r\n|------|---------------------------|--------------------------------------|------------------------------|\r\n| 1 | SCOT (Email config) | SMTP server and route | Active SMTP route to mail server |\r\n| 2 | SCOT | Default sender address | noreply@company.com |\r\n| 3 | SWI5 / SWDD | ZORDER_WORKFLOW activation | Workflow must be active in target system |\r\n| 4 | IMG: SD Order Management | Approval threshold (if configurable) | EUR 10,000 (stored in ZORD_CONFIG table) |\r\n\r\n---\r\n\r\n## 5. Authorization Model\r\n\r\n| Auth Object | Fields | Typical Role | Checked In |\r\n|---------------|---------------------|-----------------------|-----------------------|\r\n| V_VBAK_AAT | AUART (order type) | SD Sales Order Approver | ZCL_ORDER_APPROVAL=>APPROVE |\r\n| V_VBAK_VKO | VKORG (sales org) | SD Sales Order Approver | ZCL_ORDER_APPROVAL=>APPROVE |\r\n\r\nNote: The email notification does not require separate authorization. CL_BCS\r\nuses the technical system user's mail configuration.\r\n\r\n---\r\n\r\n## 6. Test Plan\r\n\r\n### Functional Tests\r\n| Test Case | Precondition | Steps | Expected Result |\r\n|-----------------------------------|---------------------------------|---------------------------------------|----------------------------------|\r\n| Approve valid order | Sales order >10K in status 'Open'| Call APPROVE with valid order ID | Status → 'Approved'; email sent |\r\n| Reject with reason | Sales order in status 'Open' | Call REJECT with reason text | Status → 'Rejected'; email sent |\r\n| Approve already-approved order | Order in status 'Approved' | Call APPROVE again | ZCX_APPROVAL_ERROR raised |\r\n| Reject with empty reason | Any open order | Call REJECT with iv_reason = '' | ZCX_APPROVAL_ERROR raised |\r\n| Email server down | SCOT SMTP inactive | Call APPROVE | Approval succeeds; SLG1 log entry |\r\n\r\n### Edge Cases\r\n- Order with no requester email address — should log warning, not dump\r\n- Order ID not found in I_SalesOrder — ZCX_APPROVAL_ERROR with meaningful text\r\n- Concurrent approval by two managers — second approval should raise error gracefully\r\n\r\n### Negative Tests\r\n- Call APPROVE without V_VBAK_AAT authorization → NO_AUTHORITY exception expected\r\n\r\n---\r\n\r\n## 7. Operational Notes\r\n\r\n**Background Jobs:** None — approval is triggered synchronously from workflow.\r\n\r\n**Error Logs:** Check SLG1 with log object ZORD for email notification errors.\r\nEmail failures do not block the approval outcome but are logged for monitoring.\r\n\r\n**Known Limitations:**\r\n- Only single-level approval supported. Multi-level approval requires workflow redesign.\r\n- Email template is hardcoded in ZCL_BCS_EMAIL_SENDER — not configurable via Customizing.\r\n\r\n**Upgrade Considerations:**\r\n- Uses I_SalesOrder (C1 Released) — safe for S/4HANA upgrades.\r\n- CL_BCS is C1 Released — safe for BTP migration.\r\n- ZORDER_WORKFLOW uses classic SAP Workflow (BC-BMT-WFM) — consider migration\r\n to SAP Build Process Automation for BTP environments.\r\n```\r\n",
128
+ "sha256": "e0288cba97c213ba3aabc1bda6678f010e0d1b3dcf6d8c4814fe53bebf5d21cb",
122
129
  "signature": "",
123
- "signedAt": "2026-09-17T20:19:05.024Z"
130
+ "signedAt": "2026-09-28T14:37:04.656Z"
124
131
  },
125
132
  "abap-impact": {
126
133
  "name": "abap-impact",
127
134
  "body": "---\r\nname: abap-impact\r\ndescription: >\r\n Analyze the impact of changing an ABAP object before you touch it.\r\n Traces all where-used references, identifies dependent objects, checks\r\n transport status, runs ATC, and produces a risk assessment. Use before\r\n any modification to understand what could break.\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.3.0\"\r\nwidget: dep-graph\r\n---\r\n\r\n# abap-impact\r\n\r\n## Purpose\r\n\r\nAnswer the question: \"If I change this object, what else is affected?\"\r\n\r\nBefore modifying any ABAP object, this skill traces every reference,\r\nidentifies dependent programs, checks transport status, and produces a\r\nrisk assessment. Prevents the #1 cause of production incidents — changing\r\nsomething without knowing what depends on it.\r\n\r\nUse cases:\r\n- **Pre-change assessment** — before a bug fix, enhancement, or refactoring\r\n- **Transport planning** — understand the blast radius before releasing\r\n- **Retirement analysis** — is this object safe to delete?\r\n- **Upgrade remediation** — what else uses the deprecated table/FM we're replacing?\r\n\r\n## When to Use\r\n\r\n- Use BEFORE changing or deleting an object — blast radius: where-used, dependents, open transports, risk rating.\r\n- NOT for understanding the object's internals — use `/abap-radar`; NOT for a quality grade — use `/abap-review`.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_usage_references` | Find all objects that reference this one |\r\n| `sap_get_source` | Read callers to understand how they use this object |\r\n| `sap_search_object` | Find related objects (same naming pattern, package) |\r\n| `sap_sql_query` | Query TADIR for package, transport status |\r\n| `sap_atc_run` | Run quality checks on dependent objects |\r\n\r\n## Inputs\r\n\r\nRequired:\r\n- Object type (PROG, CLAS, INTF, FUGR, TABL, DDLS, etc.)\r\n- Object name\r\n\r\nOptional:\r\n- Change description — what are you planning to change? (helps assess risk)\r\n- Depth: `quick` (where-used only) or `deep` (includes ATC on dependents). Default: `quick`\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Identify the Object\r\n\r\n```\r\nsap_get_source (objectType, objectName)\r\n```\r\n\r\nRead the source to understand what this object provides — public methods,\r\nfunction module interface, table fields, CDS view elements.\r\n\r\n### Step 2 — Trace Where-Used\r\n\r\n```\r\nsap_usage_references (objectType, objectName)\r\n```\r\n\r\nGet every object that references this one. Group by type:\r\n- Programs that call this FM/method\r\n- Classes that implement this interface\r\n- Views that reference this table\r\n- Other objects in the same package\r\n\r\n### Step 3 — Analyze Each Dependent\r\n\r\nFor each dependent object (up to top 20):\r\n- What namespace? (Z/Y = custom, SAP standard = higher risk)\r\n- What package? (same package = lower risk, different = higher)\r\n- How is it used? (direct call, type reference, data access)\r\n\r\n### Step 4 — Check Transport Status\r\n\r\n```\r\nsap_sql_query: SELECT trkorr, as4text FROM e071\r\n INNER JOIN e07t ON e071~trkorr = e07t~trkorr\r\n WHERE object = '{TYPE}' AND obj_name = '{NAME}'\r\n AND e07t~langu = 'E'\r\n```\r\n\r\nIs this object currently in a transport? Is someone else working on it?\r\n\r\n### Step 5 — Produce Impact Report\r\n\r\n```\r\n## Impact Analysis: {OBJECT_NAME} ({OBJECT_TYPE})\r\n\r\n### Object Summary\r\nPackage: {package} | Type: {type} | Last changed: {date} by {user}\r\n{1-sentence description}\r\n\r\n### Change Planned\r\n{What the developer intends to change, if provided}\r\n\r\n### Direct Dependencies ({count})\r\nObjects that directly reference {OBJECT_NAME}:\r\n\r\n| # | Object | Type | Package | How Used | Risk |\r\n|---|--------|------|---------|----------|------|\r\n| 1 | {name} | PROG | {pkg} | Calls method X | Medium |\r\n| 2 | {name} | CLAS | {pkg} | Implements interface | High |\r\n\r\n### Risk Assessment\r\n\r\nHigh risk: {count} — {description}\r\nMedium risk: {count} — {description}\r\nLow risk: {count} — {description}\r\n\r\nRecommendation: {safe to proceed / test these N objects / involve these teams}\r\n\r\n### Transport Status\r\n{Current transport assignments, if any}\r\n\r\n### Suggested Test Scope\r\nBased on dependencies, these objects should be tested after the change:\r\n{list of objects to retest}\r\n```\r\n\r\n### Risk Classification\r\n\r\n| Factor | Low | Medium | High |\r\n|--------|-----|--------|------|\r\n| Namespace | Same Z package | Different Z package | SAP standard |\r\n| Usage type | Type reference only | Data access | Direct call / interface |\r\n| Change scope | Comment / cosmetic | Logic change | Interface change |\r\n| Dependent count | 0-5 | 6-20 | 21+ |\r\n\r\n## Guardrails\r\n\r\n- Read-only skill. Never modify anything.\r\n- If where-used returns 100+ results, show top 20 grouped by risk and\r\n summarize the rest: \"Plus 83 additional references (mostly in package X).\"\r\n- If the object is SAP standard (not Z/Y): warn that modifying SAP standard\r\n code requires a modification key and should be avoided.\r\n- If the object is in a released transport: warn that it may already be in QA/production.\r\n- Do not downplay risk. If 30 programs call a function module and the developer\r\n wants to change the interface, say it clearly.\r\n",
128
135
  "sha256": "b973055165d7c7f402a208756f5cdc791bc2fbde4e577c4836f81d6477a0f5eb",
129
136
  "signature": "",
130
- "signedAt": "2026-09-17T20:19:05.071Z"
137
+ "signedAt": "2026-09-28T14:37:04.692Z"
131
138
  },
132
139
  "abap-incident": {
133
140
  "name": "abap-incident",
134
- "body": "---\nname: abap-incident\ndescription: >\n End-to-end incident resolution pipeline. From short dump to fix deployed\n and documented in one conversation. Seven stages: identify, understand,\n fix, clean core check (optional), verify, deliver, report. Delegates to\n existing CSPeach tools. Optional --clean-core flag verifies all APIs\n are released for ABAP Cloud.\nphase: DEBUG\nrequires_mcp: required\nforge_rules: [7, 8, 9, 10]\nversion: \"1.1\"\n---\n\n# abap-incident\n\n## Purpose\n\nResolve an SAP production incident end-to-end in a single conversation. The\npipeline walks through seven stages — from reading a short dump to producing a\nformal incident resolution report — delegating each stage to existing CSPeach\ntools. The developer approves every stage before the next one begins.\n\nForge Rule 7: snapshot before every write (and 7a: `sap_update_method` for\nmethod-only changes, never `sap_set_source`). Forge Rule 8: present the\nfull multi-object plan before writing. Forge Rule 9: assign all changes\nto a dedicated session transport. Forge Rule 10: verify activation after\nevery write. All are hard stops.\n\nOptional `--clean-core` flag adds a released API verification stage using\n`sap_api_state` (queries ARS_W_API_STATE).\n\n## When to Use\n\n- A short dump has occurred and you need to diagnose, fix, verify, and document\n- Developer provides a dump ID, program name, error description, or says\n \"show me recent dumps\"\n- After-the-fact investigation of a resolved dump (reporting only)\n- Incident ticket requires structured resolution documentation\n\nDo NOT use for:\n- Proactive code scanning (use `/abap-upgrade-scan` or `/abap-atc-fix`)\n- Greenfield development (use `/abap-generate` or `/abap-rap`)\n- Performance tuning without a dump (use `/abap-performance`)\n\n## Inputs Expected\n\nRequired (any one of these):\n- Dump ID (from ST22 or monitoring)\n- Program name + error description\n- \"Show me recent dumps\" / \"dumps for user X\"\n- Manual dump details (if dump is in PRD and MCP connects to DEV)\n\nOptional:\n- `--clean-core` — enable Stage 4 released API verification\n- Transport request number (will be asked if not provided and needed)\n\n## Cross-System Awareness\n\nDumps typically occur in QAS or PRD, but fixes must be made in DEV. The MCP\nconnection is to a single system. If the dump is in production and the\nconnection is to development:\n\n- **Stages 1-2:** Developer provides the dump details manually (dump ID, error\n message, screenshot, stack trace) rather than via `sap_short_dump`. The\n orchestrator works with whatever the developer provides.\n- **Stages 3-7:** Execute on the DEV system as normal.\n\nDocument the cross-system context in the final report (Stage 7).\n\n## Delegation Strategy\n\nThe orchestrator delegates — it does NOT reimplement existing tool logic.\n\n| Pipeline Stage | Delegates To | What Orchestrator Adds |\n|---|---|---|\n| Stage 1+2 (IDENTIFY+UNDERSTAND) | `/abap-dump` Steps 1–5 | Pipeline state, where-used impact, advance-vs-write decision |\n| Stage 3 (FIX) | Forge Rules 7, 7a, 10 safety pattern | Lock pre-check, diff preview, per-method writes via `sap_update_method` |\n| Stage 4 (CLEAN CORE) | `sap_api_state` tool | Optional flag, pattern extraction |\n| Stage 5 (VERIFY) | `sap_atc_run`, `sap_run_unit_test` | Go/no-go decision |\n| Stage 6 (DELIVER) | `sap_transport`, `sap_sql_query` | Conflict check, transport isolation (Rule 9) |\n| Stage 7 (REPORT) | Generate formatted report | Formal deliverable |\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Required Behavior\n\n### Stage 1+2 — IDENTIFY + UNDERSTAND (delegates to `/abap-dump`)\n\n**Purpose:** Find the dump, read the crashing code in full context, and produce\na proposed fix — without writing yet.\n\n**Delegation contract:** This stage runs `/abap-dump` Steps 1–5\nunmodified. The orchestrator does NOT reimplement diagnosis logic. When\n`abap-dump` improves a heuristic, this pipeline benefits automatically.\n\n`abap-dump` produces:\n- **diagnostic extract** — error name, system + client, program/method/line,\n source extract with `>>>>>` marker, call stack (top 15), system fields\n- **root-cause diagnosis** with confidence rating\n- **proposed fix** with diff and explanation\n\n**The orchestrator adds on top:**\n- Pipeline state tracking (which dump, which transport, which snapshot path)\n- Impact analysis via `sap_usage_references` — callers downstream of the crash\n (raw reference list; carried into the Stage 7 report)\n- The decision to advance to Stage 3 (vs. write immediately, which is what\n `abap-dump` does on its own)\n\n**Manual check:** If the dump suggests orphaned locks, advise the developer to\ncheck SM12 on the affected system. This is outside ADT scope.\n\n**SAP standard code escape:** `/abap-dump` already declines to fix\ndumps in SAP standard code (non-Z/Y namespace) and reports relevant SAP Note\nkeywords. The orchestrator captures those Note numbers in Stage 7 and skips\nStages 3–6.\n\n**Cross-system context:** If the dump is in PRD and MCP connects to DEV, the\ndeveloper pastes the dump details directly. `/abap-dump` accepts\nmanual input the same way; the orchestrator does not branch.\n\n**Developer decision:** \"Approve the fix? [y / modify / skip]\" — same gate\n`/abap-dump` provides natively. Here the answer drives the pipeline\nto Stage 3 instead of writing immediately.\n\n### Stage 3 — FIX\n\n**Purpose:** Apply the fix with full safety controls.\n\n**Sequence (exact order — no deviations):**\n\n1. **Lock pre-check:** Call `sap_lock` to check if the object is locked by\n another user. If locked: report the lock holder, skip to Stage 7 with\n status \"blocked -- locked by {user}\". Do NOT attempt `sap_set_source`\n (it would burn 3 retries and fail with a raw HTTP 423).\n\n2. **Snapshot:** `sap_snapshot` (operation: take). If snapshot fails: stop\n immediately. No write proceeds without a successful snapshot. No exceptions.\n\n3. **Preview diff:** Use `sap_diff` to show old source vs proposed new source\n before writing. The developer sees exactly what will change. Wait for\n explicit approval.\n\n4. **Write:** Per **Forge Rule 7a**, prefer `sap_update_method` for any\n change confined to existing method bodies. Use `sap_set_source` only when\n the class definition itself must change (new method, removed method,\n signature change) or for non-class objects (programs, FMs).\n\n - `sap_update_method` — class methods in main source, body-only edits\n - `sap_set_source` — full programs, full class rewrites, definition changes\n\n **Limitation:** `sap_update_method` only works on methods in the main class\n source. Local handler class methods (locals_def/locals_imp) need\n `sap_set_source` with the full class source. See CLAUDE.md known issues.\n\n5. **Activation verify:** Call `sap_activate` explicitly (do not trust\n auto-activation from `sap_set_source`). Then call `sap_inactive_objects` to\n confirm the object is NOT on the inactive list. This is the only reliable\n activation check.\n\n6. **If anything fails at steps 4-5:** Call `sap_snapshot` (operation: restore)\n with the snapshot from step 2. Report the failure. Stop the pipeline.\n\n**Output:**\n- Fix applied, activation verified\n- Diff: what changed (from step 3 preview)\n- Snapshot file path (for rollback reference in the report)\n\n**Multi-object fixes:** If the fix requires changes to multiple objects, process\neach object through the full Stage 3 sequence (lock, snapshot, diff, write,\nactivate, verify) on the same transport.\n\n### Stage 4 — CLEAN CORE CHECK (optional)\n\n**Trigger:** Only when `--clean-core` flag is provided. Skipped by default.\n\n**Purpose:** Verify every API used in the fixed code is released for ABAP Cloud.\n\n**Step 1 — Extract API references from source via pattern matching:**\n\n| Source Pattern | ARS Object Type |\n|---|---|\n| `CALL FUNCTION '{name}'` | FUNC |\n| `NEW {class}(` or `{class}=>{method}(` | CLAS |\n| `SELECT ... FROM {table}` | TABL |\n| `TYPE REF TO {interface}` | INTF |\n| CDS view references | DDLS |\n\nPresent the extracted list to the developer for review before querying.\n\n**Step 2 — Discovery (first run only):**\n\nRun a discovery query to learn what release state values exist on THIS system:\n```sql\nSELECT DISTINCT RELEASE_STATE FROM ARS_W_API_STATE UP TO 20 ROWS\n```\n\nDo NOT hardcode release state values. The enum changes between S/4HANA releases.\nAlways check what the connected system returns.\n\n**Step 3 — Check each API:**\n\nCall `sap_api_state` with the extracted object names and types, or fall back to\n`sap_sql_query` on ARS_W_API_STATE:\n```sql\nSELECT OBJECT_NAME, OBJECT_TYPE, RELEASE_STATE\n FROM ARS_W_API_STATE\n WHERE OBJECT_NAME = '{NAME}'\n AND OBJECT_TYPE = '{TYPE}'\n```\n\n**Fallback:** If ARS_W_API_STATE does not exist on the system (ECC, older S/4),\nthe query will fail. In that case report: \"Clean core check not available --\nARS_W_API_STATE not found on this system. Upgrade to S/4HANA 2020+ for released\nAPI verification.\" Do not crash. Continue the pipeline.\n\n**Output:**\n```\nClean Core Check:\n FM BAPI_SALESORDER_GETLIST -> RELEASED\n FM CONVERSION_EXIT_ALPHA_INPUT -> RELEASED\n TABLE VBAK -> NOT_TO_BE_RELEASED\n -> Alternative: CDS view I_SalesOrder (RELEASED)\n CLASS CL_GUI_ALV_GRID -> NOT_TO_BE_RELEASED\n -> No released alternative (UI technology change needed)\n\nResult: 2/4 APIs clean core compliant. 2 need attention.\n```\n\n**Developer decision:** Fix the unreleased API usage now, or note as technical\ndebt and proceed?\n\n### Stage 5 — VERIFY\n\n**Purpose:** Quality gate before transport release.\n\n**Tools:**\n- `sap_atc_run` (variant: DEFAULT) — standard quality checks\n- `sap_atc_run` (variant: cloud readiness variant) — only if `--clean-core`.\n The variant name is system-dependent (could be `ABAP_CLOUD_READINESS`,\n `SAP_CLOUD_READINESS`, or a customer variant). Ask the developer or query\n available variants if unknown.\n- `sap_run_unit_test` — only if tests already exist for this object\n\n**Rules:**\n- Priority 1 ATC findings = blocker. Must fix before proceeding.\n- Priority 2 = warning. Report but do not block.\n- Unit tests: if they exist, run them. If they do not exist, move on. Do not\n block for missing tests.\n\n**Output:**\n- ATC results (P1/P2/P3 count)\n- Unit test results (pass/fail/not found)\n- Go/no-go for transport\n\n### Stage 5b — RETEST (developer action)\n\n**Purpose:** Confirm the fix actually resolves the original issue.\n\n**This is a manual step.** Prompt the developer:\n\n\"Please re-execute the transaction/job that caused the dump and confirm the\nerror no longer occurs.\"\n\nWait for developer confirmation before proceeding to Stage 6.\n\nIf the developer reports it still dumps: loop back to Stage 1 with the new dump\ndetails. Do not proceed to Stage 6.\n\n### Stage 6 — DELIVER\n\n**Purpose:** Confirm transport readiness.\n\n**Note:** `sap_set_source` in Stage 3 already performs a transport pre-check\nvia E071 internally (WriteSourceTool.findExistingTransport). Stage 6 adds the\nconflict check and summary — it does not duplicate the pre-check.\n\n**Tools:**\n- `sap_sql_query` on E071/E070 — conflict check (same object in other open\n transports)\n- `sap_transport` (operation: list) — verify transport is modifiable\n\n**If no transport assigned:** Ask the developer for a transport number or create\none via `sap_transport` (operation: create, with package).\n\n**Output:**\n- Transport number, description, owner\n- Objects in transport\n- Conflict check result (any other transports with the same object)\n\n**Developer decision:** \"Release transport?\" CSPeach does NOT release\nautomatically. That is the developer's call in STMS/SE09.\n\n### Stage 7 — REPORT\n\n**Purpose:** Produce a formal incident resolution document.\n\n**Template:**\n\n```\n## Incident Resolution Report\n\n### System\nSID: {SID} | Client: {CLIENT} | Release: {SAP_RELEASE}\n\n### Incident\nError: {RUNTIME_ERROR}\nProgram: {PROGRAM} | Method: {METHOD} | Line: {LINE}\nUser: {USER} | Date: {DATE}\nTransaction: {TCODE}\n\n### Root Cause\n{1-3 sentence explanation}\n\n### Fix Applied\n{Diff showing old vs new code}\n\n### Rollback\nSnapshot: {SNAPSHOT_FILE_PATH}\nRestore command: sap_snapshot (operation: restore, file: {path})\n\n### Clean Core Status\n{If --clean-core was used: compliance table}\n{If not: \"Clean core check not requested\"}\n\n### Verification\nATC (DEFAULT): {P1}/{P2}/{P3}\nATC (Cloud): {if applicable}\nUnit Tests: {pass/fail/not found}\nRetest: {developer confirmed / not performed}\n\n### Transport\nNumber: {TRANSPORT}\nDescription: {DESC}\nStatus: Ready for release\n\n### Related SAP Notes\n{If standard code was involved: relevant SAP Note numbers}\n{If not applicable: \"N/A -- custom code only\"}\n\n### Timeline\nIdentified: {timestamp}\nFixed: {timestamp}\nVerified: {timestamp}\nTotal: {minutes} minutes\n```\n\nThis report is the deliverable. For consulting engagements, it is the proof of\nwork. For internal support, it is the ticket documentation.\n\n## Edge Cases\n\n| Edge Case | Handling |\n|-----------|---------|\n| No dump found for the error | Ask developer to reproduce, or search by program name |\n| Dump is in SAP standard code (not Z/Y) | Do NOT fix. Search for SAP Notes with error keywords. Include Note number in report. |\n| Dump in PRD, MCP connected to DEV | Developer provides dump details manually. Fix in DEV. |\n| Object locked by another user | Pre-check via `sap_lock`. Report lock holder. Skip to Stage 7 with \"blocked\" status. |\n| Fix introduces new ATC P1 findings | Block at Stage 5. Developer must resolve before transport. |\n| No transport assigned | Ask developer or create one |\n| Clean core check finds unreleased API | Report it. Developer decides: fix now or accept debt. |\n| ARS_W_API_STATE not on system | Report \"not available -- S/4HANA 2020+ required\". Continue pipeline. |\n| Object has no unit tests | Run ATC only. Do not block. |\n| Multiple dumps for same root cause | Identify the pattern, fix the root cause once |\n| Fix requires multiple objects changed | Process each object through Stages 3-5, same transport |\n| Fix is in local class (locals_def/locals_imp) | Use `sap_set_source` with full class. `sap_update_method` only works on main source. |\n| Orphaned locks after dump (SM12) | Advise developer to check SM12 manually. Outside ADT scope. |\n| Cloud readiness ATC variant name unknown | Ask developer or query available variants on the system. |\n\n## Guardrails\n\n### Write Safety\n- NEVER call `sap_set_source` or `sap_update_method` without a prior successful\n `sap_snapshot` for that object (Forge Rule 7). Snapshot failure = hard stop.\n- NEVER call `sap_set_source` before `sap_syntax_check` passes. `sap_set_source`\n auto-activates — broken code becomes live for all users immediately.\n- NEVER attempt to write if `sap_lock` shows the object is locked by another\n user. Report the lock holder and skip to Stage 7.\n\n### Activation Safety\n- ALWAYS call `sap_activate` explicitly after `sap_set_source`. The\n auto-activation in `sap_set_source` is unreliable — it reports `activated:true`\n while the object remains inactive.\n- ALWAYS call `sap_inactive_objects` after `sap_activate` to confirm the object\n is no longer on the inactive list. This is the only reliable check.\n- If the object is still inactive after explicit activation: treat as activation\n failure. Show the errors, restore from snapshot, report failure.\n\n### Developer Control\n- NEVER auto-fix without developer approval. The developer approves at every\n stage transition and before every write.\n- NEVER release a transport automatically. Transport release is the developer's\n explicit decision.\n- NEVER skip Stage 5b (RETEST). Always ask the developer to confirm the fix\n resolves the original issue, even if ATC and unit tests pass.\n\n### Clean Core Safety\n- NEVER hardcode release state values. Always discover what values exist on the\n connected system via the discovery query.\n- If `sap_api_state` or ARS_W_API_STATE is not available, report it and continue.\n Do not crash the pipeline.\n\n### Error Handling\n- If any stage fails, stop the pipeline. Report what succeeded, what failed,\n and the current system state. Ask the developer how to proceed.\n- If a write fails, restore from snapshot immediately. Do not leave the object\n in an inconsistent state.\n- If activation fails, restore from snapshot. Do not proceed to verification\n with an inactive object.\n\n## Example Prompt\n\n```\n/abap-incident\n\nDump ID: 20260408_143022_DEVK900001\n```\n\nOr:\n\n```\n/abap-incident --clean-core\n\nProgram ZSD_ORDER_PROCESS crashed with RAISE_EXCEPTION in production.\nError: CX_SY_OPEN_SQL_DB. The SELECT on VBAK returns too many rows\nwhen customer ID is initial.\n```\n\nOr:\n\n```\n/abap-incident\n\nShow me recent dumps for user JSMITH\n```\n\n## Example Output\n\n```\n--- Stage 1: IDENTIFY ---\n\nsap_short_dump (list, user: JSMITH) -> 3 dumps found\nsap_short_dump (detail, id: 20260408_143022) -> diagnostics extracted\n\nError: RAISE_EXCEPTION (CX_SY_OPEN_SQL_DB)\nProgram: ZSD_ORDER_PROCESS | Method: GET_ORDERS | Line: 47\nUser: JSMITH | Date: 2026-04-08 | Transaction: VA01\nCall stack: ZSD_ORDER_PROCESS=>GET_ORDERS (line 47) -> ...\n\nRoot cause: SELECT on VBAK without WHERE clause on KUNNR when\niv_customer is initial. Returns all rows, exceeds memory limit.\n\nIs this the right dump? Proceed to understand? [y/n]\n> y\n\n--- Stage 2: UNDERSTAND ---\n\nsap_get_source (ZSD_ORDER_PROCESS) -> source loaded (312 lines)\nsap_usage_references (ZSD_ORDER_PROCESS) -> 4 callers found\n\nBusiness purpose: Reads sales orders for a given customer.\nCrash condition: iv_customer parameter is INITIAL, causing unfiltered\nSELECT on VBAK.\n\nProposed fix: Add guard clause at method entry — raise CX_PARAMETER_ERROR\nif iv_customer is initial.\n\nWhere-used: ZSD_ORDER_REPORT, ZSD_BATCH_EXPORT, ZSD_API_WRAPPER,\nZSD_FIORI_SERVICE (all pass customer ID from user input).\n\nApprove the fix? [y / modify / skip]\n> y\n\n--- Stage 3: FIX ---\n\nsap_lock (ZSD_ORDER_PROCESS) -> not locked\nsap_snapshot (take, ZSD_ORDER_PROCESS) -> snapshot saved\nsap_diff -> showing proposed changes:\n\n Line 45: METHOD get_orders.\n+ Line 46: IF iv_customer IS INITIAL.\n+ Line 47: RAISE EXCEPTION TYPE cx_parameter_error.\n+ Line 48: ENDIF.\n Line 49: SELECT vbeln, erdat, kunnr FROM vbak ...\n\nProceed with write? [y/n]\n> y\n\nsap_syntax_check -> PASSED (0 errors)\nsap_set_source (transport: DEVK912400) -> 200\nsap_activate -> success\nsap_inactive_objects -> ZSD_ORDER_PROCESS NOT on inactive list\n\nFix applied and activation verified.\n\n--- Stage 5: VERIFY ---\n\nsap_atc_run (DEFAULT, ZSD_ORDER_PROCESS) -> P1: 0, P2: 1, P3: 3\n P2: Variable lv_temp declared but not used (line 201)\nsap_run_unit_test (ZSD_ORDER_PROCESS) -> not found (no test class)\n\nGo/no-go: GO (no P1 blockers)\n\n--- Stage 5b: RETEST ---\n\nPlease re-execute the transaction/job that caused the dump and confirm\nthe error no longer occurs.\n> Confirmed, no more dump.\n\n--- Stage 6: DELIVER ---\n\nsap_sql_query (E071, object ZSD_ORDER_PROCESS) -> 1 transport found\nTransport: DEVK912400 | Description: Fix null customer crash\nsap_sql_query (E070, conflict check) -> no conflicts\n\nRelease transport? (developer decision in STMS/SE09)\n\n--- Stage 7: REPORT ---\n\n## Incident Resolution Report\n\n### System\nSID: DEV | Client: 100 | Release: S/4HANA 2023\n\n### Incident\nError: RAISE_EXCEPTION (CX_SY_OPEN_SQL_DB)\nProgram: ZSD_ORDER_PROCESS | Method: GET_ORDERS | Line: 47\nUser: JSMITH | Date: 2026-04-08\nTransaction: VA01\n\n### Root Cause\nSELECT on VBAK without WHERE clause on KUNNR when iv_customer is\ninitial. Returns all rows, exceeds memory limit.\n\n### Fix Applied\nAdded guard clause: raise CX_PARAMETER_ERROR if iv_customer is initial.\n[diff shown above]\n\n### Rollback\nSnapshot: .cspeach/snapshots/ZSD_ORDER_PROCESS_20260408_143500.abap\nRestore command: sap_snapshot (operation: restore, file: above path)\n\n### Clean Core Status\nClean core check not requested\n\n### Verification\nATC (DEFAULT): P1: 0 / P2: 1 / P3: 3\nUnit Tests: not found\nRetest: developer confirmed\n\n### Transport\nNumber: DEVK912400\nDescription: Fix null customer crash\nStatus: Ready for release\n\n### Related SAP Notes\nN/A -- custom code only\n\n### Timeline\nIdentified: 14:35\nFixed: 14:38\nVerified: 14:41\nTotal: 6 minutes\n```\n\n## What This Is NOT\n\n- NOT a monitoring tool (does not watch for dumps in real-time)\n- NOT a ticketing system (does not create/close tickets)\n- NOT an approval workflow (developer makes all decisions)\n- NOT automatic (developer approves every stage)\n- NOT cross-system (MCP connects to one system at a time)\n\nThe developer drives. CSPeach does the heavy lifting.\n",
135
- "sha256": "e113cc331e1854dbc3adb16a575f25e844962b3c523f336539ca7e9c35c9c086",
141
+ "body": "---\r\nname: abap-incident\r\ndescription: >\r\n End-to-end incident resolution pipeline. From short dump to fix deployed\r\n and documented in one conversation. Seven stages: identify, understand,\r\n fix, clean core check (optional), verify, deliver, report. Delegates to\r\n existing CSPeach tools. Optional --clean-core flag verifies all APIs\r\n are released for ABAP Cloud.\r\nphase: DEBUG\r\nrequires_mcp: required\r\nforge_rules: [7, 8, 9, 10]\r\nversion: \"1.1\"\r\n---\r\n\r\n# abap-incident\r\n\r\n## Purpose\r\n\r\nResolve an SAP production incident end-to-end in a single conversation. The\r\npipeline walks through seven stages — from reading a short dump to producing a\r\nformal incident resolution report — delegating each stage to existing CSPeach\r\ntools. The developer approves every stage before the next one begins.\r\n\r\nForge Rule 7: snapshot before every write (and 7a: `sap_update_method` for\r\nmethod-only changes, never `sap_set_source`). Forge Rule 8: present the\r\nfull multi-object plan before writing. Forge Rule 9: assign all changes\r\nto a dedicated session transport. Forge Rule 10: verify activation after\r\nevery write. All are hard stops.\r\n\r\nOptional `--clean-core` flag adds a released API verification stage using\r\n`sap_api_state` (queries ARS_W_API_STATE).\r\n\r\n## When to Use\r\n\r\n- A short dump has occurred and you need to diagnose, fix, verify, and document\r\n- Developer provides a dump ID, program name, error description, or says\r\n \"show me recent dumps\"\r\n- After-the-fact investigation of a resolved dump (reporting only)\r\n- Incident ticket requires structured resolution documentation\r\n\r\nDo NOT use for:\r\n- Proactive code scanning (use `/abap-upgrade-scan` or `/abap-atc-fix`)\r\n- Greenfield development (use `/abap-generate` or `/abap-rap`)\r\n- Performance tuning without a dump (use `/abap-performance`)\r\n\r\n## Inputs Expected\r\n\r\nRequired (any one of these):\r\n- Dump ID (from ST22 or monitoring)\r\n- Program name + error description\r\n- \"Show me recent dumps\" / \"dumps for user X\"\r\n- Manual dump details (if dump is in PRD and MCP connects to DEV)\r\n\r\nOptional:\r\n- `--clean-core` — enable Stage 4 released API verification\r\n- Transport request number (will be asked if not provided and needed)\r\n\r\n## Cross-System Awareness\r\n\r\nDumps typically occur in QAS or PRD, but fixes must be made in DEV. The MCP\r\nconnection is to a single system. If the dump is in production and the\r\nconnection is to development:\r\n\r\n- **Stages 1-2:** Developer provides the dump details manually (dump ID, error\r\n message, screenshot, stack trace) rather than via `sap_short_dump`. The\r\n orchestrator works with whatever the developer provides.\r\n- **Stages 3-7:** Execute on the DEV system as normal.\r\n\r\nDocument the cross-system context in the final report (Stage 7).\r\n\r\n## Delegation Strategy\r\n\r\nThe orchestrator delegates — it does NOT reimplement existing tool logic.\r\n\r\n| Pipeline Stage | Delegates To | What Orchestrator Adds |\r\n|---|---|---|\r\n| Stage 1+2 (IDENTIFY+UNDERSTAND) | `/abap-dump` Steps 1–5 | Pipeline state, where-used impact, advance-vs-write decision |\r\n| Stage 3 (FIX) | Forge Rules 7, 7a, 10 safety pattern | Lock pre-check, diff preview, per-method writes via `sap_update_method` |\r\n| Stage 4 (CLEAN CORE) | `sap_api_state` tool | Optional flag, pattern extraction |\r\n| Stage 5 (VERIFY) | `sap_atc_run`, `sap_run_unit_test` | Go/no-go decision |\r\n| Stage 6 (DELIVER) | `sap_transport`, `sap_sql_query` | Conflict check, transport isolation (Rule 9) |\r\n| Stage 7 (REPORT) | Generate formatted report | Formal deliverable |\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Stage 1+2 — IDENTIFY + UNDERSTAND (delegates to `/abap-dump`)\r\n\r\n**Purpose:** Find the dump, read the crashing code in full context, and produce\r\na proposed fix — without writing yet.\r\n\r\n**Delegation contract:** This stage runs `/abap-dump` Steps 1–5\r\nunmodified. The orchestrator does NOT reimplement diagnosis logic. When\r\n`abap-dump` improves a heuristic, this pipeline benefits automatically.\r\n\r\n`abap-dump` produces:\r\n- **diagnostic extract** — error name, system + client, program/method/line,\r\n source extract with `>>>>>` marker, call stack (top 15), system fields\r\n- **root-cause diagnosis** with confidence rating\r\n- **proposed fix** with diff and explanation\r\n\r\n**The orchestrator adds on top:**\r\n- Pipeline state tracking (which dump, which transport, which snapshot path)\r\n- Impact analysis via `sap_usage_references` — callers downstream of the crash\r\n (raw reference list; carried into the Stage 7 report)\r\n- The decision to advance to Stage 3 (vs. write immediately, which is what\r\n `abap-dump` does on its own)\r\n\r\n**Manual check:** If the dump suggests orphaned locks, advise the developer to\r\ncheck SM12 on the affected system. This is outside ADT scope.\r\n\r\n**SAP standard code escape:** `/abap-dump` already declines to fix\r\ndumps in SAP standard code (non-Z/Y namespace) and reports relevant SAP Note\r\nkeywords. The orchestrator captures those Note numbers in Stage 7 and skips\r\nStages 3–6.\r\n\r\n**Cross-system context:** If the dump is in PRD and MCP connects to DEV, the\r\ndeveloper pastes the dump details directly. `/abap-dump` accepts\r\nmanual input the same way; the orchestrator does not branch.\r\n\r\n**Developer decision:** \"Approve the fix? [y / modify / skip]\" — same gate\r\n`/abap-dump` provides natively. Here the answer drives the pipeline\r\nto Stage 3 instead of writing immediately.\r\n\r\n### Stage 3 — FIX\r\n\r\n**Purpose:** Apply the fix with full safety controls.\r\n\r\n**Sequence (exact order — no deviations):**\r\n\r\n1. **Lock pre-check:** Call `sap_lock` to check if the object is locked by\r\n another user. If locked: report the lock holder, skip to Stage 7 with\r\n status \"blocked -- locked by {user}\". Do NOT attempt `sap_set_source`\r\n (it would burn 3 retries and fail with a raw HTTP 423).\r\n\r\n2. **Snapshot:** `sap_snapshot` (operation: take). If snapshot fails: stop\r\n immediately. No write proceeds without a successful snapshot. No exceptions.\r\n\r\n3. **Preview diff:** Use `sap_diff` to show old source vs proposed new source\r\n before writing. The developer sees exactly what will change. Wait for\r\n explicit approval.\r\n\r\n4. **Write:** Per **Forge Rule 7a**, prefer `sap_update_method` for any\r\n change confined to existing method bodies. Use `sap_set_source` only when\r\n the class definition itself must change (new method, removed method,\r\n signature change) or for non-class objects (programs, FMs).\r\n\r\n - `sap_update_method` — class methods in main source, body-only edits\r\n - `sap_set_source` — full programs, full class rewrites, definition changes\r\n\r\n **Limitation:** `sap_update_method` only works on methods in the main class\r\n source. Local handler class methods (locals_def/locals_imp) need\r\n `sap_set_source` with the full class source. See CLAUDE.md known issues.\r\n\r\n5. **Activation verify:** Call `sap_activate` explicitly (do not trust\r\n auto-activation from `sap_set_source`). Then call `sap_inactive_objects` to\r\n confirm the object is NOT on the inactive list. This is the only reliable\r\n activation check.\r\n\r\n6. **If anything fails at steps 4-5:** Call `sap_snapshot` (operation: restore)\r\n with the snapshot from step 2. Report the failure. Stop the pipeline.\r\n\r\n**Output:**\r\n- Fix applied, activation verified\r\n- Diff: what changed (from step 3 preview)\r\n- Snapshot file path (for rollback reference in the report)\r\n\r\n**Multi-object fixes:** If the fix requires changes to multiple objects, process\r\neach object through the full Stage 3 sequence (lock, snapshot, diff, write,\r\nactivate, verify) on the same transport.\r\n\r\n### Stage 4 — CLEAN CORE CHECK (optional)\r\n\r\n**Trigger:** Only when `--clean-core` flag is provided. Skipped by default.\r\n\r\n**Purpose:** Verify every API used in the fixed code is released for ABAP Cloud.\r\n\r\n**Step 1 — Extract API references from source via pattern matching:**\r\n\r\n| Source Pattern | ARS Object Type |\r\n|---|---|\r\n| `CALL FUNCTION '{name}'` | FUNC |\r\n| `NEW {class}(` or `{class}=>{method}(` | CLAS |\r\n| `SELECT ... FROM {table}` | TABL |\r\n| `TYPE REF TO {interface}` | INTF |\r\n| CDS view references | DDLS |\r\n\r\nPresent the extracted list to the developer for review before querying.\r\n\r\n**Step 2 — Discovery (first run only):**\r\n\r\nRun a discovery query to learn what release state values exist on THIS system:\r\n```sql\r\nSELECT DISTINCT RELEASE_STATE FROM ARS_W_API_STATE UP TO 20 ROWS\r\n```\r\n\r\nDo NOT hardcode release state values. The enum changes between S/4HANA releases.\r\nAlways check what the connected system returns.\r\n\r\n**Step 3 — Check each API:**\r\n\r\nCall `sap_api_state` with the extracted object names and types, or fall back to\r\n`sap_sql_query` on ARS_W_API_STATE:\r\n```sql\r\nSELECT OBJECT_NAME, OBJECT_TYPE, RELEASE_STATE\r\n FROM ARS_W_API_STATE\r\n WHERE OBJECT_NAME = '{NAME}'\r\n AND OBJECT_TYPE = '{TYPE}'\r\n```\r\n\r\n**Fallback:** If ARS_W_API_STATE does not exist on the system (ECC, older S/4),\r\nthe query will fail. In that case report: \"Clean core check not available --\r\nARS_W_API_STATE not found on this system. Upgrade to S/4HANA 2020+ for released\r\nAPI verification.\" Do not crash. Continue the pipeline.\r\n\r\n**Output:**\r\n```\r\nClean Core Check:\r\n FM BAPI_SALESORDER_GETLIST -> RELEASED\r\n FM CONVERSION_EXIT_ALPHA_INPUT -> RELEASED\r\n TABLE VBAK -> NOT_TO_BE_RELEASED\r\n -> Alternative: CDS view I_SalesOrder (RELEASED)\r\n CLASS CL_GUI_ALV_GRID -> NOT_TO_BE_RELEASED\r\n -> No released alternative (UI technology change needed)\r\n\r\nResult: 2/4 APIs clean core compliant. 2 need attention.\r\n```\r\n\r\n**Developer decision:** Fix the unreleased API usage now, or note as technical\r\ndebt and proceed?\r\n\r\n### Stage 5 — VERIFY\r\n\r\n**Purpose:** Quality gate before transport release.\r\n\r\n**Tools:**\r\n- `sap_atc_run` (variant: DEFAULT) — standard quality checks\r\n- `sap_atc_run` (variant: cloud readiness variant) — only if `--clean-core`.\r\n The variant name is system-dependent (could be `ABAP_CLOUD_READINESS`,\r\n `SAP_CLOUD_READINESS`, or a customer variant). Ask the developer or query\r\n available variants if unknown.\r\n- `sap_run_unit_test` — only if tests already exist for this object\r\n\r\n**Rules:**\r\n- Priority 1 ATC findings = blocker. Must fix before proceeding.\r\n- Priority 2 = warning. Report but do not block.\r\n- Unit tests: if they exist, run them. If they do not exist, move on. Do not\r\n block for missing tests.\r\n\r\n**Output:**\r\n- ATC results (P1/P2/P3 count)\r\n- Unit test results (pass/fail/not found)\r\n- Go/no-go for transport\r\n\r\n### Stage 5b — RETEST (developer action)\r\n\r\n**Purpose:** Confirm the fix actually resolves the original issue.\r\n\r\n**This is a manual step.** Prompt the developer:\r\n\r\n\"Please re-execute the transaction/job that caused the dump and confirm the\r\nerror no longer occurs.\"\r\n\r\nWait for developer confirmation before proceeding to Stage 6.\r\n\r\nIf the developer reports it still dumps: loop back to Stage 1 with the new dump\r\ndetails. Do not proceed to Stage 6.\r\n\r\n### Stage 6 — DELIVER\r\n\r\n**Purpose:** Confirm transport readiness.\r\n\r\n**Note:** `sap_set_source` in Stage 3 already performs a transport pre-check\r\nvia E071 internally (WriteSourceTool.findExistingTransport). Stage 6 adds the\r\nconflict check and summary — it does not duplicate the pre-check.\r\n\r\n**Tools:**\r\n- `sap_sql_query` on E071/E070 — conflict check (same object in other open\r\n transports)\r\n- `sap_transport` (operation: list) — verify transport is modifiable\r\n\r\n**If no transport assigned:** Ask the developer for a transport number or create\r\none via `sap_transport` (operation: create, with package).\r\n\r\n**Output:**\r\n- Transport number, description, owner\r\n- Objects in transport\r\n- Conflict check result (any other transports with the same object)\r\n\r\n**Developer decision:** \"Release transport?\" CSPeach does NOT release\r\nautomatically. That is the developer's call in STMS/SE09.\r\n\r\n### Stage 7 — REPORT\r\n\r\n**Purpose:** Produce a formal incident resolution document.\r\n\r\n**Template:**\r\n\r\n```\r\n## Incident Resolution Report\r\n\r\n### System\r\nSID: {SID} | Client: {CLIENT} | Release: {SAP_RELEASE}\r\n\r\n### Incident\r\nError: {RUNTIME_ERROR}\r\nProgram: {PROGRAM} | Method: {METHOD} | Line: {LINE}\r\nUser: {USER} | Date: {DATE}\r\nTransaction: {TCODE}\r\n\r\n### Root Cause\r\n{1-3 sentence explanation}\r\n\r\n### Fix Applied\r\n{Diff showing old vs new code}\r\n\r\n### Rollback\r\nSnapshot: {SNAPSHOT_FILE_PATH}\r\nRestore command: sap_snapshot (operation: restore, file: {path})\r\n\r\n### Clean Core Status\r\n{If --clean-core was used: compliance table}\r\n{If not: \"Clean core check not requested\"}\r\n\r\n### Verification\r\nATC (DEFAULT): {P1}/{P2}/{P3}\r\nATC (Cloud): {if applicable}\r\nUnit Tests: {pass/fail/not found}\r\nRetest: {developer confirmed / not performed}\r\n\r\n### Transport\r\nNumber: {TRANSPORT}\r\nDescription: {DESC}\r\nStatus: Ready for release\r\n\r\n### Related SAP Notes\r\n{If standard code was involved: relevant SAP Note numbers}\r\n{If not applicable: \"N/A -- custom code only\"}\r\n\r\n### Timeline\r\nIdentified: {timestamp}\r\nFixed: {timestamp}\r\nVerified: {timestamp}\r\nTotal: {minutes} minutes\r\n```\r\n\r\nThis report is the deliverable. For consulting engagements, it is the proof of\r\nwork. For internal support, it is the ticket documentation.\r\n\r\n## Edge Cases\r\n\r\n| Edge Case | Handling |\r\n|-----------|---------|\r\n| No dump found for the error | Ask developer to reproduce, or search by program name |\r\n| Dump is in SAP standard code (not Z/Y) | Do NOT fix. Search for SAP Notes with error keywords. Include Note number in report. |\r\n| Dump in PRD, MCP connected to DEV | Developer provides dump details manually. Fix in DEV. |\r\n| Object locked by another user | Pre-check via `sap_lock`. Report lock holder. Skip to Stage 7 with \"blocked\" status. |\r\n| Fix introduces new ATC P1 findings | Block at Stage 5. Developer must resolve before transport. |\r\n| No transport assigned | Ask developer or create one |\r\n| Clean core check finds unreleased API | Report it. Developer decides: fix now or accept debt. |\r\n| ARS_W_API_STATE not on system | Report \"not available -- S/4HANA 2020+ required\". Continue pipeline. |\r\n| Object has no unit tests | Run ATC only. Do not block. |\r\n| Multiple dumps for same root cause | Identify the pattern, fix the root cause once |\r\n| Fix requires multiple objects changed | Process each object through Stages 3-5, same transport |\r\n| Fix is in local class (locals_def/locals_imp) | Use `sap_set_source` with full class. `sap_update_method` only works on main source. |\r\n| Orphaned locks after dump (SM12) | Advise developer to check SM12 manually. Outside ADT scope. |\r\n| Cloud readiness ATC variant name unknown | Ask developer or query available variants on the system. |\r\n\r\n## Guardrails\r\n\r\n### Write Safety\r\n- NEVER call `sap_set_source` or `sap_update_method` without a prior successful\r\n `sap_snapshot` for that object (Forge Rule 7). Snapshot failure = hard stop.\r\n- NEVER call `sap_set_source` before `sap_syntax_check` passes. `sap_set_source`\r\n auto-activates — broken code becomes live for all users immediately.\r\n- NEVER attempt to write if `sap_lock` shows the object is locked by another\r\n user. Report the lock holder and skip to Stage 7.\r\n\r\n### Activation Safety\r\n- ALWAYS call `sap_activate` explicitly after `sap_set_source`. The\r\n auto-activation in `sap_set_source` is unreliable — it reports `activated:true`\r\n while the object remains inactive.\r\n- ALWAYS call `sap_inactive_objects` after `sap_activate` to confirm the object\r\n is no longer on the inactive list. This is the only reliable check.\r\n- If the object is still inactive after explicit activation: treat as activation\r\n failure. Show the errors, restore from snapshot, report failure.\r\n\r\n### Developer Control\r\n- NEVER auto-fix without developer approval. The developer approves at every\r\n stage transition and before every write.\r\n- NEVER release a transport automatically. Transport release is the developer's\r\n explicit decision.\r\n- NEVER skip Stage 5b (RETEST). Always ask the developer to confirm the fix\r\n resolves the original issue, even if ATC and unit tests pass.\r\n\r\n### Clean Core Safety\r\n- NEVER hardcode release state values. Always discover what values exist on the\r\n connected system via the discovery query.\r\n- If `sap_api_state` or ARS_W_API_STATE is not available, report it and continue.\r\n Do not crash the pipeline.\r\n\r\n### Error Handling\r\n- If any stage fails, stop the pipeline. Report what succeeded, what failed,\r\n and the current system state. Ask the developer how to proceed.\r\n- If a write fails, restore from snapshot immediately. Do not leave the object\r\n in an inconsistent state.\r\n- If activation fails, restore from snapshot. Do not proceed to verification\r\n with an inactive object.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-incident\r\n\r\nDump ID: 20260408_143022_DEVK900001\r\n```\r\n\r\nOr:\r\n\r\n```\r\n/abap-incident --clean-core\r\n\r\nProgram ZSD_ORDER_PROCESS crashed with RAISE_EXCEPTION in production.\r\nError: CX_SY_OPEN_SQL_DB. The SELECT on VBAK returns too many rows\r\nwhen customer ID is initial.\r\n```\r\n\r\nOr:\r\n\r\n```\r\n/abap-incident\r\n\r\nShow me recent dumps for user JSMITH\r\n```\r\n\r\n## Example Output\r\n\r\n```\r\n--- Stage 1: IDENTIFY ---\r\n\r\nsap_short_dump (list, user: JSMITH) -> 3 dumps found\r\nsap_short_dump (detail, id: 20260408_143022) -> diagnostics extracted\r\n\r\nError: RAISE_EXCEPTION (CX_SY_OPEN_SQL_DB)\r\nProgram: ZSD_ORDER_PROCESS | Method: GET_ORDERS | Line: 47\r\nUser: JSMITH | Date: 2026-04-08 | Transaction: VA01\r\nCall stack: ZSD_ORDER_PROCESS=>GET_ORDERS (line 47) -> ...\r\n\r\nRoot cause: SELECT on VBAK without WHERE clause on KUNNR when\r\niv_customer is initial. Returns all rows, exceeds memory limit.\r\n\r\nIs this the right dump? Proceed to understand? [y/n]\r\n> y\r\n\r\n--- Stage 2: UNDERSTAND ---\r\n\r\nsap_get_source (ZSD_ORDER_PROCESS) -> source loaded (312 lines)\r\nsap_usage_references (ZSD_ORDER_PROCESS) -> 4 callers found\r\n\r\nBusiness purpose: Reads sales orders for a given customer.\r\nCrash condition: iv_customer parameter is INITIAL, causing unfiltered\r\nSELECT on VBAK.\r\n\r\nProposed fix: Add guard clause at method entry — raise CX_PARAMETER_ERROR\r\nif iv_customer is initial.\r\n\r\nWhere-used: ZSD_ORDER_REPORT, ZSD_BATCH_EXPORT, ZSD_API_WRAPPER,\r\nZSD_FIORI_SERVICE (all pass customer ID from user input).\r\n\r\nApprove the fix? [y / modify / skip]\r\n> y\r\n\r\n--- Stage 3: FIX ---\r\n\r\nsap_lock (ZSD_ORDER_PROCESS) -> not locked\r\nsap_snapshot (take, ZSD_ORDER_PROCESS) -> snapshot saved\r\nsap_diff -> showing proposed changes:\r\n\r\n Line 45: METHOD get_orders.\r\n+ Line 46: IF iv_customer IS INITIAL.\r\n+ Line 47: RAISE EXCEPTION TYPE cx_parameter_error.\r\n+ Line 48: ENDIF.\r\n Line 49: SELECT vbeln, erdat, kunnr FROM vbak ...\r\n\r\nProceed with write? [y/n]\r\n> y\r\n\r\nsap_syntax_check -> PASSED (0 errors)\r\nsap_set_source (transport: DEVK912400) -> 200\r\nsap_activate -> success\r\nsap_inactive_objects -> ZSD_ORDER_PROCESS NOT on inactive list\r\n\r\nFix applied and activation verified.\r\n\r\n--- Stage 5: VERIFY ---\r\n\r\nsap_atc_run (DEFAULT, ZSD_ORDER_PROCESS) -> P1: 0, P2: 1, P3: 3\r\n P2: Variable lv_temp declared but not used (line 201)\r\nsap_run_unit_test (ZSD_ORDER_PROCESS) -> not found (no test class)\r\n\r\nGo/no-go: GO (no P1 blockers)\r\n\r\n--- Stage 5b: RETEST ---\r\n\r\nPlease re-execute the transaction/job that caused the dump and confirm\r\nthe error no longer occurs.\r\n> Confirmed, no more dump.\r\n\r\n--- Stage 6: DELIVER ---\r\n\r\nsap_sql_query (E071, object ZSD_ORDER_PROCESS) -> 1 transport found\r\nTransport: DEVK912400 | Description: Fix null customer crash\r\nsap_sql_query (E070, conflict check) -> no conflicts\r\n\r\nRelease transport? (developer decision in STMS/SE09)\r\n\r\n--- Stage 7: REPORT ---\r\n\r\n## Incident Resolution Report\r\n\r\n### System\r\nSID: DEV | Client: 100 | Release: S/4HANA 2023\r\n\r\n### Incident\r\nError: RAISE_EXCEPTION (CX_SY_OPEN_SQL_DB)\r\nProgram: ZSD_ORDER_PROCESS | Method: GET_ORDERS | Line: 47\r\nUser: JSMITH | Date: 2026-04-08\r\nTransaction: VA01\r\n\r\n### Root Cause\r\nSELECT on VBAK without WHERE clause on KUNNR when iv_customer is\r\ninitial. Returns all rows, exceeds memory limit.\r\n\r\n### Fix Applied\r\nAdded guard clause: raise CX_PARAMETER_ERROR if iv_customer is initial.\r\n[diff shown above]\r\n\r\n### Rollback\r\nSnapshot: .cspeach/snapshots/ZSD_ORDER_PROCESS_20260408_143500.abap\r\nRestore command: sap_snapshot (operation: restore, file: above path)\r\n\r\n### Clean Core Status\r\nClean core check not requested\r\n\r\n### Verification\r\nATC (DEFAULT): P1: 0 / P2: 1 / P3: 3\r\nUnit Tests: not found\r\nRetest: developer confirmed\r\n\r\n### Transport\r\nNumber: DEVK912400\r\nDescription: Fix null customer crash\r\nStatus: Ready for release\r\n\r\n### Related SAP Notes\r\nN/A -- custom code only\r\n\r\n### Timeline\r\nIdentified: 14:35\r\nFixed: 14:38\r\nVerified: 14:41\r\nTotal: 6 minutes\r\n```\r\n\r\n## What This Is NOT\r\n\r\n- NOT a monitoring tool (does not watch for dumps in real-time)\r\n- NOT a ticketing system (does not create/close tickets)\r\n- NOT an approval workflow (developer makes all decisions)\r\n- NOT automatic (developer approves every stage)\r\n- NOT cross-system (MCP connects to one system at a time)\r\n\r\nThe developer drives. CSPeach does the heavy lifting.\r\n",
142
+ "sha256": "307a43c71851e8b69f72931cd54fec850ff71405b2a52b9bd2f6f755282e650f",
136
143
  "signature": "",
137
- "signedAt": "2026-09-17T20:19:05.116Z"
144
+ "signedAt": "2026-09-28T14:37:04.721Z"
138
145
  },
139
146
  "abap-migrate": {
140
147
  "name": "abap-migrate",
141
148
  "body": "---\r\nname: abap-migrate\r\ndescription: >\r\n ECC to S/4HANA migration assessment for custom ABAP objects — API analysis,\r\n successor lookup, per-object effort estimation, and classification into five\r\n migration strategies: retire, replace with standard, refactor, wrap, or defer.\r\n Produces a migration sequence, effort summary, and risk register.\r\nphase: SUPPORT\r\nrequires_mcp: optional\r\nforge_rules: [3, 6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-migrate\r\n\r\n## Purpose\r\n\r\nAssess a set of custom ABAP objects for ECC-to-S/4HANA migration readiness.\r\nFor each object: classify the migration strategy, identify API successors,\r\nestimate effort, and flag risks. Produce a migration sequence that respects\r\ndependencies and a risk register for project planning.\r\n\r\nThis skill is read-only and produces assessments and recommendations. It does\r\nnot execute migration steps. Use `/abap-cloud-check` for detailed API-level\r\nanalysis; use `/abap-refactor` to apply code changes.\r\n\r\nForge Rule 3: No cloud readiness claim without evidence. Every assessment in\r\nthis output is labeled with its confidence level.\r\n\r\n## When to Use\r\n\r\n- Planning an ECC to S/4HANA migration project\r\n- Sizing the custom code remediation effort\r\n- Identifying quick wins (objects to retire or that need no change)\r\n- Prioritizing technical debt before the migration project starts\r\n- When SAP Readiness Check provides a list of objects to address\r\n\r\nWhen NOT to use: discovering and classifying the whole estate — `/abap-cca`; fixing ATC findings object-by-object — `/abap-upgrade-fix`; rebuilding one object as RAP/Fiori — `/abap-modernize`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- List of custom objects to assess (with names and types), OR\r\n- Package name(s) to assess (MCP will retrieve object list), OR\r\n- Source code of objects to analyze\r\n\r\nUseful (will be asked):\r\n- Target S/4HANA version (2020 / 2021 / 2022 / 2023 / latest)\r\n- Target deployment model: on-prem / private cloud / public cloud (BTP)\r\n- Whether clean core enforcement is required (public cloud: yes, mandatory)\r\n- Known business criticality of the objects (high / medium / low)\r\n\r\n## Migration Strategies\r\n\r\n| Strategy | Definition | When to Apply |\r\n|----------|-----------|---------------|\r\n| **Retire** | Decommission the object — no replacement needed | Object is dead code, superseded by standard, or no longer used |\r\n| **Replace with Standard** | Standard SAP functionality now covers the use case | SAP S/4HANA provides an equivalent — verify feature parity |\r\n| **Refactor** | Modify the custom object to use released APIs and remove blocked statements | Core logic is still needed, but APIs must change |\r\n| **Wrap** | Encapsulate blocked standard API in a thin wrapper using a released API | Minimal change needed; wrapper hides the unreleased call |\r\n| **Defer** | Keep as-is for now; acceptable in on-prem S/4HANA if no ATC blockers | Used on-prem with no cloud plans; low risk; budget constraints |\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Build Object Inventory\r\n\r\nList all objects to be assessed. For each:\r\n- Name, type (class / FM / report / enhancement / table / workflow)\r\n- Package and development area\r\n- Estimated size (lines of code if available)\r\n- Known business function\r\n\r\nIf MCP is connected: retrieve objects from package using sap_search_object.\r\n\r\n### Step 2 — Assess Each Object\r\n\r\nFor each object, analyze:\r\n\r\n1. **API Usage:** What SAP APIs does it use? Are they released in S/4HANA?\r\n2. **Table Access:** Does it access SAP standard tables directly? Are there VDM successors?\r\n3. **Blocked Statements:** CALL TRANSACTION, SUBMIT, MESSAGE E, etc.\r\n4. **Business Relevance:** Is this functionality available in standard S/4HANA?\r\n5. **Usage:** Is this object actually called? (if MCP: use sap_usage_references)\r\n\r\n### Step 3 — Classify Migration Strategy\r\n\r\nAssign one of the five strategies with justification.\r\n\r\n### Step 4 — Estimate Effort\r\n\r\nPer object effort estimate:\r\n- XS: < 1 day (config change, parameter adjustment)\r\n- S: 1-3 days (minor refactor, wrapper class)\r\n- M: 3-7 days (significant refactor, new API integration)\r\n- L: 7-20 days (major redesign, RAP migration)\r\n- XL: > 20 days (complete rewrite or replace with standard implementation)\r\n\r\n### Step 5 — Build Migration Sequence\r\n\r\nOrder objects by migration priority:\r\n1. Retire first (free up scope)\r\n2. Simple replacements and wrappers\r\n3. Medium refactors\r\n4. Complex redesigns\r\n5. Defer items (out of scope)\r\n\r\nRespect dependencies: if Object B depends on Object A, migrate A first.\r\n\r\n### Step 6 — Risk Register\r\n\r\nFor each object classified as Refactor/Replace/Wrap-complex: produce a risk entry.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Migration Assessment — {scope}\r\n\r\n**Target:** S/4HANA {version} — {deployment_model}\r\n**Clean Core Required:** {Yes | No}\r\n**Objects Assessed:** {count}\r\n**Assessment Date:** {date}\r\n\r\n---\r\n\r\n## Summary Statistics\r\n\r\n| Strategy | Count | % of Total | Total Effort |\r\n|----------|-------|-----------|--------------|\r\n| Retire | {n} | {%} | — |\r\n| Replace with Standard | {n} | {%} | {effort} |\r\n| Refactor | {n} | {%} | {effort} |\r\n| Wrap | {n} | {%} | {effort} |\r\n| Defer | {n} | {%} | {effort} |\r\n| **Total** | {n} | 100% | {total_effort} |\r\n\r\n---\r\n\r\n## Object Assessment Table\r\n\r\n| Object | Type | Strategy | Reason | Effort | Confidence | Successor / Action |\r\n|--------|------|----------|--------|--------|------------|--------------------|\r\n| {name} | {type} | {strategy} | {reason} | {T-shirt} | High/Med/Low | {action} |\r\n\r\n---\r\n\r\n## Detailed Findings\r\n\r\n### {Object_Name} — {Strategy}\r\n\r\n**Business Function:** {function}\r\n**Current Issues:**\r\n- {issue_1} (API: {api_name} — Not Released)\r\n- {issue_2}\r\n\r\n**Recommended Action:** {description}\r\n**Effort:** {estimate}\r\n**Successor APIs / Objects:** {list}\r\n**Confidence:** {High | Medium | Low} — {reason for confidence level}\r\n\r\n---\r\n\r\n## Migration Sequence\r\n\r\n**Phase 1 — Quick Wins (Retire + Simple Replace):**\r\n1. {object} — {action} (effort: {t-shirt})\r\n2. {object} — {action}\r\n\r\n**Phase 2 — Refactor and Wrap:**\r\n3. {object} — {action}\r\n4. {object} — {action}\r\n\r\n**Phase 3 — Complex Redesign:**\r\n5. {object} — {action}\r\n\r\n**Phase 4 — Defer (on-prem only):**\r\n6. {object} — monitor for next phase\r\n\r\n---\r\n\r\n## Effort Summary\r\n\r\n| Phase | Objects | Total Effort | Dev Days (estimate) |\r\n|-------|---------|--------------|---------------------|\r\n| Phase 1 | {n} | {effort} | {days} |\r\n| Phase 2 | {n} | {effort} | {days} |\r\n| Phase 3 | {n} | {effort} | {days} |\r\n| **Total** | {n} | | {days} |\r\n\r\n---\r\n\r\n## Risk Register\r\n\r\n| Risk | Objects Affected | Likelihood | Impact | Mitigation |\r\n|------|-----------------|------------|--------|------------|\r\n| {risk} | {objects} | High/Med/Low | High/Med/Low | {mitigation} |\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER claim an API is released without evidence — state confidence level\r\n- NEVER recommend \"Replace with Standard\" without identifying the specific\r\n standard function; if unknown, recommend investigation first\r\n- NEVER estimate effort with false precision — use T-shirt sizes\r\n- If usage/call frequency cannot be determined: note it under Open Questions\r\n- For \"Retire\" classification: note that business sign-off is required before\r\n actual decommissioning — technical assessment is not sufficient alone\r\n- Forge Rule 3: every cloud readiness statement must include confidence level\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-migrate\r\n\r\nAssess these ECC custom objects for migration to S/4HANA 2023 on-prem.\r\nClean core is a target but not hard-enforced. Business criticality noted.\r\n\r\n1. ZFI_POSTING_REPORT (report) — posts FI documents, uses BKPF/BSEG direct access\r\n and FM 'FI_DOCUMENT_CHANGE', HIGH criticality\r\n2. ZMM_VENDOR_SYNC (class) — syncs vendor data with external system, uses LFA1\r\n and RFC FM 'Z_EXT_VENDOR_CREATE', MEDIUM criticality\r\n3. ZSD_PRICE_REPORT (report) — price list report, SELECT from KONP/KONH,\r\n read-only, LOW criticality\r\n4. ZCL_OLD_UTIL (class) — utility functions for string formatting, pure Z logic,\r\n no SAP API calls, MEDIUM criticality\r\n5. ZECC_ARCHIVE_PROG (report) — data archiving from old ECC custom tables.\r\n Tables no longer exist in S/4HANA. LOW criticality.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Migration Assessment — Custom Code Package\r\n\r\n**Target:** S/4HANA 2023 on-premise\r\n**Clean Core Required:** Target (not hard-enforced)\r\n**Objects Assessed:** 5\r\n**Assessment Date:** 2026-03-29\r\n\r\n---\r\n\r\n## Summary Statistics\r\n\r\n| Strategy | Count | % of Total | Total Effort |\r\n|-----------------------|-------|-----------|--------------|\r\n| Retire | 1 | 20% | — |\r\n| Replace with Standard | 0 | 0% | — |\r\n| Refactor | 2 | 40% | L + M |\r\n| Wrap | 1 | 20% | S |\r\n| Defer | 1 | 20% | — |\r\n| **Total** | 5 | 100% | ~20-30 dev days |\r\n\r\n---\r\n\r\n## Object Assessment Table\r\n\r\n| Object | Type | Strategy | Reason | Effort | Confidence | Action |\r\n|----------------------|---------|----------|---------------------------------------------|--------|------------|--------------------------------------|\r\n| ZFI_POSTING_REPORT | Report | Refactor | FI_DOCUMENT_CHANGE not released; BKPF/BSEG has VDM | L | High | Replace with BAPI_ACC_DOCUMENT_POST + I_JournalEntry |\r\n| ZMM_VENDOR_SYNC | Class | Wrap | RFC to Z_EXT_VENDOR_CREATE can stay; LFA1 access needs VDM | S | Medium | Wrap LFA1 reads in I_Supplier CDS |\r\n| ZSD_PRICE_REPORT | Report | Defer | KONP/KONH access not clean core but no blocker in on-prem | XS | High | Monitor for cloud migration phase |\r\n| ZCL_OLD_UTIL | Class | Defer | Pure Z logic, no SAP APIs — no migration work needed | XS | High | No action; already clean |\r\n| ZECC_ARCHIVE_PROG | Report | Retire | Archives tables that no longer exist in S/4 | — | High | Decommission after business sign-off |\r\n\r\n---\r\n\r\n## Detailed Findings\r\n\r\n### ZFI_POSTING_REPORT — Refactor\r\n\r\n**Business Function:** Posts FI journal entries, including period-end adjustments.\r\nHIGH business criticality.\r\n\r\n**Current Issues:**\r\n- Direct SELECT from BKPF/BSEG — should use I_JournalEntry / I_JournalEntryItem\r\n (released CDS VDM). Confidence: High — these views are C1 released.\r\n- FM FI_DOCUMENT_CHANGE — release status not confirmed offline. Verify in system.\r\n If not released: replace with BAPI_ACC_DOCUMENT_POST. Confidence: Medium.\r\n\r\n**Recommended Action:** Replace BKPF/BSEG reads with I_JournalEntry SELECT.\r\nReplace FI_DOCUMENT_CHANGE with BAPI_ACC_DOCUMENT_POST after verifying the\r\nBAPI covers the required document change types.\r\n\r\n**Effort:** L (7-15 days including testing)\r\n**Successor APIs:** I_JournalEntry, I_JournalEntryItem, BAPI_ACC_DOCUMENT_POST\r\n\r\n---\r\n\r\n### ZECC_ARCHIVE_PROG — Retire\r\n\r\n**Business Function:** Archives records from ZECC_ORDERS and ZECC_ARCHIVE_HEAD.\r\nThese were custom ECC tables that were removed when the business process was\r\nreplaced by standard S/4HANA order management.\r\n\r\n**Recommended Action:** Decommission. Confirm with business that no historical\r\ndata retrieval is needed. If archiving is still required for audit: check if\r\nthe data was migrated to a standard S/4HANA table and adjust accordingly.\r\n\r\n**Effort:** Zero (development). Business sign-off required.\r\n\r\n---\r\n\r\n## Migration Sequence\r\n\r\n**Phase 1 — Quick Wins:**\r\n1. ZECC_ARCHIVE_PROG — Retire (pending business sign-off)\r\n2. ZCL_OLD_UTIL — No action needed\r\n\r\n**Phase 2 — Wrap and Minor Refactor:**\r\n3. ZMM_VENDOR_SYNC — Wrap LFA1 reads with I_Supplier CDS view (S effort)\r\n\r\n**Phase 3 — Major Refactor:**\r\n4. ZFI_POSTING_REPORT — Full API replacement (L effort, HIGH risk)\r\n\r\n**Phase 4 — Defer:**\r\n5. ZSD_PRICE_REPORT — Monitor; address in cloud migration phase if needed\r\n\r\n---\r\n\r\n## Effort Summary\r\n\r\n| Phase | Objects | Effort | Dev Days (estimate) |\r\n|-------|---------|---------|---------------------|\r\n| Phase 1 | 2 | — | 0-2 (sign-off only) |\r\n| Phase 2 | 1 | S | 1-3 |\r\n| Phase 3 | 1 | L | 7-15 |\r\n| Phase 4 | 1 | Deferred| 0 |\r\n| **Total** | 5 | | **8-20 dev days** |\r\n\r\n---\r\n\r\n## Risk Register\r\n\r\n| Risk | Objects | Likelihood | Impact | Mitigation |\r\n|----------------------------------------------|---------------------|------------|--------|-----------------------------------------|\r\n| FI_DOCUMENT_CHANGE may be released — refactor may be larger than needed | ZFI_POSTING_REPORT | Medium | Medium | Verify in system before starting refactor |\r\n| FI refactor changes posting behavior subtly | ZFI_POSTING_REPORT | Medium | High | Extensive parallel testing in QA required |\r\n| Business refuses to retire ZECC_ARCHIVE_PROG | ZECC_ARCHIVE_PROG | Low | Low | Minimal effort to keep; document as known debt |\r\n```\r\n",
142
149
  "sha256": "89448a0d820f587298864f946416935b101777f7fc3ebef6f2ad9dcc398c1265",
143
150
  "signature": "",
144
- "signedAt": "2026-09-17T20:19:05.161Z"
151
+ "signedAt": "2026-09-28T14:37:04.750Z"
145
152
  },
146
153
  "abap-modernize": {
147
154
  "name": "abap-modernize",
148
- "body": "---\nname: abap-modernize\ndescription: >\n Transform classic ABAP programs to the best possible modern architecture\n for the customer's SAP release level. Reads the source, detects the system\n capabilities, proposes the optimal modernization path (RAP, SEGW, or OO),\n and builds the full stack on the system in one conversation.\nphase: BUILD\nrequires_mcp: required\nforge_rules: [6, 7, 8, 9, 10]\nversion: \"1.1\"\n---\n\n# abap-modernize\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n\n## Purpose\n\nTransform classic ABAP programs (WRITE reports, dynpro transactions, function\nmodules) into the best modern architecture the customer's SAP release supports.\nRun the full journey in one conversation: read legacy source, detect system\ncapabilities, propose the optimal target architecture, get approval, build every\nobject, verify, and document the result.\n\n## What This Skill Is NOT\n\n- **Not an upgrade tool.** `/abap-upgrade-fix` patches syntax compatibility for\n higher ABAP releases. This skill redesigns architecture.\n- **Not a Clean Core tool.** `/abap-cca` assesses API compliance and replacement\n readiness. This skill builds the replacement.\n- **Not a refactoring tool.** `/abap-refactor` improves code quality within the\n same architecture. This skill changes the architecture itself.\n\nDifferent buyers: \"make this Fiori\" vs \"make this cloud-ready\" vs \"clean up this\ncode.\" This skill handles \"make this Fiori.\"\n\n## When to Use\n\n- Classic WRITE report that needs to become a Fiori list report\n- Dynpro transaction (module pool) that needs to become a Fiori Elements object page\n- Function module that needs to become an OData service or RAP API\n- Legacy procedural code that needs modern OO patterns with a UI\n\nDo NOT use for:\n- SAP standard code (never modify standard objects)\n- Objects that are already modern (CDS views, RAP BDEFs, service bindings)\n- Pure code cleanup without architecture change (use `/abap-refactor`)\n- Upgrade compatibility fixes (use `/abap-upgrade-fix`)\n\n## Required Tools\n\n| Tool | Phase | Purpose |\n|---|---|---|\n| `sap_get_source` | ASSESS, DESIGN | Read program source and INCLUDEs |\n| `sap_sql_query` | ASSESS | Release detection (CVERS), type detection (TRDIR), capability probes |\n| `sap_search_object` | DESIGN | Name collision check for new objects |\n| `sap_usage_references` | ASSESS | Find callers and callees of the legacy object |\n| `sap_create_object` | BUILD | Create new objects (tables, CDS, classes, etc.) |\n| `sap_set_source` | BUILD, DOCUMENT | Write source code, add header comments |\n| `sap_update_method` | BUILD | Update individual methods in existing classes |\n| `sap_activate` | BUILD | Activate created objects |\n| `sap_syntax_check` | BUILD, VERIFY | Syntax validation after every write |\n| `sap_inactive_objects` | BUILD, VERIFY | Confirm activation succeeded |\n| `sap_atc_run` | VERIFY | ATC quality check on all new objects |\n| `sap_lock` / `sap_unlock` | BUILD | Lock management during writes |\n| `sap_api_state` | VERIFY | Clean Core compliance check |\n\n## SQL Constraints\n\nWhen using `sap_sql_query`, follow these constraints strictly:\n\n- **No aggregate functions.** Do not use SUM, COUNT(*), AVG, MIN, MAX, GROUP BY.\n These are unreliable on the ADT SQL endpoint. Use `SELECT COUNT(*)` only for\n existence probes where an approximate or error result is acceptable.\n- **No JOINs.** Run separate queries and merge results in your reasoning.\n- **Keyset pagination.** For large result sets, use WHERE clauses with key ranges\n rather than OFFSET. Example: `WHERE NAME > '{last_key}' ORDER BY NAME`.\n- **Limit result size.** Never SELECT all rows from large tables. Always filter\n by specific keys or use TOP / UP TO N ROWS.\n\n## Phase Overview\n\n```\n/abap-modernize {PROGRAM_NAME}\n -> ASSESS : Read source, detect release, analyze patterns\n -> PROPOSE : Present target architecture with rationale\n -> APPROVE : User confirms or adjusts the proposal\n -> DESIGN : Detail every object (names, fields, source skeletons)\n -> BUILD : Create and activate all objects (Rule 7, 8, 9, 10)\n -> VERIFY : Syntax check, activation check, ATC run\n -> DOCUMENT : Add header comments, produce summary\n```\n\n## CCA Integration\n\nAt startup, check for existing CCA analysis data in `.cspeach/cca/projects/`\n(for older projects, also check the legacy `.abapforge/cca/projects/`).\nIf a CCA report exists for the program or its package, incorporate it:\n\n| CCA Data | How Modernize Uses It |\n|---|---|\n| ATC findings (P1 table replacements) | Design CDS views using the replacement tables |\n| Clean Core successors (`BSEG` -> `I_JOURNALENTRY`) | Use the CDS successor entity directly instead of the legacy table |\n| Technical readiness (`NEEDS_REDESIGN`) | Confirms modernization is the correct action |\n| Usage status (`UNVERIFIED`) | Note in the proposal that usage data was not verified |\n\nField paths in the CCA JSON: `atcFindings[]`, `cleanCore.successors[]`,\n`technicalReadiness.status`, `usageStatus.status`.\n\nIf no CCA data exists, ASSESS performs the full analysis from scratch. Do not\nrequire the user to run `/abap-cca` first.\n\n### `--from @<cca-assessment>` chain (v0.7)\n\nWhen invoked as `/abap-modernize --from @<cca-assessment-file>`, the CLI\nadapter reads the assessment's `content.detailPath` (project.json at\n`.cspeach/cca/projects/<slug>/project.json`; older projects may still point at\nthe legacy `.abapforge/cca/projects/<slug>/` location — read there as a\nfallback) and pre-filters the work list to:\n\n1. Every object classified `redesign` (full architectural rebuild needed).\n2. Every object classified `fix` whose findings include Clean Core API\n violations — direct reads of `NOT_TO_BE_RELEASED` tables (`BSEG`,\n `BKPF`, `ACDOCA`, etc.), calls to deprecated BAPIs without released\n successors, or other structural patterns that modernize can rewrite.\n\nObjects classified `fix` with PURE ATC syntax findings (WRBTR\nfield-length, deprecated statements without API replacements) are\nSKIPPED with a hint suggesting `/abap-upgrade-fix` — those are\nmechanical, not architectural, and don't need modernization's heavier\nloop.\n\nProcess the filtered list one object at a time, applying the full\nASSESS → PROPOSE → APPROVE → DESIGN → SCAFFOLD → IMPLEMENT → ACTIVATE →\nHANDOFF flow per object. The cca-assessment carries the per-object\nclassifications + successor mappings; reuse them rather than\nre-running the analysis.\n\n---\n\n## ASSESS Phase\n\nStart ASSESS immediately when the user invokes `/abap-modernize {PROGRAM_NAME}`.\nDo not ask preliminary questions -- read the system first, then ask only what\nthe source code cannot answer.\n\n### 0. Already-Modernized Check\n\n**Step 1: Check if the object is already modern.**\n\nQuery the object type — if it is DDLS, BDEF, SRVD, or SRVB:\n\n```\nThis is already a modern ABAP Cloud object ({type}). Nothing to modernize.\n```\n\n**Step 2: Check local modernization registry.**\n\nCheck `.cspeach/modernize/registry.json` for a record of this program (for\nolder projects, also check the legacy `.abapforge/modernize/registry.json`).\nThe registry maps original program names to their modernized objects:\n\n```json\n{\n \"ZLEGACY_FI_REPORT\": {\n \"modernizedAt\": \"2026-04-13\",\n \"businessName\": \"FI_VENDOR_AGING\",\n \"objects\": [\n \"ZI_FI_VENDOR_AGING\",\n \"ZC_FI_VENDOR_AGING\",\n \"ZFI_VENDOR_AGING_SD\",\n \"ZFI_VENDOR_AGING_UI_V4\"\n ],\n \"package\": \"ZCUSTOM_FI\",\n \"transport\": \"S4HK903456\"\n }\n}\n```\n\nIf the program is in the registry, inform the user:\n\n```\nThis program was already modernized on {date}.\n New objects: {object list}\n Package: {package}\n\nOptions:\n 1. Re-modernize (create a new version with different names)\n 2. Cancel — use the existing objects\n```\n\n**Step 3: Check source for modernization comment (fallback).**\n\nIf the registry has no record (different machine, registry deleted), read\nthe source via `sap_get_source` and scan for `* MODERNIZED:` in the first\n20 lines. If found, extract the new version name and date from the comment\nand present the same options as Step 2.\n\nIf neither the registry nor the source comment indicates a previous\nmodernization, proceed to ASSESS.\n\n**DOCUMENT phase writes the registry:** After a successful modernization,\nsave/update `.cspeach/modernize/registry.json` with the program name\nand all created objects. This is a LOCAL file only — not on the SAP system.\n\n### 1.1 Program Type Detection\n\nRun via `sap_sql_query`:\n\n```sql\nSELECT SUBC FROM TRDIR WHERE NAME = '{program_name}'\n```\n\nMap the result to a modernization pattern:\n\n| SUBC | Type | Modernization Pattern |\n|---|---|---|\n| `1` | Executable report | Report modernization (ALV/WRITE -> Fiori list report) |\n| `M` | Module pool | Transaction modernization (dynpro -> Fiori Elements object page) |\n| `F` | Function group | FM/API modernization (FM -> OData service or RAP API) |\n| `S` | Subroutine pool | Utility extraction (FORM routines -> OO class methods) |\n| `J` | Interface pool | Skip -- already modern OO artifact |\n| `K` | Class pool | Evaluate -- may already be modern; check for RAP/OO patterns |\n\nIf the SUBC query returns no rows, the object may be a CDS view, BDEF, or\nservice binding. Check via:\n\n```sql\nSELECT OBJECT, OBJ_NAME FROM TADIR WHERE OBJ_NAME = '{object_name}'\n```\n\nIf the object type is `DDLS`, `BDEF`, `SRVD`, `SRVB`, or `DDLX`, tell the user:\n\"This object is already a modern artifact ({type}). No modernization needed.\"\nStop the workflow.\n\n### 1.2 System Release Detection\n\nRun two queries to determine the system landscape:\n\n```sql\nSELECT COMPONENT, RELEASE FROM CVERS WHERE COMPONENT = 'SAP_BASIS'\n```\n\n```sql\nSELECT COMPONENT, RELEASE FROM CVERS WHERE COMPONENT = 'S4CORE'\n```\n\nIf `S4CORE` exists, the system is S/4HANA. Otherwise it is ECC.\n\nMap the SAP_BASIS release to available technology stack:\n\n| SAP_BASIS | System | Available Stack |\n|---|---|---|\n| 740-749 | ECC 7.40 | SEGW + Gateway OData V2, OO classes |\n| 750-751 | ECC 7.50 | SEGW + Gateway, basic CDS views |\n| 752 | S/4 1809 | CDS views, basic RAP (unmanaged only) |\n| 753 | S/4 1909 | CDS views, RAP unmanaged |\n| 754-755 | S/4 2020 | RAP managed, CDS with draft |\n| 756 | S/4 2021 | RAP managed + draft, behavior projections |\n| 757 | S/4 2022 | RAP + ABAP Cloud, extensibility |\n| 758+ | S/4 2023+ | Full RAP, ABAP Cloud, side-by-side |\n\n### Runtime Verification Probes\n\nDo not trust the release matrix alone. Run these probes to confirm what actually\nworks on the system:\n\n```sql\nSELECT COUNT(*) FROM DDDDLSRC WHERE DDLNAME LIKE 'Z%'\n```\nIf count > 0, CDS views are confirmed working.\n\n```sql\nSELECT COUNT(*) FROM TADIR WHERE OBJECT = 'BDEF' AND OBJ_NAME LIKE 'Z%'\n```\nIf count > 0, RAP behavior definitions are confirmed working.\n\n```sql\nSELECT COUNT(*) FROM TADIR WHERE OBJECT = 'IWPR' AND OBJ_NAME LIKE 'Z%'\n```\nIf count > 0, SEGW/Gateway projects are confirmed working.\n\nIf probes contradict the release matrix (e.g., release says RAP is available but\nzero BDEFs exist), ask the user: \"Your system release supports RAP, but I found\nno existing RAP objects. Has RAP been used on this system before? Should I\nproceed with RAP or fall back to SEGW?\"\n\n### 1.3 Source Analysis\n\nRead the full source via `sap_get_source` for the program name. If the source\ncontains INCLUDE statements, read each included program separately. Analyze the\ncombined source for these categories:\n\n**Data model.** Extract every database table referenced in SELECT, UPDATE,\nINSERT, DELETE, and MODIFY statements. Note the fields used, WHERE clause\npatterns, and relationships between tables (shared key fields). These become\nCDS view entities or SEGW entity types.\n\n**Selection screen.** Extract all PARAMETERS and SELECT-OPTIONS declarations.\nMap each to a filter annotation in the target architecture:\n- `PARAMETERS p_bukrs TYPE bukrs` -> `@UI.selectionField` on CompanyCode\n- `SELECT-OPTIONS s_matnr FOR mara-matnr` -> `@UI.selectionField` with range support\n\n**Output structure.** Identify all WRITE statements (field list and formatting),\nALV field catalog definitions, or dynpro screen fields. These define the columns\nand layout of the target Fiori UI. Map WRITE positions to `@UI.lineItem`\nannotations with position values.\n\n**Business logic.** Identify calculations, aggregations, conditional logic, and\ndata transformations. Classify each as:\n- Pure data derivation -> CDS calculated field or virtual element\n- Stateful computation -> ABAP class method in the behavior implementation\n- Aggregation -> CDS with aggregation annotation (if release supports it) or ABAP logic\n\n**Authorization checks.** Find all `AUTHORITY-CHECK` statements and map to the\ntarget authorization model:\n- Simple field check -> `pfcg_auth` aspect in DCL access control\n- CRUD-based check -> `authorization master` in behavior definition\n- Complex multi-object check -> Flag for manual review, note in proposal\n\n**Function module calls.** Classify each FM call:\n- Conversion exits (e.g., `CONVERSION_EXIT_ALPHA_INPUT`) -> CDS `@Semantics` annotations\n- BAPIs (e.g., `BAPI_SALESORDER_CREATEFROMDAT2`) -> RAP create/update operations\n- Dialog FMs (e.g., `POPUP_TO_CONFIRM`) -> Flag as \"not portable to Fiori Elements; requires app-level handling\"\n- Utility FMs -> Evaluate for direct ABAP replacement or class method\n\n**Variant usage.** If the program uses selection screen variants, note this for\nthe user: \"The legacy program supports variants. Fiori apps handle saved filters\ndifferently (via Manage Views / tile parameters). Existing variants cannot be\nmigrated automatically.\"\n\nIf any aspect of the source is ambiguous or has multiple valid interpretations,\nask the user with concrete options rather than open-ended questions. Example:\n\"The program writes to both VBAK and VBAP. Should I model these as (A) a single\nflat entity or (B) a root/child composition?\"\n\n### ASSESS Phase Output\n\nProduce a structured summary with these sections:\n1. **Program type** and SUBC code\n2. **System release** and available technology stack\n3. **Data model** -- tables, fields, relationships\n4. **UI mapping** -- selection screen fields, output columns\n5. **Business logic** -- classified as CDS-eligible or ABAP-required\n6. **Authorization model** -- current checks mapped to target mechanism\n7. **Risks and flags** -- dialog FMs, variants, complex auth, unclear patterns\n8. **Recommended target** -- RAP managed, RAP unmanaged, SEGW, or OO-only\n\nEnd ASSESS with: \"ASSESS complete. Ready to move to PROPOSE phase.\"\n\n---\n\n## PROPOSE Phase\n\nPresent 1-2 ranked modernization options based on what ASSESS found. Do not ask\nopen-ended questions here -- the analysis is done. Present concrete options with\nobject lists and let the user pick.\n\n### 2.1 Modernization Path Matrix\n\nSelect the target architecture by crossing the program type (from ASSESS 1.1)\nwith the system release (from ASSESS 1.2):\n\n| Program Type | ECC 7.40+ | ECC 7.50+ | S/4 1909+ | S/4 2021+ | S/4 2022+ |\n|---|---|---|---|---|---|\n| ALV/WRITE Report | SEGW + Fiori | SEGW + Fiori Elements | CDS + Fiori Elements | CDS + Fiori Elements | RAP read-only + Fiori Elements |\n| Transaction (CRUD) | SEGW + Fiori | SEGW + Fiori Elements | CDS + RAP unmanaged | RAP managed + draft | RAP managed + draft |\n| Batch/Background | Refactor to OO | Refactor to OO | Refactor + new syntax | Refactor + new ABAP | Refactor + ABAP Cloud |\n| FM/RFC (API) | Clean up FM | Clean up FM | CDS + SRVD API | RAP unmanaged API | RAP managed API |\n| Utility/Include | Extract to class | Extract to class | Extract to class | Extract to class | Extract to class |\n\n**S/4 1909 note:** CDS views are exposed via SEGW wrapper or basic service\ndefinition. Full RAP read-only requires S/4 2020+. If the runtime verification\nprobes from ASSESS confirmed RAP availability on an S/4 1909 system, use RAP.\nOtherwise fall back to CDS + SEGW.\n\n**ECC adjustments:** On ECC systems, replace \"RAP\" with \"SEGW\" and \"Fiori\nElements\" with \"Fiori freestyle or Elements\" depending on whether Gateway and\nUI5 are available (confirmed by IWPR probe in ASSESS).\n\n### 2.2 Option Selection Logic\n\nFor each program type, present Option 1 (recommended) and Option 2 (alternative):\n\n| Program Type | Option 1 (recommended) | Option 2 (alternative) |\n|---|---|---|\n| Report (any UI) | CDS + Fiori Elements list report | CDS + CL_SALV_TABLE ALV |\n| Transaction (CRUD) | RAP managed BO + Fiori Elements object page | SEGW + Fiori (ECC) |\n| Batch program | Refactor to OO + new ABAP syntax | Extract to class library only |\n| FM/RFC (API) | RAP API service binding | Wrapper class with clean interface |\n| Utility/Include | Extract to OO class library | Refactor in place |\n\nApply these adjustments before presenting options:\n\n- If the system is ECC and the matrix says RAP, downgrade Option 1 to SEGW and\n move the SEGW option to Option 1. Drop the RAP option entirely.\n- If the ASSESS phase flagged complex authorization patterns, note in both options\n that authorization handling requires manual review.\n- If the ASSESS phase found dialog FM calls (POPUP_TO_CONFIRM, etc.), note that\n these cannot be ported to Fiori Elements and require app-level handling or\n removal.\n- If the program has no UI (batch/background), do not propose Fiori Elements.\n Propose OO refactoring only.\n- If the program already uses ALV via CL_SALV_TABLE or CL_GUI_ALV_GRID, Option 2\n becomes \"Keep ALV, refactor data layer to CDS\" instead of another ALV variant.\n\n### 2.3 Object Estimation\n\nFor each option, list the concrete objects that will be created. Use the naming\nconventions from `rap-patterns.md` and `abap-conventions.md`. Count the objects\nand report the total.\n\nTypical object counts by architecture:\n\n| Architecture | Objects |\n|---|---|\n| RAP managed (root only) | Table + CDS interface view + CDS projection view + BDEF + behavior class + SRVD + SRVB + DDLX + DCL = 9 |\n| RAP managed (root + 1 child) | 9 (root) + 5 (child: table, CDS I, CDS C, DDLX, DCL) = 14 |\n| SEGW + Fiori | SEGW project + entity type + service impl class + Fiori app = 4+ |\n| CDS + Fiori Elements (read-only) | CDS interface view + CDS projection view + SRVD + SRVB + DDLX + DCL = 6 |\n| OO refactoring | 1-3 classes + 1-2 interfaces |\n| Wrapper class | 1 class + 1 interface |\n\n### 2.4 PROPOSE Output Format\n\nPresent the proposal in this exact format:\n\n```\n{program_name} — {description from source analysis}\n Reads: {tables}\n UI: {WRITE/ALV/dynpro}\n Logic: {key business logic}\n System: {release + available stack}\n\nOption 1 (recommended): {path from matrix}\n - {object 1}: {type} — {purpose}\n - {object 2}: {type} — {purpose}\n - ...\n - Estimated: {count} objects\n\nOption 2: {alternative from matrix}\n - {object 1}: {type} — {purpose}\n - ...\n - Estimated: {count} objects\n\nWhich option? (or adjust)\n```\n\nDo not elaborate beyond this format. The user needs a clear, scannable summary\nto make a decision. If the user asks for more detail on an option, provide it.\nIf the user asks to adjust (different naming, fewer objects, different\narchitecture), revise the proposal and present again in the same format.\n\n### Phase Transition\n\nEnd PROPOSE with: \"PROPOSE complete. Move to APPROVE.\"\n\n---\n\n## APPROVE Phase\n\nAccept the user's selection or adjustments. The user may:\n\n- Pick an option as-is: \"Use option 1\"\n- Pick an option with modifications: \"Use option 1 but keep BSEG, don't migrate to ACDOCA\"\n- Request additions: \"Add a search help for the customer field\"\n- Change naming: \"Use a different naming convention\"\n- Change packaging: \"Put this in package ZNEW_FI\"\n- Mix options: \"Option 1 architecture but with Option 2's simpler data model\"\n\n### 3.1 Processing Adjustments\n\nApply the user's changes to the proposal. For each adjustment:\n\n- **Table substitution** (\"keep BSEG, don't migrate to ACDOCA\"): Update the CDS view\n design to use the original table. Remove any CCA successor references for that table.\n Note the trade-off: \"Keeping BSEG — this means the CDS view will not be Cloud-ready\n if you migrate to ABAP Cloud later.\"\n\n- **Additional UI elements** (\"add a search help for the customer field\"): Add a value\n help view (ZVH_ prefix) to the object list and add the `@Consumption.valueHelpDefinition`\n annotation to the relevant field in the projection view design.\n\n- **Naming changes** (\"use a different naming convention\"): Apply the user's convention\n consistently across all objects. Verify the convention does not conflict with\n `rap-patterns.md` or `abap-conventions.md` structural requirements (Z/Y prefix,\n interface vs. projection prefix distinction).\n\n- **Package changes** (\"put this in package ZNEW_FI\"): Update the target package for all\n objects. Verify the package exists via `sap_search_object` (query: the package name,\n type: \"DEVC\"). If the package does not exist, ask: \"Package {name} does not exist.\n Should I create it, or use a different package?\"\n\n- **Architecture changes** (\"actually, make it unmanaged RAP\"): Revise the full object\n list. Unmanaged RAP requires a saver class and different BDEF syntax. Managed RAP\n requires persistent table mapping. Present the revised proposal in the same format\n as PROPOSE 2.4.\n\n### 3.2 Confirmation\n\nAfter applying adjustments, confirm with the user:\n\n```\nUpdated plan — {summary of changes applied}.\n{count} objects will be created in package {package}.\nProceed to DESIGN?\n```\n\nIf the user confirms, move to DESIGN. If the user requests further changes, repeat\nthe adjustment cycle. Do not limit the number of revision rounds.\n\n### Phase Transition\n\nEnd APPROVE with: \"APPROVE complete. Moving to DESIGN.\"\n\n---\n\n## DESIGN Phase\n\nDetail every object that will be created: full names, CDS source skeletons, BDEF\nsource, metadata extension annotations, and access control definitions. Present the\ncomplete design for user review before building anything.\n\n### 4.1 Object Naming\n\nDerive names from the original program's business meaning, not from the original\nprogram name. A program named `ZRFI0042` that produces an aging report becomes\n`ZI_FI_AGING`, not `ZI_RFI0042`.\n\nAsk the user:\n\"What should I call the modernized version? Suggestion: `{derived_name}`. Or type\nyour own.\"\n\nApply the user's choice (or the suggested default) to all objects using this naming\nconvention table (per `rap-patterns.md`):\n\n| Object | Pattern | Example |\n|---|---|---|\n| Database table (if new) | ZT_{name} | ZT_FI_AGING |\n| CDS interface view | ZI_{name} | ZI_FI_AGING |\n| CDS projection view | ZC_{name} | ZC_FI_AGING |\n| Behavior definition (read-only) | ZI_{name} | ZI_FI_AGING |\n| Behavior definition (CRUD) | ZR_{name}_TP | ZR_FI_AGING_TP |\n| Behavior pool class | ZBP_{name} | ZBP_FI_AGING |\n| Service definition | Z{name}_SD | ZFI_AGING_SD |\n| Service binding (V4) | Z{name}_UI_V4 | ZFI_AGING_UI_V4 |\n| Metadata extension | ZC_{name}_MDE | ZC_FI_AGING_MDE |\n| Access control (DCL) | ZI_{name} | ZI_FI_AGING |\n\nFor read-only BDEF: use the same name as the interface view (no `_TP` suffix — there\nis no transactional processing). For CRUD BDEF: use ZR_ prefix and _TP suffix per\n`rap-patterns.md`.\n\n### 4.1b Name Collision Check (MANDATORY — Hard Stop)\n\nBefore proceeding past DESIGN, check EVERY derived name via `sap_search_object`.\nRun one search per object:\n\n```\nsap_search_object (query: \"ZI_FI_AGING\", type: \"DDLS\")\nsap_search_object (query: \"ZC_FI_AGING\", type: \"DDLS\")\nsap_search_object (query: \"ZBP_FI_AGING\", type: \"CLAS\")\nsap_search_object (query: \"ZFI_AGING_SD\", type: \"SRVD\")\nsap_search_object (query: \"ZFI_AGING_UI_V4\", type: \"SRVB\")\nsap_search_object (query: \"ZC_FI_AGING_MDE\", type: \"DDLX\")\nsap_search_object (query: \"ZI_FI_AGING\", type: \"DCLS\")\n... for every object in the plan\n```\n\nIf ANY name already exists on the system: **STOP.** Report the collision:\n\"Name collision: `{object_name}` already exists as {type}. Choose a different base\nname or confirm overwrite.\"\n\nDo NOT proceed to BUILD until all names are confirmed free. This is a hard stop,\nnot a warning. If the user provides a new name, re-run the full collision check\nwith the new name.\n\n### 4.2 Package Assignment\n\nAsk the user for the target package. Default: the same package as the original\nprogram (determined during ASSESS via TADIR lookup).\n\n```\nTarget package? Default: {original_package}. Or type a different package name.\n```\n\nIf the user provides a different package, verify it exists via `sap_search_object`\n(query: the package name, type: \"DEVC\"). If it does not exist, ask whether to\ncreate it or use a different one.\n\n### 4.3 CDS View Design\n\nPresent the full CDS interface view source for review. The view must include:\n\n- **All fields from the original output** mapped to CDS element names using\n CamelCase (e.g., `matnr` becomes `MaterialNumber`, `bukrs` becomes `CompanyCode`).\n- **Associations** for related master data. Use the `_Entity` naming convention:\n - `_Customer` for LFA1/KNA1 lookups\n - `_Material` for MARA lookups\n - `_CompanyCode` for T001 lookups\n - `_Currency` for TCURC lookups\n- **Calculated fields** for business logic that can be expressed in CDS:\n - Aging buckets: `CASE WHEN datediff ... END AS AgingBucket`\n - Status derivation: `CASE WHEN ... END AS StatusText`\n - Amount conversions: currency conversion functions if available\n- **Filter annotations** mapped from original selection screen parameters:\n - `@Consumption.filter: { selectionType: #SINGLE }` for PARAMETERS\n - `@Consumption.filter: { selectionType: #INTERVAL }` for SELECT-OPTIONS\n- **Search annotations** on searchable fields:\n - `@Search.searchable: true` at entity level\n - `@Search.defaultSearchElement: true` on key text fields\n - `@Search.fuzzinessThreshold: 0.8` for fuzzy-searchable fields\n- **Text associations** for language-dependent descriptions:\n - `@ObjectModel.text.element: ['MaterialName']` linked to text views\n- **CCA successor tables** if CCA data is available. Use the successor entity\n (e.g., `I_JOURNALENTRY` instead of `BSEG`) unless the user explicitly opted\n to keep the legacy table in the APPROVE phase.\n\nPresent the projection view (ZC_) source as well, showing:\n- Field aliases for Fiori-friendly names where needed\n- `@Consumption.valueHelpDefinition` annotations for lookup fields\n- Exposed associations redirected to projection-level entities\n\n**Read-only reports use a CONSUMPTION view, never a projection.** For a read-only\nlist/analytical report (no behavior), define the ZC_ view as\n`define view entity ZC_… as select from ZI_…` (a consumption view). Do NOT use\n`as projection on ZI_…` — that creates a *transactional projection view* which\nrequires a behavior definition (a BO), and activation fails with \"Transactional\nProjection View must be part of a business object.\" `as projection on` is only for\ntransactional RAP stacks that have a BDEF.\n\n**Currency/amount semantics in view entities.** On amount fields reference the\ncurrency with `@Semantics.amount.currencyCode: 'CurrencyField'`. Do NOT put\n`@Semantics.currencyCode: true` on the currency field — that is the old DDIC\n`define view` syntax and is rejected in a `define view entity` (\"Annotation\nSemantics.currencyCode is not allowed in view entities\"). Do NOT set\n`@Metadata.ignorePropagatedAnnotations: true` when you rely on propagated currency\nsemantics — it blocks them.\n\n### 4.4 BDEF Design\n\n#### For read-only programs (reports)\n\nGenerate a minimal BDEF with authorization master only:\n\n```cds\ndefine behavior for ZI_{name}\nauthorization master ( instance )\n{\n}\n```\n\nThis requires a behavior pool class (ZBP_{name}) with at minimum the\n`get_instance_authorizations` method. Present the class skeleton:\n\n```abap\nCLASS zbp_{name} DEFINITION PUBLIC ABSTRACT FINAL\n FOR BEHAVIOR OF zi_{name}.\nENDCLASS.\n\nCLASS zbp_{name} IMPLEMENTATION.\nENDCLASS.\n```\n\nWith a local handler class implementing `get_instance_authorizations` that maps\nthe original program's AUTHORITY-CHECK statements to RAP authorization responses.\n\n#### For transactional programs (CRUD)\n\nGenerate a full BDEF based on the ASSESS analysis:\n\n- **Managed or unmanaged** based on whether the persistence maps cleanly to a\n single table (managed) or requires complex multi-table writes (unmanaged).\n- **Validations** derived from the original program's input checks:\n - Each `IF ... IS INITIAL` check on a mandatory field becomes a validation\n - Each range/value check becomes a validation\n - Name pattern: `Validate{FieldName}` or `Validate{BusinessRule}`\n- **Determinations** derived from the original program's calculations:\n - Each derived/computed field becomes a determination\n - Name pattern: `Determine{FieldName}`\n - Trigger: `on modify { field Source1, Source2; }` for immediate derivations\n - Trigger: `on save { create; }` for one-time defaults\n- **Actions** derived from the original program's user commands:\n - Status changes become instance actions: `action Submit`, `action Approve`\n - Copy operations become factory actions: `action ( features: instance ) Copy`\n - Background processing becomes static actions\n- **Draft handling** if the original transaction has save/cancel semantics\n (most module pool transactions do).\n\nPresent the full BDEF source for review.\n\n### 4.5 Metadata Extension Design\n\nMap the original program's output layout to Fiori Elements annotations:\n\n| Original | Fiori Annotation | Rule |\n|---|---|---|\n| WRITE field order (left to right) | `@UI.lineItem: [{ position: N }]` | Position increments by 10 |\n| ALV column headers / WRITE labels | `@UI.lineItem: [{ label: '{text}' }]` | Use original label text |\n| Selection screen parameters | `@UI.selectionField: [{ position: N }]` | Position increments by 10 |\n| Field grouping (AT NEW, subtotals) | `@UI.fieldGroup: [{ qualifier: '{group}' }]` | One group per logical section |\n| Key fields (top of output) | `@UI.identification: [{ position: N }]` | Show on object page header |\n| Numeric fields (totals, amounts) | `@UI.dataPoint` | For KPI/header display |\n| Status / aging / RAG / traffic-light logic (CASE buckets, `COLOR =` / colour WRITEs, status flags) | CDS criticality calculated field (1=red / 2=orange / 3=green) + `@UI.lineItem: [{ criticality: 'CriticalityField' }]` and `@UI.dataPoint: { criticality: 'CriticalityField' }` | **Always map this when the source colours or buckets rows.** Drive criticality off the NUMERIC value (e.g. age in days), not the bucket text. Keep any original bucket column as a plain visible field alongside it. |\n\n**Criticality is part of \"make it Fiori, make it sexy\" — emit it by default**, not only when the user asks. If the source program assigns colours, status icons, or aging/RAG buckets to its output, the modern app MUST reproduce that as criticality so the list and object page show the same red/amber/green at a glance.\n\nPresent the full DDLX source with `@UI.headerInfo`, `@UI.lineItem`, `@UI.identification`,\n`@UI.selectionField`, `@UI.fieldGroup`, `@UI.facet`, and (where the source colours/buckets) `@UI.criticality` annotations.\n\n**Always emit a POPULATED object page — the detail view must never be blank.** For every\n`@UI.fieldGroup` qualifier above, emit a matching `@UI.facet` that references it, so clicking a\nrow opens a real detail page:\n\n```cds\n@UI.facet: [\n { id: 'Main', type: #COLLECTION, label: '{Entity}', position: 10 },\n { id: 'DetailsRef', type: #FIELDGROUP_REFERENCE, parentId: 'Main',\n targetQualifier: '{group}', label: '{Section}', position: 10 }\n]\n```\n\nRules:\n- Group fields into logical sections (e.g. **Document** / **Account & Amounts** / **Aging**),\n one `@UI.fieldGroup` qualifier per section, each referenced by a `@UI.facet`.\n- A `@UI.facet` that references no field group — or a field group with no facet — renders an\n **empty** object page. Every field group used MUST have a facet pointing at it, and vice versa.\n- Put the aging/status field (with its criticality) on the object page too, so the detail view\n shows the same red/amber/green as the list.\n\n### 4.6 Access Control Design\n\nPresent the DCL source. Map the original program's AUTHORITY-CHECK to DCL aspects:\n\n```cds\n@EndUserText.label: 'Access Control: {entity description}'\n@MappingRole: true\ndefine role ZI_{name} {\n grant select on ZI_{name}\n where ({field}) = aspect pfcg_auth( {auth_object}, {auth_field}, ACTVT = '03' );\n}\n```\n\nIf the original program has no AUTHORITY-CHECK statements, generate a permissive\nDCL with a comment noting that authorization should be added:\n\n```cds\n// TODO: Add field-level authorization. Original program had no AUTHORITY-CHECK.\n@EndUserText.label: 'Access Control: {entity description}'\n@MappingRole: true\ndefine role ZI_{name} {\n grant select on ZI_{name}\n where _global_context = ' ';\n}\n```\n\n### 4.7 Design Review\n\nPresent the complete design as a summary table:\n\n```\nDESIGN — {base_name} ({count} objects)\n\n| # | Object Name | Type | Purpose |\n|---|---|---|---|\n| 1 | ZI_{name} | CDS View (DDLS) | Interface view — data model |\n| 2 | ZC_{name} | CDS View (DDLS) | Projection view — UI binding |\n| 3 | ZI_{name} | BDEF | Behavior definition |\n| 4 | ZBP_{name} | Class (CLAS) | Behavior pool |\n| 5 | Z{name}_SD | Service Def (SRVD) | Service definition |\n| 6 | Z{name}_UI_V4 | Service Binding (SRVB) | OData V4 UI binding |\n| 7 | ZC_{name}_MDE | Metadata Ext (DDLX) | UI annotations |\n| 8 | ZI_{name} | Access Control (DCLS) | Authorization |\n```\n\nFollowed by: \"Full CDS, BDEF, DDLX, and DCL source shown above. Review and confirm.\"\n\n### Phase Transition\n\nEnd DESIGN with: \"DESIGN complete. Ready to BUILD. Confirm to proceed.\"\n\n---\n\n## BUILD Phase\n\nCreate all modernization objects on the system. Forge Rules 7–10 apply to every write.\n\n**Delegation contract.** For the RAP path, this phase follows `/abap-rap`'s\ncreation order and per-object safety pattern exactly — the orchestrator does\nNOT re-derive that logic. The `abap-modernize` skill adds: the ASSESS / PROPOSE\n/ DESIGN context that produced the source code, the dedicated session transport\n(5.1), the SEGW-specific path (5.5), the batch/utility path (5.6), and the\npost-build VERIFY phase (Section 6). Where a brand-new DDIC object is needed\n(table, domain, data element), creation follows `/abap-data-model`'s pattern\n(CDS `define table` source-based, snapshot before write). When `/abap-rap`\nor `/abap-data-model` documentation evolves (e.g. ordering exceptions, new\nrelease-specific endpoints), this skill benefits automatically — do not copy\ntheir prose here.\n\n### 5.1 Transport Creation (Forge Rule 9)\n\nCreate a **fresh** dedicated transport before the first write:\n\n```\nsap_transport_create: \"Modernize {original_name} to {new_name}\"\n```\n\n**Do NOT pass `for_object_name` = the legacy program here.** The legacy program is\nread-only — modernize never writes it — so its transport lock is irrelevant to the\nnew objects. Passing it makes `sap_transport_create` reuse the program's owning\ntransport, which can route the new CDS/service stack into a request that cannot\nhold them (and overrides the user's \"fresh transport\" choice). The new objects do\nnot exist yet, so a fresh transport is the correct and only answer. Keep the\ndescription ASCII — no em-dashes or arrows (CTS rejects them).\n\nRecord the transport number. Assign every object created or modified during BUILD\nto this transport. Never use `$TMP` unless the user explicitly requests it.\n\n### 5.2 Creation Order (delegates to `/abap-rap`)\n\nThe RAP path follows `/abap-rap`'s documented creation order verbatim:\n\n```\nTable (if new) → CDS interface → CDS projection → BDEF → BP class → SRVD → activate SRVD → SRVB (create + publish) → DDLX (MDE) → DCL\n```\n\nFor the canonical sequence — including the SRVD-must-be-active-before-SRVB\nconstraint, the dependency rationale at each step, and the binding PUBLISH step\n(`sap_service_binding_publish` activates the binding AND registers the OData\nendpoint — verify the result is success) — see `.claude/skills/abap-rap/SKILL.md`.\nThis skill does not duplicate that prose; when `/abap-rap` evolves (new\nrelease-specific behaviour, additional ordering exceptions), the modernization\nBUILD path picks it up automatically.\n\nPer-path rules:\n- **Skip the Table step** for read-only modernization (CDS reads existing tables).\n- **If a brand-new table is needed**, the table creation follows\n `/abap-data-model`'s pattern (CDS `define table` source-based, snapshot\n before write). See `.claude/skills/abap-data-model/SKILL.md`.\n- **For ECC / older S/4** (no RAP support), skip this section and use Section\n 5.5 (SEGW path).\n- **For batch / utility modernization** (no UI/RAP target), skip to Section\n 5.6.\n\n### 5.3 Per-Object Safety Loop\n\nFor each object in the creation order:\n\n1. **Snapshot** (if modifying an EXISTING object): call `sap_snapshot_take` per Rule 7.\n For brand-new objects, no snapshot is needed (nothing to lose).\n2. **Create**: call `sap_create_object` for new objects. Assign to the session transport\n and target package from APPROVE phase.\n3. **Write source**: call `sap_set_source` with the designed source code from DESIGN phase.\n4. **Syntax check**: call `sap_syntax_check` on the object. If errors are returned,\n **STOP. Do not activate.** Report the errors and enter the failure handling flow (5.4).\n5. **Activate**: call `sap_activate` on the object.\n6. **Verify activation**: call `sap_inactive_objects` and confirm the object is NOT listed.\n If it is still listed as inactive, treat this as an activation failure.\n7. **Report**: output the result line for this object.\n\nReport format per object:\n```\n✓ {object_name} ({type}) — created, active\n```\n\n### 5.4 Mid-BUILD Failure Handling\n\nIf any object fails (syntax error, activation error, transport issue):\n\n1. **STOP immediately.** Do not continue to the next object.\n2. Report which objects succeeded and which failed:\n\n```\nBUILD stopped at object 3 of 7.\n ✓ ZI_FI_AGING (CDS interface) — created, active\n ✓ ZC_FI_AGING (CDS projection) — created, active\n ✗ ZI_FI_AGING (BDEF) — syntax error: line 3, unknown keyword\n · ZBP_FI_AGING (Class) — not started\n · ZFI_AGING_SD (Service def) — not started\n · ZFI_AGING_UI_V4 (Service bind) — not started\n · ZC_FI_AGING_MDE (Metadata ext) — not started\n```\n\n3. Offer three options:\n - **Fix and continue:** diagnose the error, fix the source, retry from the failed\n object. Take a new snapshot before each retry write (Rule 7). Re-run the full\n verify sequence: write → syntax check → activate → verify inactive objects.\n - **Skip and continue:** skip the failed object, continue with the next. Warn the\n user that downstream objects depending on the skipped object may also fail.\n - **Stop:** halt BUILD entirely. The user fixes manually and resumes later.\n\nWait for the user's choice before proceeding.\n\n### 5.5 SEGW Path (ECC Systems)\n\nSEGW project creation requires transaction SEGW in SAP GUI. There is NO ADT REST\nendpoint for creating SEGW projects. The skill cannot create the SEGW project\nautomatically.\n\nWhat the skill does for ECC modernization:\n\n```\nYour system is ECC {release}. The modernization requires a SEGW OData service.\nI'll design the complete service model and implement the code, but the SEGW\nproject itself needs to be created in transaction SEGW.\n\nStep 1: I'll give you the model specification (entity types, properties)\nStep 2: You create the project in SEGW and define the model (~15 min)\nStep 3: Generate DPC/MPC classes in SEGW\nStep 4: I'll implement all the DPC_EXT methods automatically\n```\n\nAfter the user creates the SEGW project and generates DPC/MPC:\n- Implement DPC_EXT methods via `sap_update_method` (one call per CRUD operation\n method). This is safer than rewriting the full class per Rule 7a.\n- Create the CDS view (if CDS is supported on the release) that the DPC_EXT reads from.\n- Apply the same safety rules to every write: syntax check, activate, verify.\n\n### 5.6 Batch/Utility Path\n\nNo new RAP objects are created. Modify existing source only.\n\n- **Method-only changes** (logic inside existing methods): use `sap_update_method`\n per method (Rule 7a). This is safer than a full class rewrite because it leaves\n untouched methods, parameters, and class definition intact.\n- **Structural changes** (new methods, parameter changes, visibility changes): use\n `sap_set_source` with a snapshot taken first (Rule 7). Required when the class\n definition itself must change.\n\nTake a snapshot before every write, regardless of whether using `sap_update_method`\nor `sap_set_source`.\n\n### Phase Transition\n\n```\nBUILD complete.\n Objects created: {n}\n All active: {yes/no}\n Transport: {transport_number}\n\nMove to VERIFY.\n```\n\n---\n\n## VERIFY Phase\n\nAfter all objects are built, run the full verification sequence. Do not skip any step.\n\n### 6.1 ATC Run\n\nRun ATC on all new objects. Use `sap_atc_run` per object or per package (if all\nobjects share the same package):\n\n```\nsap_atc_run: object={object_name}, type={object_type}\n```\n\nCollect all findings. Classify by priority:\n- **P1 (Error):** Must fix before declaring success. Stop and fix.\n- **P2 (Warning):** Report to user. Fix if straightforward; otherwise document.\n- **P3 (Info):** Report in summary only. No action required.\n\nIf ATC returns `ARS_W_API_STATE` findings (unreleased API usage), flag each one\nand include in the Clean Core section of the report.\n\n### 6.2 Syntax Check\n\nRun `sap_syntax_check` on every object created during BUILD:\n\n```\nsap_syntax_check: object={object_name}, type={object_type}\n```\n\nAll objects must pass with zero errors. Warnings are acceptable but must be reported.\n\n### 6.3 Inactive Objects Check\n\nRun `sap_inactive_objects` and confirm that NONE of the newly created objects\nappear in the list. If any object is still inactive, treat it as an activation\nfailure:\n\n1. Attempt `sap_activate` on the inactive object.\n2. Re-check `sap_inactive_objects`.\n3. If still inactive after retry, report the failure and stop.\n\n### 6.4 Clean Core API State (Optional)\n\nIf the system supports the `sap_api_state` tool (confirmed by ARS_W_API_STATE\nfindings during ATC or during ASSESS probes), run it on each new object:\n\n```\nsap_api_state: object={object_name}, type={object_type}\n```\n\nVerify that all new objects use only released APIs. Report any unreleased API\nreferences.\n\nIf `sap_api_state` is not available on the system, skip this step and report\n\"Clean Core: not checked (tool unavailable)\" in the summary.\n\n### 6.5 VERIFY Output\n\nReport results in this format:\n\n```\nVERIFY complete.\n Objects created: {n}\n Syntax: all clean\n ATC findings: {n} (list any P1/P2)\n Activation: all active\n Clean Core: {all released / n unreleased / not checked}\n\n Modernization successful.\n```\n\nIf any verification fails: stop, report the exact issue with error messages and\nline numbers, and offer to fix or rollback via snapshots.\n\n### Phase Transition\n\nEnd VERIFY with: \"VERIFY complete. Moving to DOCUMENT.\"\n\n---\n\n## DOCUMENT Phase\n\n### 7.1 Link Original to New (OPTIONAL — Ask User First)\n\nAsk the user before touching the original program:\n\n```\nAdd a modernization comment to the original program? This requires assigning it\nto a transport. [y/n]\n```\n\n**If user agrees:**\n\n1. Take snapshot of original program via `sap_snapshot_take` (Rule 7 — mandatory\n even for a comment addition).\n2. Read current source via `sap_get_source`.\n3. Prepend a comment block to the source:\n\n```abap\n*----------------------------------------------------------------------*\n* MODERNIZED: This program has been modernized with CSPeach.\n* New version: ZC_{name} (CDS view + Fiori Elements)\n* Service: Z{name}_UI_V4\n* Date: {today}\n* Original program preserved — decommission when ready.\n*----------------------------------------------------------------------*\n```\n\n4. Write back via `sap_set_source`. Assign to the session transport.\n5. Run `sap_syntax_check` on the original (Rule 10).\n6. Run `sap_activate` and verify activation (Rule 10).\n\n**If user declines:**\n\nRecord the cross-reference only in the summary output. Do not touch the original.\n\n### 7.2 Generate Modernization Summary\n\nProduce the final summary in this format:\n\n```\nModernization Summary\n Original: {program_name} ({type}, {description})\n New stack: {architecture from PROPOSE}\n Objects created:\n ZI_{name} (CDS interface view)\n ZC_{name} (CDS projection view)\n ZI_{name} (Behavior definition — auth master)\n ZBP_{name} (Behavior pool class)\n Z{name}_SD (Service definition)\n Z{name}_UI_V4 (Service binding — published via sap_service_binding_publish)\n ZC_{name}_MDE (Metadata extension)\n ZI_{name} (Access control — DCL)\n Package: {package}\n Transport: {transport_number}\n Tables migrated: {old -> new} (if CCA successors used)\n Business logic: {what was mapped to CDS / what needs class implementation}\n Authorization: {DCL created / flagged for manual review}\n Note: Service binding is auto-published during BUILD (sap_service_binding_publish) — the OData endpoint is live.\n```\n\nAdapt the object list to match the actual objects created (fewer for read-only,\nmore for root+child compositions). Only list objects that were actually created.\n\n### Phase Transition\n\nEnd DOCUMENT with:\n\"Modernization complete. Original untouched. {service_binding_name} is published — open its service-binding preview to see the Fiori Elements app.\"\n\n---\n\n## Safety Summary\n\nAll CSPeach Forge Rules that apply to `/abap-modernize`:\n\n| Rule | When It Applies | What It Requires |\n|---|---|---|\n| **Rule 6** | ASSESS phase | Read-only — no writes until BUILD |\n| **Rule 7** | Before modifying the original (comment addition only) | `sap_snapshot_take` before `sap_set_source` |\n| **Rule 7a** | Method-only changes (SEGW DPC_EXT, batch refactoring) | `sap_update_method` per method, NOT `sap_set_source` |\n| **Rule 8** | BUILD phase (multi-object workflow) | Present plan, wait for explicit user approval |\n| **Rule 9** | Session start, every object assignment | Dedicated transport for all modernization objects |\n| **Rule 10** | After every `sap_set_source` and `sap_activate` | Syntax check after every write, verify activation |\n\n### Original Program Handling\n\n- Never deleted. Never modified except optional comment (with snapshot).\n- Original continues to work after modernization.\n- Customer decides when to decommission.\n\n### Data Handling\n\n- No data migration. If the modernized version uses CDS views on the SAME tables\n as the original program, no migration is needed.\n- If table structure changes are required (rare), document the gap and flag it in\n the modernization summary.\n\n---\n\n## Out of Scope (V1)\n\nThese are explicit boundaries for V1 of `/abap-modernize`. Do NOT attempt any of\nthese. If the user asks for any of them, respond with: \"This is out of scope for\nV1 of /abap-modernize.\" followed by an explanation of what IS in scope.\n\n| Out of Scope | Why | What IS In Scope |\n|---|---|---|\n| **SAP standard code** | Never modify standard objects | Custom Z/Y/namespace objects only |\n| **Data migration** | Modernize architecture, not data | CDS views on existing tables; document table gaps |\n| **UI5 freestyle apps** | Requires custom UI5 development | Fiori Elements (annotation-driven) only |\n| **Fiori launchpad config** | Tile/catalog setup is manual | Service binding created; user publishes and configures tile |\n| **Multi-program process redesign** | Scope is single program per invocation | Each program modernized individually |\n| **Functional equivalence guarantee** | New architecture may behave differently | User must test; summary documents what was mapped |\n| **SEGW deep entity** | Complex nested entity sets need manual work | Basic CRUD entity sets with flat or single-level structure |\n| **RAP unmanaged with save** | Complex persistence mapping | Managed RAP (auto-persistence) and read-only RAP |\n\n---\n\n## Manifest Emission (CLI integration — v0.7)\n\nAfter all per-object modernizations complete (or are skipped/failed), emit a\nhidden HTML-comment manifest block as the last thing in the response. The\nCSPeach CLI parses this block (via\n`cspeach-cli/src/projects/extract-modernize.ts`) into a `modernize-result`\nenvelope so the file picker, `--from` chain visibility, and team-flow\nstatus renderer all see the run.\n\nThe block must appear EXACTLY once per response, after all human output, in\nthis format:\n\n```\n<!-- cspeach:modernize-manifest\nartefact: modernize-result\ntitle: <one-line title — e.g. \"ZDPR_PAYMENTS — modernization\">\ndetail_path: .cspeach/modernize/<slug>/project.json\nbaseline_path: <project-relative path to the source artefact — usually the\n cca-assessment.cspeach.json that fed this run; \"-\" if standalone>\nbaseline_sha256: <hex if baseline_path points at a cca-assessment; \"\" otherwise>\ntransport: <transport-id or \"-\">\napplied: <n>\nskipped: <n>\nfailed: <n>\npending: <n>\nmodernizations:\n item-001 | <objectName> | <objectType> | <from-architecture> | <to-architecture> | <status>\n item-002 | <objectName> | <objectType> | <from-architecture> | <to-architecture> | <status>\n ...\n-->\n```\n\nField rules:\n- `status` is one of: `applied` | `skipped` | `failed` | `pending`. Match the\n result of the IMPLEMENT/ACTIVATE phase per object.\n- `fromArchitecture` is a short kebab-case tag describing the legacy pattern\n this object had: `classic-procedural`, `bdc-against-<tcode>`,\n `select-from-<table>`, `dynpro-with-table-control`, etc.\n- `toArchitecture` is the rebuild target: `rap-behavior`, `fm-based-posting`,\n `cds-i_journalentryitem`, `class-based-oo`, `fiori-elements`, etc.\n- `detail_path` is project-relative. Resolved from the git root or the\n current workspace.\n- `baseline_path` points BACK at the upstream artefact (the cca-assessment\n this modernize run consumed via `--from`). When the run was standalone\n (no `--from`), set baseline_path to \"-\" and baseline_sha256 to \"\".\n\n## Outputs Signpost\n\nAfter the envelope is saved, name the outputs for the developer so they know\nwhere to look:\n\n```\nOutputs:\n - Open in the viewer -> <modernize-result.cspeach.json> (the saved envelope)\n - Internal detail -> .cspeach/modernize/<slug>/project.json (no need to open)\n - Local registry -> .cspeach/modernize/registry.json (program -> rebuilt objects)\n```\n\n## Post-Save Chain Prompt\n\nAfter the CLI prints `Saved: ...` for the modernize-result envelope, ask once:\n\n> \"Modernization complete. {applied_count} of {total_count} objects rebuilt.\n> Verify the result by re-running CCA to confirm the technical-readiness +\n> usage-status of the modernized objects?\"\n>\n> Options:\n> - Run /abap-cca on the modernized package(s) to re-classify (recommended)\n> - Run the generated tests via /abap-test --status if test-coverage exists\n> - End turn\n\nThe chain prompt is a hint, not a `--from` injection — /abap-cca takes its\nown input (a package or scope). Use the hint pattern:\n\n> `→ Run \\`/abap-cca on <package>\\` to re-classify the modernized objects.`\n",
149
- "sha256": "1a037b27903c8753f8f65276af097bf3ce40013aab8146f392c880ecb556c46e",
155
+ "body": "---\r\nname: abap-modernize\r\ndescription: >\r\n Transform classic ABAP programs to the best possible modern architecture\r\n for the customer's SAP release level. Reads the source, detects the system\r\n capabilities, proposes the optimal modernization path (RAP, SEGW, or OO),\r\n and builds the full stack on the system in one conversation.\r\nphase: BUILD\r\nrequires_mcp: required\r\nforge_rules: [6, 7, 8, 9, 10]\r\nversion: \"1.1\"\r\n---\r\n\r\n# abap-modernize\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n\r\n## Purpose\r\n\r\nTransform classic ABAP programs (WRITE reports, dynpro transactions, function\r\nmodules) into the best modern architecture the customer's SAP release supports.\r\nRun the full journey in one conversation: read legacy source, detect system\r\ncapabilities, propose the optimal target architecture, get approval, build every\r\nobject, verify, and document the result.\r\n\r\n## What This Skill Is NOT\r\n\r\n- **Not an upgrade tool.** `/abap-upgrade-fix` patches syntax compatibility for\r\n higher ABAP releases. This skill redesigns architecture.\r\n- **Not a Clean Core tool.** `/abap-cca` assesses API compliance and replacement\r\n readiness. This skill builds the replacement.\r\n- **Not a refactoring tool.** `/abap-refactor` improves code quality within the\r\n same architecture. This skill changes the architecture itself.\r\n\r\nDifferent buyers: \"make this Fiori\" vs \"make this cloud-ready\" vs \"clean up this\r\ncode.\" This skill handles \"make this Fiori.\"\r\n\r\n## When to Use\r\n\r\n- Classic WRITE report that needs to become a Fiori list report\r\n- Dynpro transaction (module pool) that needs to become a Fiori Elements object page\r\n- Function module that needs to become an OData service or RAP API\r\n- Legacy procedural code that needs modern OO patterns with a UI\r\n\r\nDo NOT use for:\r\n- SAP standard code (never modify standard objects)\r\n- Objects that are already modern (CDS views, RAP BDEFs, service bindings)\r\n- Pure code cleanup without architecture change (use `/abap-refactor`)\r\n- Upgrade compatibility fixes (use `/abap-upgrade-fix`)\r\n\r\n## Required Tools\r\n\r\n| Tool | Phase | Purpose |\r\n|---|---|---|\r\n| `sap_get_source` | ASSESS, DESIGN | Read program source and INCLUDEs |\r\n| `sap_sql_query` | ASSESS | Release detection (CVERS), type detection (TRDIR), capability probes |\r\n| `sap_search_object` | DESIGN | Name collision check for new objects |\r\n| `sap_usage_references` | ASSESS | Find callers and callees of the legacy object |\r\n| `sap_create_object` | BUILD | Create new objects (tables, CDS, classes, etc.) |\r\n| `sap_set_source` | BUILD, DOCUMENT | Write source code, add header comments |\r\n| `sap_update_method` | BUILD | Update individual methods in existing classes |\r\n| `sap_activate` | BUILD | Activate created objects |\r\n| `sap_syntax_check` | BUILD, VERIFY | Syntax validation after every write |\r\n| `sap_inactive_objects` | BUILD, VERIFY | Confirm activation succeeded |\r\n| `sap_atc_run` | VERIFY | ATC quality check on all new objects |\r\n| `sap_lock` / `sap_unlock` | BUILD | Lock management during writes |\r\n| `sap_api_state` | VERIFY | Clean Core compliance check |\r\n\r\n## SQL Constraints\r\n\r\nWhen using `sap_sql_query`, follow these constraints strictly:\r\n\r\n- **No aggregate functions.** Do not use SUM, COUNT(*), AVG, MIN, MAX, GROUP BY.\r\n These are unreliable on the ADT SQL endpoint. Use `SELECT COUNT(*)` only for\r\n existence probes where an approximate or error result is acceptable.\r\n- **No JOINs.** Run separate queries and merge results in your reasoning.\r\n- **Keyset pagination.** For large result sets, use WHERE clauses with key ranges\r\n rather than OFFSET. Example: `WHERE NAME > '{last_key}' ORDER BY NAME`.\r\n- **Limit result size.** Never SELECT all rows from large tables. Always filter\r\n by specific keys or use TOP / UP TO N ROWS.\r\n\r\n## Phase Overview\r\n\r\n```\r\n/abap-modernize {PROGRAM_NAME}\r\n -> ASSESS : Read source, detect release, analyze patterns\r\n -> PROPOSE : Present target architecture with rationale\r\n -> APPROVE : User confirms or adjusts the proposal\r\n -> DESIGN : Detail every object (names, fields, source skeletons)\r\n -> BUILD : Create and activate all objects (Rule 7, 8, 9, 10)\r\n -> VERIFY : Syntax check, activation check, ATC run\r\n -> DOCUMENT : Add header comments, produce summary\r\n```\r\n\r\n## CCA Integration\r\n\r\nAt startup, check for existing CCA analysis data in `.cspeach/cca/projects/`\r\n(for older projects, also check the legacy `.abapforge/cca/projects/`).\r\nIf a CCA report exists for the program or its package, incorporate it:\r\n\r\n| CCA Data | How Modernize Uses It |\r\n|---|---|\r\n| ATC findings (P1 table replacements) | Design CDS views using the replacement tables |\r\n| Clean Core successors (`BSEG` -> `I_JOURNALENTRY`) | Use the CDS successor entity directly instead of the legacy table |\r\n| Technical readiness (`NEEDS_REDESIGN`) | Confirms modernization is the correct action |\r\n| Usage status (`UNVERIFIED`) | Note in the proposal that usage data was not verified |\r\n\r\nField paths in the CCA JSON: `atcFindings[]`, `cleanCore.successors[]`,\r\n`technicalReadiness.status`, `usageStatus.status`.\r\n\r\nIf no CCA data exists, ASSESS performs the full analysis from scratch. Do not\r\nrequire the user to run `/abap-cca` first.\r\n\r\n### `--from @<cca-assessment>` chain (v0.7)\r\n\r\nWhen invoked as `/abap-modernize --from @<cca-assessment-file>`, the CLI\r\nadapter reads the assessment's `content.detailPath` (project.json at\r\n`.cspeach/cca/projects/<slug>/project.json`; older projects may still point at\r\nthe legacy `.abapforge/cca/projects/<slug>/` location — read there as a\r\nfallback) and pre-filters the work list to:\r\n\r\n1. Every object classified `redesign` (full architectural rebuild needed).\r\n2. Every object classified `fix` whose findings include Clean Core API\r\n violations — direct reads of `NOT_TO_BE_RELEASED` tables (`BSEG`,\r\n `BKPF`, `ACDOCA`, etc.), calls to deprecated BAPIs without released\r\n successors, or other structural patterns that modernize can rewrite.\r\n\r\nObjects classified `fix` with PURE ATC syntax findings (WRBTR\r\nfield-length, deprecated statements without API replacements) are\r\nSKIPPED with a hint suggesting `/abap-upgrade-fix` — those are\r\nmechanical, not architectural, and don't need modernization's heavier\r\nloop.\r\n\r\nProcess the filtered list one object at a time, applying the full\r\nASSESS → PROPOSE → APPROVE → DESIGN → SCAFFOLD → IMPLEMENT → ACTIVATE →\r\nHANDOFF flow per object. The cca-assessment carries the per-object\r\nclassifications + successor mappings; reuse them rather than\r\nre-running the analysis.\r\n\r\n---\r\n\r\n## ASSESS Phase\r\n\r\nStart ASSESS immediately when the user invokes `/abap-modernize {PROGRAM_NAME}`.\r\nDo not ask preliminary questions -- read the system first, then ask only what\r\nthe source code cannot answer.\r\n\r\n### 0. Already-Modernized Check\r\n\r\n**Step 1: Check if the object is already modern.**\r\n\r\nQuery the object type — if it is DDLS, BDEF, SRVD, or SRVB:\r\n\r\n```\r\nThis is already a modern ABAP Cloud object ({type}). Nothing to modernize.\r\n```\r\n\r\n**Step 2: Check local modernization registry.**\r\n\r\nCheck `.cspeach/modernize/registry.json` for a record of this program (for\r\nolder projects, also check the legacy `.abapforge/modernize/registry.json`).\r\nThe registry maps original program names to their modernized objects:\r\n\r\n```json\r\n{\r\n \"ZLEGACY_FI_REPORT\": {\r\n \"modernizedAt\": \"2026-04-13\",\r\n \"businessName\": \"FI_VENDOR_AGING\",\r\n \"objects\": [\r\n \"ZI_FI_VENDOR_AGING\",\r\n \"ZC_FI_VENDOR_AGING\",\r\n \"ZFI_VENDOR_AGING_SD\",\r\n \"ZFI_VENDOR_AGING_UI_V4\"\r\n ],\r\n \"package\": \"ZCUSTOM_FI\",\r\n \"transport\": \"S4HK903456\"\r\n }\r\n}\r\n```\r\n\r\nIf the program is in the registry, inform the user:\r\n\r\n```\r\nThis program was already modernized on {date}.\r\n New objects: {object list}\r\n Package: {package}\r\n\r\nOptions:\r\n 1. Re-modernize (create a new version with different names)\r\n 2. Cancel — use the existing objects\r\n```\r\n\r\n**Step 3: Check source for modernization comment (fallback).**\r\n\r\nIf the registry has no record (different machine, registry deleted), read\r\nthe source via `sap_get_source` and scan for `* MODERNIZED:` in the first\r\n20 lines. If found, extract the new version name and date from the comment\r\nand present the same options as Step 2.\r\n\r\nIf neither the registry nor the source comment indicates a previous\r\nmodernization, proceed to ASSESS.\r\n\r\n**DOCUMENT phase writes the registry:** After a successful modernization,\r\nsave/update `.cspeach/modernize/registry.json` with the program name\r\nand all created objects. This is a LOCAL file only — not on the SAP system.\r\n\r\n### 1.1 Program Type Detection\r\n\r\nRun via `sap_sql_query`:\r\n\r\n```sql\r\nSELECT SUBC FROM TRDIR WHERE NAME = '{program_name}'\r\n```\r\n\r\nMap the result to a modernization pattern:\r\n\r\n| SUBC | Type | Modernization Pattern |\r\n|---|---|---|\r\n| `1` | Executable report | Report modernization (ALV/WRITE -> Fiori list report) |\r\n| `M` | Module pool | Transaction modernization (dynpro -> Fiori Elements object page) |\r\n| `F` | Function group | FM/API modernization (FM -> OData service or RAP API) |\r\n| `S` | Subroutine pool | Utility extraction (FORM routines -> OO class methods) |\r\n| `J` | Interface pool | Skip -- already modern OO artifact |\r\n| `K` | Class pool | Evaluate -- may already be modern; check for RAP/OO patterns |\r\n\r\nIf the SUBC query returns no rows, the object may be a CDS view, BDEF, or\r\nservice binding. Check via:\r\n\r\n```sql\r\nSELECT OBJECT, OBJ_NAME FROM TADIR WHERE OBJ_NAME = '{object_name}'\r\n```\r\n\r\nIf the object type is `DDLS`, `BDEF`, `SRVD`, `SRVB`, or `DDLX`, tell the user:\r\n\"This object is already a modern artifact ({type}). No modernization needed.\"\r\nStop the workflow.\r\n\r\n### 1.2 System Release Detection\r\n\r\nRun two queries to determine the system landscape:\r\n\r\n```sql\r\nSELECT COMPONENT, RELEASE FROM CVERS WHERE COMPONENT = 'SAP_BASIS'\r\n```\r\n\r\n```sql\r\nSELECT COMPONENT, RELEASE FROM CVERS WHERE COMPONENT = 'S4CORE'\r\n```\r\n\r\nIf `S4CORE` exists, the system is S/4HANA. Otherwise it is ECC.\r\n\r\nMap the SAP_BASIS release to available technology stack:\r\n\r\n| SAP_BASIS | System | Available Stack |\r\n|---|---|---|\r\n| 740-749 | ECC 7.40 | SEGW + Gateway OData V2, OO classes |\r\n| 750-751 | ECC 7.50 | SEGW + Gateway, basic CDS views |\r\n| 752 | S/4 1809 | CDS views, basic RAP (unmanaged only) |\r\n| 753 | S/4 1909 | CDS views, RAP unmanaged |\r\n| 754-755 | S/4 2020 | RAP managed, CDS with draft |\r\n| 756 | S/4 2021 | RAP managed + draft, behavior projections |\r\n| 757 | S/4 2022 | RAP + ABAP Cloud, extensibility |\r\n| 758+ | S/4 2023+ | Full RAP, ABAP Cloud, side-by-side |\r\n\r\n### Runtime Verification Probes\r\n\r\nDo not trust the release matrix alone. Run these probes to confirm what actually\r\nworks on the system:\r\n\r\n```sql\r\nSELECT COUNT(*) FROM DDDDLSRC WHERE DDLNAME LIKE 'Z%'\r\n```\r\nIf count > 0, CDS views are confirmed working.\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TADIR WHERE OBJECT = 'BDEF' AND OBJ_NAME LIKE 'Z%'\r\n```\r\nIf count > 0, RAP behavior definitions are confirmed working.\r\n\r\n```sql\r\nSELECT COUNT(*) FROM TADIR WHERE OBJECT = 'IWPR' AND OBJ_NAME LIKE 'Z%'\r\n```\r\nIf count > 0, SEGW/Gateway projects are confirmed working.\r\n\r\nIf probes contradict the release matrix (e.g., release says RAP is available but\r\nzero BDEFs exist), ask the user: \"Your system release supports RAP, but I found\r\nno existing RAP objects. Has RAP been used on this system before? Should I\r\nproceed with RAP or fall back to SEGW?\"\r\n\r\n### 1.3 Source Analysis\r\n\r\nRead the full source via `sap_get_source` for the program name. If the source\r\ncontains INCLUDE statements, read each included program separately. Analyze the\r\ncombined source for these categories:\r\n\r\n**Data model.** Extract every database table referenced in SELECT, UPDATE,\r\nINSERT, DELETE, and MODIFY statements. Note the fields used, WHERE clause\r\npatterns, and relationships between tables (shared key fields). These become\r\nCDS view entities or SEGW entity types.\r\n\r\n**Selection screen.** Extract all PARAMETERS and SELECT-OPTIONS declarations.\r\nMap each to a filter annotation in the target architecture:\r\n- `PARAMETERS p_bukrs TYPE bukrs` -> `@UI.selectionField` on CompanyCode\r\n- `SELECT-OPTIONS s_matnr FOR mara-matnr` -> `@UI.selectionField` with range support\r\n\r\n**Output structure.** Identify all WRITE statements (field list and formatting),\r\nALV field catalog definitions, or dynpro screen fields. These define the columns\r\nand layout of the target Fiori UI. Map WRITE positions to `@UI.lineItem`\r\nannotations with position values.\r\n\r\n**Business logic.** Identify calculations, aggregations, conditional logic, and\r\ndata transformations. Classify each as:\r\n- Pure data derivation -> CDS calculated field or virtual element\r\n- Stateful computation -> ABAP class method in the behavior implementation\r\n- Aggregation -> CDS with aggregation annotation (if release supports it) or ABAP logic\r\n\r\n**Authorization checks.** Find all `AUTHORITY-CHECK` statements and map to the\r\ntarget authorization model:\r\n- Simple field check -> `pfcg_auth` aspect in DCL access control\r\n- CRUD-based check -> `authorization master` in behavior definition\r\n- Complex multi-object check -> Flag for manual review, note in proposal\r\n\r\n**Function module calls.** Classify each FM call:\r\n- Conversion exits (e.g., `CONVERSION_EXIT_ALPHA_INPUT`) -> CDS `@Semantics` annotations\r\n- BAPIs (e.g., `BAPI_SALESORDER_CREATEFROMDAT2`) -> RAP create/update operations\r\n- Dialog FMs (e.g., `POPUP_TO_CONFIRM`) -> Flag as \"not portable to Fiori Elements; requires app-level handling\"\r\n- Utility FMs -> Evaluate for direct ABAP replacement or class method\r\n\r\n**Variant usage.** If the program uses selection screen variants, note this for\r\nthe user: \"The legacy program supports variants. Fiori apps handle saved filters\r\ndifferently (via Manage Views / tile parameters). Existing variants cannot be\r\nmigrated automatically.\"\r\n\r\nIf any aspect of the source is ambiguous or has multiple valid interpretations,\r\nask the user with concrete options rather than open-ended questions. Example:\r\n\"The program writes to both VBAK and VBAP. Should I model these as (A) a single\r\nflat entity or (B) a root/child composition?\"\r\n\r\n### ASSESS Phase Output\r\n\r\nProduce a structured summary with these sections:\r\n1. **Program type** and SUBC code\r\n2. **System release** and available technology stack\r\n3. **Data model** -- tables, fields, relationships\r\n4. **UI mapping** -- selection screen fields, output columns\r\n5. **Business logic** -- classified as CDS-eligible or ABAP-required\r\n6. **Authorization model** -- current checks mapped to target mechanism\r\n7. **Risks and flags** -- dialog FMs, variants, complex auth, unclear patterns\r\n8. **Recommended target** -- RAP managed, RAP unmanaged, SEGW, or OO-only\r\n\r\nEnd ASSESS with: \"ASSESS complete. Ready to move to PROPOSE phase.\"\r\n\r\n---\r\n\r\n## PROPOSE Phase\r\n\r\nPresent 1-2 ranked modernization options based on what ASSESS found. Do not ask\r\nopen-ended questions here -- the analysis is done. Present concrete options with\r\nobject lists and let the user pick.\r\n\r\n### 2.1 Modernization Path Matrix\r\n\r\nSelect the target architecture by crossing the program type (from ASSESS 1.1)\r\nwith the system release (from ASSESS 1.2):\r\n\r\n| Program Type | ECC 7.40+ | ECC 7.50+ | S/4 1909+ | S/4 2021+ | S/4 2022+ |\r\n|---|---|---|---|---|---|\r\n| ALV/WRITE Report | SEGW + Fiori | SEGW + Fiori Elements | CDS + Fiori Elements | CDS + Fiori Elements | RAP read-only + Fiori Elements |\r\n| Transaction (CRUD) | SEGW + Fiori | SEGW + Fiori Elements | CDS + RAP unmanaged | RAP managed + draft | RAP managed + draft |\r\n| Batch/Background | Refactor to OO | Refactor to OO | Refactor + new syntax | Refactor + new ABAP | Refactor + ABAP Cloud |\r\n| FM/RFC (API) | Clean up FM | Clean up FM | CDS + SRVD API | RAP unmanaged API | RAP managed API |\r\n| Utility/Include | Extract to class | Extract to class | Extract to class | Extract to class | Extract to class |\r\n\r\n**S/4 1909 note:** CDS views are exposed via SEGW wrapper or basic service\r\ndefinition. Full RAP read-only requires S/4 2020+. If the runtime verification\r\nprobes from ASSESS confirmed RAP availability on an S/4 1909 system, use RAP.\r\nOtherwise fall back to CDS + SEGW.\r\n\r\n**ECC adjustments:** On ECC systems, replace \"RAP\" with \"SEGW\" and \"Fiori\r\nElements\" with \"Fiori freestyle or Elements\" depending on whether Gateway and\r\nUI5 are available (confirmed by IWPR probe in ASSESS).\r\n\r\n### 2.2 Option Selection Logic\r\n\r\nFor each program type, present Option 1 (recommended) and Option 2 (alternative):\r\n\r\n| Program Type | Option 1 (recommended) | Option 2 (alternative) |\r\n|---|---|---|\r\n| Report (any UI) | CDS + Fiori Elements list report | CDS + CL_SALV_TABLE ALV |\r\n| Transaction (CRUD) | RAP managed BO + Fiori Elements object page | SEGW + Fiori (ECC) |\r\n| Batch program | Refactor to OO + new ABAP syntax | Extract to class library only |\r\n| FM/RFC (API) | RAP API service binding | Wrapper class with clean interface |\r\n| Utility/Include | Extract to OO class library | Refactor in place |\r\n\r\nApply these adjustments before presenting options:\r\n\r\n- If the system is ECC and the matrix says RAP, downgrade Option 1 to SEGW and\r\n move the SEGW option to Option 1. Drop the RAP option entirely.\r\n- If the ASSESS phase flagged complex authorization patterns, note in both options\r\n that authorization handling requires manual review.\r\n- If the ASSESS phase found dialog FM calls (POPUP_TO_CONFIRM, etc.), note that\r\n these cannot be ported to Fiori Elements and require app-level handling or\r\n removal.\r\n- If the program has no UI (batch/background), do not propose Fiori Elements.\r\n Propose OO refactoring only.\r\n- If the program already uses ALV via CL_SALV_TABLE or CL_GUI_ALV_GRID, Option 2\r\n becomes \"Keep ALV, refactor data layer to CDS\" instead of another ALV variant.\r\n\r\n### 2.3 Object Estimation\r\n\r\nFor each option, list the concrete objects that will be created. Use the naming\r\nconventions from `rap-patterns.md` and `abap-conventions.md`. Count the objects\r\nand report the total.\r\n\r\nTypical object counts by architecture:\r\n\r\n| Architecture | Objects |\r\n|---|---|\r\n| RAP managed (root only) | Table + CDS interface view + CDS projection view + BDEF + behavior class + SRVD + SRVB + DDLX + DCL = 9 |\r\n| RAP managed (root + 1 child) | 9 (root) + 5 (child: table, CDS I, CDS C, DDLX, DCL) = 14 |\r\n| SEGW + Fiori | SEGW project + entity type + service impl class + Fiori app = 4+ |\r\n| CDS + Fiori Elements (read-only) | CDS interface view + CDS projection view + SRVD + SRVB + DDLX + DCL = 6 |\r\n| OO refactoring | 1-3 classes + 1-2 interfaces |\r\n| Wrapper class | 1 class + 1 interface |\r\n\r\n### 2.4 PROPOSE Output Format\r\n\r\nPresent the proposal in this exact format:\r\n\r\n```\r\n{program_name} — {description from source analysis}\r\n Reads: {tables}\r\n UI: {WRITE/ALV/dynpro}\r\n Logic: {key business logic}\r\n System: {release + available stack}\r\n\r\nOption 1 (recommended): {path from matrix}\r\n - {object 1}: {type} — {purpose}\r\n - {object 2}: {type} — {purpose}\r\n - ...\r\n - Estimated: {count} objects\r\n\r\nOption 2: {alternative from matrix}\r\n - {object 1}: {type} — {purpose}\r\n - ...\r\n - Estimated: {count} objects\r\n\r\nWhich option? (or adjust)\r\n```\r\n\r\nDo not elaborate beyond this format. The user needs a clear, scannable summary\r\nto make a decision. If the user asks for more detail on an option, provide it.\r\nIf the user asks to adjust (different naming, fewer objects, different\r\narchitecture), revise the proposal and present again in the same format.\r\n\r\n### Phase Transition\r\n\r\nEnd PROPOSE with: \"PROPOSE complete. Move to APPROVE.\"\r\n\r\n---\r\n\r\n## APPROVE Phase\r\n\r\nAccept the user's selection or adjustments. The user may:\r\n\r\n- Pick an option as-is: \"Use option 1\"\r\n- Pick an option with modifications: \"Use option 1 but keep BSEG, don't migrate to ACDOCA\"\r\n- Request additions: \"Add a search help for the customer field\"\r\n- Change naming: \"Use a different naming convention\"\r\n- Change packaging: \"Put this in package ZNEW_FI\"\r\n- Mix options: \"Option 1 architecture but with Option 2's simpler data model\"\r\n\r\n### 3.1 Processing Adjustments\r\n\r\nApply the user's changes to the proposal. For each adjustment:\r\n\r\n- **Table substitution** (\"keep BSEG, don't migrate to ACDOCA\"): Update the CDS view\r\n design to use the original table. Remove any CCA successor references for that table.\r\n Note the trade-off: \"Keeping BSEG — this means the CDS view will not be Cloud-ready\r\n if you migrate to ABAP Cloud later.\"\r\n\r\n- **Additional UI elements** (\"add a search help for the customer field\"): Add a value\r\n help view (ZVH_ prefix) to the object list and add the `@Consumption.valueHelpDefinition`\r\n annotation to the relevant field in the projection view design.\r\n\r\n- **Naming changes** (\"use a different naming convention\"): Apply the user's convention\r\n consistently across all objects. Verify the convention does not conflict with\r\n `rap-patterns.md` or `abap-conventions.md` structural requirements (Z/Y prefix,\r\n interface vs. projection prefix distinction).\r\n\r\n- **Package changes** (\"put this in package ZNEW_FI\"): Update the target package for all\r\n objects. Verify the package exists via `sap_search_object` (query: the package name,\r\n type: \"DEVC\"). If the package does not exist, ask: \"Package {name} does not exist.\r\n Should I create it, or use a different package?\"\r\n\r\n- **Architecture changes** (\"actually, make it unmanaged RAP\"): Revise the full object\r\n list. Unmanaged RAP requires a saver class and different BDEF syntax. Managed RAP\r\n requires persistent table mapping. Present the revised proposal in the same format\r\n as PROPOSE 2.4.\r\n\r\n### 3.2 Confirmation\r\n\r\nAfter applying adjustments, confirm with the user:\r\n\r\n```\r\nUpdated plan — {summary of changes applied}.\r\n{count} objects will be created in package {package}.\r\nProceed to DESIGN?\r\n```\r\n\r\nIf the user confirms, move to DESIGN. If the user requests further changes, repeat\r\nthe adjustment cycle. Do not limit the number of revision rounds.\r\n\r\n### Phase Transition\r\n\r\nEnd APPROVE with: \"APPROVE complete. Moving to DESIGN.\"\r\n\r\n---\r\n\r\n## DESIGN Phase\r\n\r\nDetail every object that will be created: full names, CDS source skeletons, BDEF\r\nsource, metadata extension annotations, and access control definitions. Present the\r\ncomplete design for user review before building anything.\r\n\r\n### 4.1 Object Naming\r\n\r\nDerive names from the original program's business meaning, not from the original\r\nprogram name. A program named `ZRFI0042` that produces an aging report becomes\r\n`ZI_FI_AGING`, not `ZI_RFI0042`.\r\n\r\nAsk the user:\r\n\"What should I call the modernized version? Suggestion: `{derived_name}`. Or type\r\nyour own.\"\r\n\r\nApply the user's choice (or the suggested default) to all objects using this naming\r\nconvention table (per `rap-patterns.md`):\r\n\r\n| Object | Pattern | Example |\r\n|---|---|---|\r\n| Database table (if new) | ZT_{name} | ZT_FI_AGING |\r\n| CDS interface view | ZI_{name} | ZI_FI_AGING |\r\n| CDS projection view | ZC_{name} | ZC_FI_AGING |\r\n| Behavior definition (read-only) | ZI_{name} | ZI_FI_AGING |\r\n| Behavior definition (CRUD) | ZR_{name}_TP | ZR_FI_AGING_TP |\r\n| Behavior pool class | ZBP_{name} | ZBP_FI_AGING |\r\n| Service definition | Z{name}_SD | ZFI_AGING_SD |\r\n| Service binding (V4) | Z{name}_UI_V4 | ZFI_AGING_UI_V4 |\r\n| Metadata extension | ZC_{name}_MDE | ZC_FI_AGING_MDE |\r\n| Access control (DCL) | ZI_{name} | ZI_FI_AGING |\r\n\r\nFor read-only BDEF: use the same name as the interface view (no `_TP` suffix — there\r\nis no transactional processing). For CRUD BDEF: use ZR_ prefix and _TP suffix per\r\n`rap-patterns.md`.\r\n\r\n### 4.1b Name Collision Check (MANDATORY — Hard Stop)\r\n\r\nBefore proceeding past DESIGN, check EVERY derived name via `sap_search_object`.\r\nRun one search per object:\r\n\r\n```\r\nsap_search_object (query: \"ZI_FI_AGING\", type: \"DDLS\")\r\nsap_search_object (query: \"ZC_FI_AGING\", type: \"DDLS\")\r\nsap_search_object (query: \"ZBP_FI_AGING\", type: \"CLAS\")\r\nsap_search_object (query: \"ZFI_AGING_SD\", type: \"SRVD\")\r\nsap_search_object (query: \"ZFI_AGING_UI_V4\", type: \"SRVB\")\r\nsap_search_object (query: \"ZC_FI_AGING_MDE\", type: \"DDLX\")\r\nsap_search_object (query: \"ZI_FI_AGING\", type: \"DCLS\")\r\n... for every object in the plan\r\n```\r\n\r\nIf ANY name already exists on the system: **STOP.** Report the collision:\r\n\"Name collision: `{object_name}` already exists as {type}. Choose a different base\r\nname or confirm overwrite.\"\r\n\r\nDo NOT proceed to BUILD until all names are confirmed free. This is a hard stop,\r\nnot a warning. If the user provides a new name, re-run the full collision check\r\nwith the new name.\r\n\r\n### 4.2 Package Assignment\r\n\r\nAsk the user for the target package. Default: the same package as the original\r\nprogram (determined during ASSESS via TADIR lookup).\r\n\r\n```\r\nTarget package? Default: {original_package}. Or type a different package name.\r\n```\r\n\r\nIf the user provides a different package, verify it exists via `sap_search_object`\r\n(query: the package name, type: \"DEVC\"). If it does not exist, ask whether to\r\ncreate it or use a different one.\r\n\r\n### 4.3 CDS View Design\r\n\r\nPresent the full CDS interface view source for review. The view must include:\r\n\r\n- **All fields from the original output** mapped to CDS element names using\r\n CamelCase (e.g., `matnr` becomes `MaterialNumber`, `bukrs` becomes `CompanyCode`).\r\n- **Associations** for related master data. Use the `_Entity` naming convention:\r\n - `_Customer` for LFA1/KNA1 lookups\r\n - `_Material` for MARA lookups\r\n - `_CompanyCode` for T001 lookups\r\n - `_Currency` for TCURC lookups\r\n- **Calculated fields** for business logic that can be expressed in CDS:\r\n - Aging buckets: `CASE WHEN datediff ... END AS AgingBucket`\r\n - Status derivation: `CASE WHEN ... END AS StatusText`\r\n - Amount conversions: currency conversion functions if available\r\n- **Filter annotations** mapped from original selection screen parameters:\r\n - `@Consumption.filter: { selectionType: #SINGLE }` for PARAMETERS\r\n - `@Consumption.filter: { selectionType: #INTERVAL }` for SELECT-OPTIONS\r\n- **Search annotations** on searchable fields:\r\n - `@Search.searchable: true` at entity level\r\n - `@Search.defaultSearchElement: true` on key text fields\r\n - `@Search.fuzzinessThreshold: 0.8` for fuzzy-searchable fields\r\n- **Text associations** for language-dependent descriptions:\r\n - `@ObjectModel.text.element: ['MaterialName']` linked to text views\r\n- **CCA successor tables** if CCA data is available. Use the successor entity\r\n (e.g., `I_JOURNALENTRY` instead of `BSEG`) unless the user explicitly opted\r\n to keep the legacy table in the APPROVE phase.\r\n\r\nPresent the projection view (ZC_) source as well, showing:\r\n- Field aliases for Fiori-friendly names where needed\r\n- `@Consumption.valueHelpDefinition` annotations for lookup fields\r\n- Exposed associations redirected to projection-level entities\r\n\r\n**Read-only reports use a CONSUMPTION view, never a projection.** For a read-only\r\nlist/analytical report (no behavior), define the ZC_ view as\r\n`define view entity ZC_… as select from ZI_…` (a consumption view). Do NOT use\r\n`as projection on ZI_…` — that creates a *transactional projection view* which\r\nrequires a behavior definition (a BO), and activation fails with \"Transactional\r\nProjection View must be part of a business object.\" `as projection on` is only for\r\ntransactional RAP stacks that have a BDEF.\r\n\r\n**Currency/amount semantics in view entities.** On amount fields reference the\r\ncurrency with `@Semantics.amount.currencyCode: 'CurrencyField'`. Do NOT put\r\n`@Semantics.currencyCode: true` on the currency field — that is the old DDIC\r\n`define view` syntax and is rejected in a `define view entity` (\"Annotation\r\nSemantics.currencyCode is not allowed in view entities\"). Do NOT set\r\n`@Metadata.ignorePropagatedAnnotations: true` when you rely on propagated currency\r\nsemantics — it blocks them.\r\n\r\n### 4.4 BDEF Design\r\n\r\n#### For read-only programs (reports)\r\n\r\nGenerate a minimal BDEF with authorization master only:\r\n\r\n```cds\r\ndefine behavior for ZI_{name}\r\nauthorization master ( instance )\r\n{\r\n}\r\n```\r\n\r\nThis requires a behavior pool class (ZBP_{name}) with at minimum the\r\n`get_instance_authorizations` method. Present the class skeleton:\r\n\r\n```abap\r\nCLASS zbp_{name} DEFINITION PUBLIC ABSTRACT FINAL\r\n FOR BEHAVIOR OF zi_{name}.\r\nENDCLASS.\r\n\r\nCLASS zbp_{name} IMPLEMENTATION.\r\nENDCLASS.\r\n```\r\n\r\nWith a local handler class implementing `get_instance_authorizations` that maps\r\nthe original program's AUTHORITY-CHECK statements to RAP authorization responses.\r\n\r\n#### For transactional programs (CRUD)\r\n\r\nGenerate a full BDEF based on the ASSESS analysis:\r\n\r\n- **Managed or unmanaged** based on whether the persistence maps cleanly to a\r\n single table (managed) or requires complex multi-table writes (unmanaged).\r\n- **Validations** derived from the original program's input checks:\r\n - Each `IF ... IS INITIAL` check on a mandatory field becomes a validation\r\n - Each range/value check becomes a validation\r\n - Name pattern: `Validate{FieldName}` or `Validate{BusinessRule}`\r\n- **Determinations** derived from the original program's calculations:\r\n - Each derived/computed field becomes a determination\r\n - Name pattern: `Determine{FieldName}`\r\n - Trigger: `on modify { field Source1, Source2; }` for immediate derivations\r\n - Trigger: `on save { create; }` for one-time defaults\r\n- **Actions** derived from the original program's user commands:\r\n - Status changes become instance actions: `action Submit`, `action Approve`\r\n - Copy operations become factory actions: `action ( features: instance ) Copy`\r\n - Background processing becomes static actions\r\n- **Draft handling** if the original transaction has save/cancel semantics\r\n (most module pool transactions do).\r\n\r\nPresent the full BDEF source for review.\r\n\r\n### 4.5 Metadata Extension Design\r\n\r\nMap the original program's output layout to Fiori Elements annotations:\r\n\r\n| Original | Fiori Annotation | Rule |\r\n|---|---|---|\r\n| WRITE field order (left to right) | `@UI.lineItem: [{ position: N }]` | Position increments by 10 |\r\n| ALV column headers / WRITE labels | `@UI.lineItem: [{ label: '{text}' }]` | Use original label text |\r\n| Selection screen parameters | `@UI.selectionField: [{ position: N }]` | Position increments by 10 |\r\n| Field grouping (AT NEW, subtotals) | `@UI.fieldGroup: [{ qualifier: '{group}' }]` | One group per logical section |\r\n| Key fields (top of output) | `@UI.identification: [{ position: N }]` | Show on object page header |\r\n| Numeric fields (totals, amounts) | `@UI.dataPoint` | For KPI/header display |\r\n| Status / aging / RAG / traffic-light logic (CASE buckets, `COLOR =` / colour WRITEs, status flags) | CDS criticality calculated field (1=red / 2=orange / 3=green) + `@UI.lineItem: [{ criticality: 'CriticalityField' }]` and `@UI.dataPoint: { criticality: 'CriticalityField' }` | **Always map this when the source colours or buckets rows.** Drive criticality off the NUMERIC value (e.g. age in days), not the bucket text. Keep any original bucket column as a plain visible field alongside it. |\r\n\r\n**Criticality is part of \"make it Fiori, make it sexy\" — emit it by default**, not only when the user asks. If the source program assigns colours, status icons, or aging/RAG buckets to its output, the modern app MUST reproduce that as criticality so the list and object page show the same red/amber/green at a glance.\r\n\r\nPresent the full DDLX source with `@UI.headerInfo`, `@UI.lineItem`, `@UI.identification`,\r\n`@UI.selectionField`, `@UI.fieldGroup`, `@UI.facet`, and (where the source colours/buckets) `@UI.criticality` annotations.\r\n\r\n**Always emit a POPULATED object page — the detail view must never be blank.** For every\r\n`@UI.fieldGroup` qualifier above, emit a matching `@UI.facet` that references it, so clicking a\r\nrow opens a real detail page:\r\n\r\n```cds\r\n@UI.facet: [\r\n { id: 'Main', type: #COLLECTION, label: '{Entity}', position: 10 },\r\n { id: 'DetailsRef', type: #FIELDGROUP_REFERENCE, parentId: 'Main',\r\n targetQualifier: '{group}', label: '{Section}', position: 10 }\r\n]\r\n```\r\n\r\nRules:\r\n- Group fields into logical sections (e.g. **Document** / **Account & Amounts** / **Aging**),\r\n one `@UI.fieldGroup` qualifier per section, each referenced by a `@UI.facet`.\r\n- A `@UI.facet` that references no field group — or a field group with no facet — renders an\r\n **empty** object page. Every field group used MUST have a facet pointing at it, and vice versa.\r\n- Put the aging/status field (with its criticality) on the object page too, so the detail view\r\n shows the same red/amber/green as the list.\r\n\r\n### 4.6 Access Control Design\r\n\r\nPresent the DCL source. Map the original program's AUTHORITY-CHECK to DCL aspects:\r\n\r\n```cds\r\n@EndUserText.label: 'Access Control: {entity description}'\r\n@MappingRole: true\r\ndefine role ZI_{name} {\r\n grant select on ZI_{name}\r\n where ({field}) = aspect pfcg_auth( {auth_object}, {auth_field}, ACTVT = '03' );\r\n}\r\n```\r\n\r\nIf the original program has no AUTHORITY-CHECK statements, generate a permissive\r\nDCL with a comment noting that authorization should be added:\r\n\r\n```cds\r\n// TODO: Add field-level authorization. Original program had no AUTHORITY-CHECK.\r\n@EndUserText.label: 'Access Control: {entity description}'\r\n@MappingRole: true\r\ndefine role ZI_{name} {\r\n grant select on ZI_{name}\r\n where _global_context = ' ';\r\n}\r\n```\r\n\r\n### 4.7 Design Review\r\n\r\nPresent the complete design as a summary table:\r\n\r\n```\r\nDESIGN — {base_name} ({count} objects)\r\n\r\n| # | Object Name | Type | Purpose |\r\n|---|---|---|---|\r\n| 1 | ZI_{name} | CDS View (DDLS) | Interface view — data model |\r\n| 2 | ZC_{name} | CDS View (DDLS) | Projection view — UI binding |\r\n| 3 | ZI_{name} | BDEF | Behavior definition |\r\n| 4 | ZBP_{name} | Class (CLAS) | Behavior pool |\r\n| 5 | Z{name}_SD | Service Def (SRVD) | Service definition |\r\n| 6 | Z{name}_UI_V4 | Service Binding (SRVB) | OData V4 UI binding |\r\n| 7 | ZC_{name}_MDE | Metadata Ext (DDLX) | UI annotations |\r\n| 8 | ZI_{name} | Access Control (DCLS) | Authorization |\r\n```\r\n\r\nFollowed by: \"Full CDS, BDEF, DDLX, and DCL source shown above. Review and confirm.\"\r\n\r\n### Phase Transition\r\n\r\nEnd DESIGN with: \"DESIGN complete. Ready to BUILD. Confirm to proceed.\"\r\n\r\n---\r\n\r\n## BUILD Phase\r\n\r\nCreate all modernization objects on the system. Forge Rules 7–10 apply to every write.\r\n\r\n**Delegation contract.** For the RAP path, this phase follows `/abap-rap`'s\r\ncreation order and per-object safety pattern exactly — the orchestrator does\r\nNOT re-derive that logic. The `abap-modernize` skill adds: the ASSESS / PROPOSE\r\n/ DESIGN context that produced the source code, the dedicated session transport\r\n(5.1), the SEGW-specific path (5.5), the batch/utility path (5.6), and the\r\npost-build VERIFY phase (Section 6). Where a brand-new DDIC object is needed\r\n(table, domain, data element), creation follows `/abap-data-model`'s pattern\r\n(CDS `define table` source-based, snapshot before write). When `/abap-rap`\r\nor `/abap-data-model` documentation evolves (e.g. ordering exceptions, new\r\nrelease-specific endpoints), this skill benefits automatically — do not copy\r\ntheir prose here.\r\n\r\n### 5.1 Transport Creation (Forge Rule 9)\r\n\r\nCreate a **fresh** dedicated transport before the first write:\r\n\r\n```\r\nsap_transport_create: \"Modernize {original_name} to {new_name}\"\r\n```\r\n\r\n**Do NOT pass `for_object_name` = the legacy program here.** The legacy program is\r\nread-only — modernize never writes it — so its transport lock is irrelevant to the\r\nnew objects. Passing it makes `sap_transport_create` reuse the program's owning\r\ntransport, which can route the new CDS/service stack into a request that cannot\r\nhold them (and overrides the user's \"fresh transport\" choice). The new objects do\r\nnot exist yet, so a fresh transport is the correct and only answer. Keep the\r\ndescription ASCII — no em-dashes or arrows (CTS rejects them).\r\n\r\nRecord the transport number. Assign every object created or modified during BUILD\r\nto this transport. Never use `$TMP` unless the user explicitly requests it.\r\n\r\n### 5.2 Creation Order (delegates to `/abap-rap`)\r\n\r\nThe RAP path follows `/abap-rap`'s documented creation order verbatim:\r\n\r\n```\r\nTable (if new) → CDS interface → CDS projection → BDEF → BP class → SRVD → activate SRVD → SRVB (create + publish) → DDLX (MDE) → DCL\r\n```\r\n\r\nFor the canonical sequence — including the SRVD-must-be-active-before-SRVB\r\nconstraint, the dependency rationale at each step, and the binding PUBLISH step\r\n(`sap_service_binding_publish` activates the binding AND registers the OData\r\nendpoint — verify the result is success) — see `.claude/skills/abap-rap/SKILL.md`.\r\nThis skill does not duplicate that prose; when `/abap-rap` evolves (new\r\nrelease-specific behaviour, additional ordering exceptions), the modernization\r\nBUILD path picks it up automatically.\r\n\r\nPer-path rules:\r\n- **Skip the Table step** for read-only modernization (CDS reads existing tables).\r\n- **If a brand-new table is needed**, the table creation follows\r\n `/abap-data-model`'s pattern (CDS `define table` source-based, snapshot\r\n before write). See `.claude/skills/abap-data-model/SKILL.md`.\r\n- **For ECC / older S/4** (no RAP support), skip this section and use Section\r\n 5.5 (SEGW path).\r\n- **For batch / utility modernization** (no UI/RAP target), skip to Section\r\n 5.6.\r\n\r\n### 5.3 Per-Object Safety Loop\r\n\r\nFor each object in the creation order:\r\n\r\n1. **Snapshot** (if modifying an EXISTING object): call `sap_snapshot_take` per Rule 7.\r\n For brand-new objects, no snapshot is needed (nothing to lose).\r\n2. **Create**: call `sap_create_object` for new objects. Assign to the session transport\r\n and target package from APPROVE phase.\r\n3. **Write source**: call `sap_set_source` with the designed source code from DESIGN phase.\r\n4. **Syntax check**: call `sap_syntax_check` on the object. If errors are returned,\r\n **STOP. Do not activate.** Report the errors and enter the failure handling flow (5.4).\r\n5. **Activate**: call `sap_activate` on the object.\r\n6. **Verify activation**: call `sap_inactive_objects` and confirm the object is NOT listed.\r\n If it is still listed as inactive, treat this as an activation failure.\r\n7. **Report**: output the result line for this object.\r\n\r\nReport format per object:\r\n```\r\n✓ {object_name} ({type}) — created, active\r\n```\r\n\r\n### 5.4 Mid-BUILD Failure Handling\r\n\r\nIf any object fails (syntax error, activation error, transport issue):\r\n\r\n1. **STOP immediately.** Do not continue to the next object.\r\n2. Report which objects succeeded and which failed:\r\n\r\n```\r\nBUILD stopped at object 3 of 7.\r\n ✓ ZI_FI_AGING (CDS interface) — created, active\r\n ✓ ZC_FI_AGING (CDS projection) — created, active\r\n ✗ ZI_FI_AGING (BDEF) — syntax error: line 3, unknown keyword\r\n · ZBP_FI_AGING (Class) — not started\r\n · ZFI_AGING_SD (Service def) — not started\r\n · ZFI_AGING_UI_V4 (Service bind) — not started\r\n · ZC_FI_AGING_MDE (Metadata ext) — not started\r\n```\r\n\r\n3. Offer three options:\r\n - **Fix and continue:** diagnose the error, fix the source, retry from the failed\r\n object. Take a new snapshot before each retry write (Rule 7). Re-run the full\r\n verify sequence: write → syntax check → activate → verify inactive objects.\r\n - **Skip and continue:** skip the failed object, continue with the next. Warn the\r\n user that downstream objects depending on the skipped object may also fail.\r\n - **Stop:** halt BUILD entirely. The user fixes manually and resumes later.\r\n\r\nWait for the user's choice before proceeding.\r\n\r\n### 5.5 SEGW Path (ECC Systems)\r\n\r\nSEGW project creation requires transaction SEGW in SAP GUI. There is NO ADT REST\r\nendpoint for creating SEGW projects. The skill cannot create the SEGW project\r\nautomatically.\r\n\r\nWhat the skill does for ECC modernization:\r\n\r\n```\r\nYour system is ECC {release}. The modernization requires a SEGW OData service.\r\nI'll design the complete service model and implement the code, but the SEGW\r\nproject itself needs to be created in transaction SEGW.\r\n\r\nStep 1: I'll give you the model specification (entity types, properties)\r\nStep 2: You create the project in SEGW and define the model (~15 min)\r\nStep 3: Generate DPC/MPC classes in SEGW\r\nStep 4: I'll implement all the DPC_EXT methods automatically\r\n```\r\n\r\nAfter the user creates the SEGW project and generates DPC/MPC:\r\n- Implement DPC_EXT methods via `sap_update_method` (one call per CRUD operation\r\n method). This is safer than rewriting the full class per Rule 7a.\r\n- Create the CDS view (if CDS is supported on the release) that the DPC_EXT reads from.\r\n- Apply the same safety rules to every write: syntax check, activate, verify.\r\n\r\n### 5.6 Batch/Utility Path\r\n\r\nNo new RAP objects are created. Modify existing source only.\r\n\r\n- **Method-only changes** (logic inside existing methods): use `sap_update_method`\r\n per method (Rule 7a). This is safer than a full class rewrite because it leaves\r\n untouched methods, parameters, and class definition intact.\r\n- **Structural changes** (new methods, parameter changes, visibility changes): use\r\n `sap_set_source` with a snapshot taken first (Rule 7). Required when the class\r\n definition itself must change.\r\n\r\nTake a snapshot before every write, regardless of whether using `sap_update_method`\r\nor `sap_set_source`.\r\n\r\n### Phase Transition\r\n\r\n```\r\nBUILD complete.\r\n Objects created: {n}\r\n All active: {yes/no}\r\n Transport: {transport_number}\r\n\r\nMove to VERIFY.\r\n```\r\n\r\n---\r\n\r\n## VERIFY Phase\r\n\r\nAfter all objects are built, run the full verification sequence. Do not skip any step.\r\n\r\n### 6.1 ATC Run\r\n\r\nRun ATC on all new objects. Use `sap_atc_run` per object or per package (if all\r\nobjects share the same package):\r\n\r\n```\r\nsap_atc_run: object={object_name}, type={object_type}\r\n```\r\n\r\nCollect all findings. Classify by priority:\r\n- **P1 (Error):** Must fix before declaring success. Stop and fix.\r\n- **P2 (Warning):** Report to user. Fix if straightforward; otherwise document.\r\n- **P3 (Info):** Report in summary only. No action required.\r\n\r\nIf ATC returns `ARS_W_API_STATE` findings (unreleased API usage), flag each one\r\nand include in the Clean Core section of the report.\r\n\r\n### 6.2 Syntax Check\r\n\r\nRun `sap_syntax_check` on every object created during BUILD:\r\n\r\n```\r\nsap_syntax_check: object={object_name}, type={object_type}\r\n```\r\n\r\nAll objects must pass with zero errors. Warnings are acceptable but must be reported.\r\n\r\n### 6.3 Inactive Objects Check\r\n\r\nRun `sap_inactive_objects` and confirm that NONE of the newly created objects\r\nappear in the list. If any object is still inactive, treat it as an activation\r\nfailure:\r\n\r\n1. Attempt `sap_activate` on the inactive object.\r\n2. Re-check `sap_inactive_objects`.\r\n3. If still inactive after retry, report the failure and stop.\r\n\r\n### 6.4 Clean Core API State (Optional)\r\n\r\nIf the system supports the `sap_api_state` tool (confirmed by ARS_W_API_STATE\r\nfindings during ATC or during ASSESS probes), run it on each new object:\r\n\r\n```\r\nsap_api_state: object={object_name}, type={object_type}\r\n```\r\n\r\nVerify that all new objects use only released APIs. Report any unreleased API\r\nreferences.\r\n\r\nIf `sap_api_state` is not available on the system, skip this step and report\r\n\"Clean Core: not checked (tool unavailable)\" in the summary.\r\n\r\n### 6.5 VERIFY Output\r\n\r\nReport results in this format:\r\n\r\n```\r\nVERIFY complete.\r\n Objects created: {n}\r\n Syntax: all clean\r\n ATC findings: {n} (list any P1/P2)\r\n Activation: all active\r\n Clean Core: {all released / n unreleased / not checked}\r\n\r\n Modernization successful.\r\n```\r\n\r\nIf any verification fails: stop, report the exact issue with error messages and\r\nline numbers, and offer to fix or rollback via snapshots.\r\n\r\n### Phase Transition\r\n\r\nEnd VERIFY with: \"VERIFY complete. Moving to DOCUMENT.\"\r\n\r\n---\r\n\r\n## DOCUMENT Phase\r\n\r\n### 7.1 Link Original to New (OPTIONAL — Ask User First)\r\n\r\nAsk the user before touching the original program:\r\n\r\n```\r\nAdd a modernization comment to the original program? This requires assigning it\r\nto a transport. [y/n]\r\n```\r\n\r\n**If user agrees:**\r\n\r\n1. Take snapshot of original program via `sap_snapshot_take` (Rule 7 — mandatory\r\n even for a comment addition).\r\n2. Read current source via `sap_get_source`.\r\n3. Prepend a comment block to the source:\r\n\r\n```abap\r\n*----------------------------------------------------------------------*\r\n* MODERNIZED: This program has been modernized with CSPeach.\r\n* New version: ZC_{name} (CDS view + Fiori Elements)\r\n* Service: Z{name}_UI_V4\r\n* Date: {today}\r\n* Original program preserved — decommission when ready.\r\n*----------------------------------------------------------------------*\r\n```\r\n\r\n4. Write back via `sap_set_source`. Assign to the session transport.\r\n5. Run `sap_syntax_check` on the original (Rule 10).\r\n6. Run `sap_activate` and verify activation (Rule 10).\r\n\r\n**If user declines:**\r\n\r\nRecord the cross-reference only in the summary output. Do not touch the original.\r\n\r\n### 7.2 Generate Modernization Summary\r\n\r\nProduce the final summary in this format:\r\n\r\n```\r\nModernization Summary\r\n Original: {program_name} ({type}, {description})\r\n New stack: {architecture from PROPOSE}\r\n Objects created:\r\n ZI_{name} (CDS interface view)\r\n ZC_{name} (CDS projection view)\r\n ZI_{name} (Behavior definition — auth master)\r\n ZBP_{name} (Behavior pool class)\r\n Z{name}_SD (Service definition)\r\n Z{name}_UI_V4 (Service binding — published via sap_service_binding_publish)\r\n ZC_{name}_MDE (Metadata extension)\r\n ZI_{name} (Access control — DCL)\r\n Package: {package}\r\n Transport: {transport_number}\r\n Tables migrated: {old -> new} (if CCA successors used)\r\n Business logic: {what was mapped to CDS / what needs class implementation}\r\n Authorization: {DCL created / flagged for manual review}\r\n Note: Service binding is auto-published during BUILD (sap_service_binding_publish) — the OData endpoint is live.\r\n```\r\n\r\nAdapt the object list to match the actual objects created (fewer for read-only,\r\nmore for root+child compositions). Only list objects that were actually created.\r\n\r\n### Phase Transition\r\n\r\nEnd DOCUMENT with:\r\n\"Modernization complete. Original untouched. {service_binding_name} is published — open its service-binding preview to see the Fiori Elements app.\"\r\n\r\n---\r\n\r\n## Safety Summary\r\n\r\nAll CSPeach Forge Rules that apply to `/abap-modernize`:\r\n\r\n| Rule | When It Applies | What It Requires |\r\n|---|---|---|\r\n| **Rule 6** | ASSESS phase | Read-only — no writes until BUILD |\r\n| **Rule 7** | Before modifying the original (comment addition only) | `sap_snapshot_take` before `sap_set_source` |\r\n| **Rule 7a** | Method-only changes (SEGW DPC_EXT, batch refactoring) | `sap_update_method` per method, NOT `sap_set_source` |\r\n| **Rule 8** | BUILD phase (multi-object workflow) | Present plan, wait for explicit user approval |\r\n| **Rule 9** | Session start, every object assignment | Dedicated transport for all modernization objects |\r\n| **Rule 10** | After every `sap_set_source` and `sap_activate` | Syntax check after every write, verify activation |\r\n\r\n### Original Program Handling\r\n\r\n- Never deleted. Never modified except optional comment (with snapshot).\r\n- Original continues to work after modernization.\r\n- Customer decides when to decommission.\r\n\r\n### Data Handling\r\n\r\n- No data migration. If the modernized version uses CDS views on the SAME tables\r\n as the original program, no migration is needed.\r\n- If table structure changes are required (rare), document the gap and flag it in\r\n the modernization summary.\r\n\r\n---\r\n\r\n## Out of Scope (V1)\r\n\r\nThese are explicit boundaries for V1 of `/abap-modernize`. Do NOT attempt any of\r\nthese. If the user asks for any of them, respond with: \"This is out of scope for\r\nV1 of /abap-modernize.\" followed by an explanation of what IS in scope.\r\n\r\n| Out of Scope | Why | What IS In Scope |\r\n|---|---|---|\r\n| **SAP standard code** | Never modify standard objects | Custom Z/Y/namespace objects only |\r\n| **Data migration** | Modernize architecture, not data | CDS views on existing tables; document table gaps |\r\n| **UI5 freestyle apps** | Requires custom UI5 development | Fiori Elements (annotation-driven) only |\r\n| **Fiori launchpad config** | Tile/catalog setup is manual | Service binding created; user publishes and configures tile |\r\n| **Multi-program process redesign** | Scope is single program per invocation | Each program modernized individually |\r\n| **Functional equivalence guarantee** | New architecture may behave differently | User must test; summary documents what was mapped |\r\n| **SEGW deep entity** | Complex nested entity sets need manual work | Basic CRUD entity sets with flat or single-level structure |\r\n| **RAP unmanaged with save** | Complex persistence mapping | Managed RAP (auto-persistence) and read-only RAP |\r\n\r\n---\r\n\r\n## Manifest Emission (CLI integration — v0.7)\r\n\r\nAfter all per-object modernizations complete (or are skipped/failed), emit a\r\nhidden HTML-comment manifest block as the last thing in the response. The\r\nCSPeach CLI parses this block (via\r\n`cspeach-cli/src/projects/extract-modernize.ts`) into a `modernize-result`\r\nenvelope so the file picker, `--from` chain visibility, and team-flow\r\nstatus renderer all see the run.\r\n\r\nThe block must appear EXACTLY once per response, after all human output, in\r\nthis format:\r\n\r\n```\r\n<!-- cspeach:modernize-manifest\r\nartefact: modernize-result\r\ntitle: <one-line title — e.g. \"ZDPR_PAYMENTS — modernization\">\r\ndetail_path: .cspeach/modernize/<slug>/project.json\r\nbaseline_path: <project-relative path to the source artefact — usually the\r\n cca-assessment.cspeach.json that fed this run; \"-\" if standalone>\r\nbaseline_sha256: <hex if baseline_path points at a cca-assessment; \"\" otherwise>\r\ntransport: <transport-id or \"-\">\r\napplied: <n>\r\nskipped: <n>\r\nfailed: <n>\r\npending: <n>\r\nmodernizations:\r\n item-001 | <objectName> | <objectType> | <from-architecture> | <to-architecture> | <status>\r\n item-002 | <objectName> | <objectType> | <from-architecture> | <to-architecture> | <status>\r\n ...\r\n-->\r\n```\r\n\r\nField rules:\r\n- `status` is one of: `applied` | `skipped` | `failed` | `pending`. Match the\r\n result of the IMPLEMENT/ACTIVATE phase per object.\r\n- `fromArchitecture` is a short kebab-case tag describing the legacy pattern\r\n this object had: `classic-procedural`, `bdc-against-<tcode>`,\r\n `select-from-<table>`, `dynpro-with-table-control`, etc.\r\n- `toArchitecture` is the rebuild target: `rap-behavior`, `fm-based-posting`,\r\n `cds-i_journalentryitem`, `class-based-oo`, `fiori-elements`, etc.\r\n- `detail_path` is project-relative. Resolved from the git root or the\r\n current workspace.\r\n- `baseline_path` points BACK at the upstream artefact (the cca-assessment\r\n this modernize run consumed via `--from`). When the run was standalone\r\n (no `--from`), set baseline_path to \"-\" and baseline_sha256 to \"\".\r\n\r\n## Outputs Signpost\r\n\r\nAfter the envelope is saved, name the outputs for the developer so they know\r\nwhere to look:\r\n\r\n```\r\nOutputs:\r\n - Open in the viewer -> <modernize-result.cspeach.json> (the saved envelope)\r\n - Internal detail -> .cspeach/modernize/<slug>/project.json (no need to open)\r\n - Local registry -> .cspeach/modernize/registry.json (program -> rebuilt objects)\r\n```\r\n\r\n## Post-Save Chain Prompt\r\n\r\nAfter the CLI prints `Saved: ...` for the modernize-result envelope, ask once:\r\n\r\n> \"Modernization complete. {applied_count} of {total_count} objects rebuilt.\r\n> Verify the result by re-running CCA to confirm the technical-readiness +\r\n> usage-status of the modernized objects?\"\r\n>\r\n> Options:\r\n> - Run /abap-cca on the modernized package(s) to re-classify (recommended)\r\n> - Run the generated tests via /abap-test --status if test-coverage exists\r\n> - End turn\r\n\r\nThe chain prompt is a hint, not a `--from` injection — /abap-cca takes its\r\nown input (a package or scope). Use the hint pattern:\r\n\r\n> `→ Run \\`/abap-cca on <package>\\` to re-classify the modernized objects.`\r\n",
156
+ "sha256": "3cf38a1ef9d616a5270471013a6f71eb57d20109ae4f570f3ad806849c750c22",
150
157
  "signature": "",
151
- "signedAt": "2026-09-17T20:19:05.210Z"
158
+ "signedAt": "2026-09-28T14:37:04.779Z"
152
159
  },
153
160
  "abap-performance": {
154
161
  "name": "abap-performance",
155
162
  "body": "---\r\nname: abap-performance\r\ndescription: >\r\n Analyze ABAP code for performance issues. Identifies N+1 queries, SELECT *,\r\n missing ORDER BY, unbuffered reads, unnecessary loops, missing indexes,\r\n and produces a prioritized fix list with estimated impact. For code reviews,\r\n upgrade prep, and production performance incidents.\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-performance\r\n\r\n## Purpose\r\n\r\nRead ABAP source code and identify performance problems before they hit\r\nproduction. Most SAP performance issues are caused by a handful of\r\nwell-known antipatterns. This skill detects them, explains the impact, and\r\nproposes specific fixes.\r\n\r\nUse cases:\r\n- **Code review** — check new code before transport release\r\n- **Production incident** — program runs too long, need to find the bottleneck\r\n- **Upgrade preparation** — HANA migration changes what's fast and what's slow\r\n- **Batch job optimization** — nightly job taking 8 hours, should take 20 minutes\r\n\r\n## When to Use\r\n\r\n- Use for a deep performance hunt — N+1 selects, SELECT *, loop antipatterns, missing indexes — with a prioritized fix list.\r\n- NOT for a general quality review (performance is only one of its 7 criteria) — use `/abap-review`; NOT to diagnose a TIME_OUT short dump — start at `/abap-dump`.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_get_source` | Read the program/class source |\r\n| `sap_sql_query` | Check table sizes, index availability |\r\n| `sap_usage_references` | Find related objects if needed |\r\n| `sap_atc_run` | Run performance-related ATC checks |\r\n\r\n## Inputs\r\n\r\nRequired:\r\n- Object type and name (PROG, CLAS, FUGR)\r\n\r\nOptional:\r\n- Focus area: `sql` (database only), `loop` (internal table), `all` (default)\r\n- Runtime data: if developer has SM50/ST05 trace output, paste it\r\n\r\n## Antipatterns to Detect\r\n\r\n### Critical (fix immediately)\r\n\r\n**1. SELECT inside LOOP (N+1 query)**\r\n```abap\r\n\" BAD — executes SELECT once per row in lt_orders\r\nLOOP AT lt_orders INTO ls_order.\r\n SELECT SINGLE * FROM mara WHERE matnr = ls_order-matnr.\r\nENDLOOP.\r\n```\r\nDetection: Any SELECT statement inside a LOOP/DO/WHILE block.\r\nFix: Extract keys into an internal table, use FOR ALL ENTRIES or JOIN.\r\nImpact: 10,000 orders = 10,000 database roundtrips → 1 roundtrip.\r\n\r\n**2. SELECT * (read all columns)**\r\n```abap\r\n\" BAD — reads 200+ fields when only 3 are needed\r\nSELECT * FROM bseg INTO TABLE lt_bseg WHERE bukrs = lv_bukrs.\r\n```\r\nDetection: `SELECT *` or `SELECT SINGLE *` on any table.\r\nFix: List only the fields actually used downstream.\r\nImpact: Reduces network transfer, enables index-only scans on HANA.\r\n\r\n**3. Missing WHERE clause on large table**\r\n```abap\r\n\" BAD — full table scan on million-row table\r\nSELECT * FROM acdoca INTO TABLE lt_data.\r\n```\r\nDetection: SELECT on known large tables (ACDOCA, BSEG, MSEG, VBAP, EKPO,\r\nLIPS, MATDOC) without restrictive WHERE clause.\r\nFix: Add key field filters.\r\nImpact: Full table scan vs index lookup — minutes vs milliseconds.\r\n\r\n**4. FOR ALL ENTRIES on empty table**\r\n```abap\r\n\" BAD — returns ALL rows when lt_keys is empty\r\nSELECT * FROM mara INTO TABLE lt_result\r\n FOR ALL ENTRIES IN lt_keys WHERE matnr = lt_keys-matnr.\r\n```\r\nDetection: FOR ALL ENTRIES without a preceding IS NOT INITIAL check.\r\nFix: Add `IF lt_keys IS NOT INITIAL` guard.\r\nImpact: Accidental full table dump — can crash the system.\r\n\r\n### High (fix before go-live)\r\n\r\n**5. Nested LOOP without SORTED/HASHED table**\r\n```abap\r\n\" BAD — O(n*m) when it should be O(n*log(m))\r\nLOOP AT lt_header INTO ls_header.\r\n LOOP AT lt_item INTO ls_item WHERE vbeln = ls_header-vbeln.\r\n```\r\nDetection: LOOP AT ... WHERE on a STANDARD TABLE inside another LOOP.\r\nFix: Declare inner table as SORTED TABLE WITH NON-UNIQUE KEY or use\r\nREAD TABLE ... BINARY SEARCH.\r\nImpact: 10,000 x 50,000 = 500M comparisons → 10,000 x 17 = 170K.\r\n\r\n**6. SELECT in IF/CASE branches (hidden N+1)**\r\n```abap\r\n\" BAD — looks innocent but executes per row\r\nIF lv_type = 'A'.\r\n SELECT SINGLE * FROM table_a WHERE key = lv_key.\r\nELSEIF lv_type = 'B'.\r\n SELECT SINGLE * FROM table_b WHERE key = lv_key.\r\n```\r\nDetection: SELECT inside IF/CASE inside LOOP.\r\nFix: Batch-read all types before the loop using type-specific key tables.\r\n\r\n**7. MODIFY/INSERT/DELETE inside LOOP**\r\n```abap\r\n\" BAD — one DB operation per row\r\nLOOP AT lt_data INTO ls_data.\r\n MODIFY ztable FROM ls_data.\r\nENDLOOP.\r\n```\r\nDetection: MODIFY/INSERT/UPDATE/DELETE FROM inside LOOP.\r\nFix: Use `MODIFY ztable FROM TABLE lt_data` (array operation).\r\nImpact: 1 DB call instead of N.\r\n\r\n### Medium (optimize when possible)\r\n\r\n**8. CONCATENATE in LOOP instead of string template**\r\n```abap\r\n\" SLOW — creates new string object each iteration\r\nLOOP AT lt_data INTO ls_data.\r\n CONCATENATE lv_result ls_data-field INTO lv_result SEPARATED BY ','.\r\nENDLOOP.\r\n```\r\nFix: Use string templates or REDUCE.\r\n\r\n**9. Excessive DESCRIBE TABLE / LINES( )**\r\nDetection: `lines( )` or `DESCRIBE TABLE ... LINES` called repeatedly on\r\nsame table inside a loop.\r\nFix: Cache the count in a variable.\r\n\r\n**10. Missing buffer for customizing reads**\r\nDetection: SELECT SINGLE from customizing tables (T001, T005, TCURR, etc.)\r\ninside processing loops.\r\nFix: Read once into a local table, READ TABLE with BINARY SEARCH.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Read Source\r\n```\r\nsap_get_source (objectType, objectName)\r\n```\r\n\r\n### Step 2 — Analyze\r\nScan the source for each antipattern. For each finding:\r\n- Line number\r\n- Pattern name (N+1, SELECT *, etc.)\r\n- Severity (Critical / High / Medium)\r\n- The actual code at that line\r\n- Why it's a problem (specific to this case)\r\n\r\n### Step 3 — Check Table Sizes (for context)\r\nFor tables referenced in SELECT statements, check approximate size:\r\n```\r\nsap_sql_query: SELECT COUNT(*) FROM {table}\r\n```\r\nThis helps prioritize — SELECT * on a 100-row config table is low impact.\r\nSELECT * on ACDOCA (millions of rows) is critical.\r\n\r\n### Step 4 — Produce Report\r\n\r\n```\r\n## Performance Analysis: {OBJECT_NAME}\r\n\r\n### Summary\r\nLines analyzed: {N}\r\nIssues found: {critical} Critical, {high} High, {medium} Medium\r\n\r\n### Critical Issues\r\n\r\n#### 1. SELECT inside LOOP (Line {N})\r\nSeverity: CRITICAL\r\nTable: {table} (~{row_count} rows)\r\nCurrent code:\r\n {code}\r\nFix:\r\n {proposed fix}\r\nImpact: {estimated improvement}\r\n\r\n### High Issues\r\n...\r\n\r\n### Recommendations\r\n1. Fix critical issues before go-live\r\n2. {specific recommendations}\r\n\r\n### Estimated Impact\r\nCurrent: ~{N} database roundtrips per execution\r\nAfter fixes: ~{M} database roundtrips\r\nImprovement: {percentage}%\r\n```\r\n\r\n## Guardrails\r\n\r\n- Read-only skill. Never write or modify anything.\r\n- Don't report false positives on small tables. A SELECT SINGLE on T001\r\n inside a loop of 5 items is not worth flagging as critical.\r\n- If you can't determine table size (SQL query fails), note it and\r\n still report the finding with \"unknown table size — verify manually.\"\r\n- Don't recommend FOR ALL ENTRIES when a JOIN would be cleaner.\r\n FOR ALL ENTRIES is a fallback, not a best practice.\r\n- HANA vs non-HANA context matters: on HANA, secondary indexes are less\r\n relevant. On non-HANA, they're critical. If system type is known, adjust.\r\n",
156
163
  "sha256": "f9abcaf19d387627aed1bd76856c78dd4b1738bfddfdaf77756779eb886d7c6d",
157
164
  "signature": "",
158
- "signedAt": "2026-09-17T20:19:05.284Z"
165
+ "signedAt": "2026-09-28T14:37:04.814Z"
159
166
  },
160
167
  "abap-plan": {
161
168
  "name": "abap-plan",
162
169
  "body": "---\r\nname: abap-plan\r\ndescription: \"Plan and execute multi-session SAP projects — phase envelope holds state across sessions, each session runs exactly ONE phase with bounded context. Create from a goal or a spec-gap envelope; resume with --resume @plan-file. Also revises an existing live RAP stack — add a field/column, a validation/determination/action stub, or surface an action button — planning only the changed layers.\"\r\nversion: \"1.2\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-plan\r\n\r\n## Purpose\r\n\r\nRun SAP projects that are too big for one session. This skill produces and maintains a **plan envelope** (`*.cspeach.json`) that holds project state across sessions: an ordered list of phases, each with its own entry criteria, context manifest, delegate skill, and exit gate.\r\n\r\nThe execution model is the whole point:\r\n\r\n> **One phase at a time, bounded context.** Each `--resume` turn loads ONLY the next phase's declared context, executes it by delegating to an existing build skill, and records the result in the envelope. Phase end = turn end: the turn finishes at the manifest block, and the CLI itself offers the user the next phase — on consent it re-enters `--resume` with the exact saved file in a fresh, reset context. No transcript replay, no context pollution, ever. The envelope is the only thing that survives between phases.\r\n\r\nThis skill orchestrates — it does not design architectures (that's `/abap-design`, called per phase) and it does not generate code (that's `/abap-generate`, `/abap-rap`, `/abap-data-model`, called per phase).\r\n\r\n## When to Use\r\n\r\n- A development goal spans multiple components or layers and would blow a single session's context (multi-entity RAP stacks, interfaces with staging + extract + delivery, module-sized builds)\r\n- After `/abap-spec-gap` has been answered — the plan consumes its envelope via `--from`\r\n- When work must pause and resume across days without re-pasting context\r\n- When different phases need different bounded contexts (DDIC phase doesn't need RAP rules; behavior phase doesn't need the full spec)\r\n\r\n**Do NOT use for single-object tasks.** A single report, class, or small RAP stack fits one session — go straight to `/abap-design` then the build skill. A plan with one phase is overhead, not value.\r\n\r\n## Inputs Expected\r\n\r\n**Mode 1 — create plan.** One of:\r\n1. A project goal (1–5 sentences) plus `--from @<specgap>.cspeach.json` — the answered spec-gap envelope (preferred)\r\n2. A project goal alone — acceptable ONLY if crisp; vague goals are rejected (see Required Behavior 1)\r\n- If the source requirement is a `.pdf`/`.docx`, read it with `read_document(path)` first.\r\n\r\n**Mode 2 — resume plan.**\r\n- `--resume @<plan>.cspeach.json` — the CLI loads and validates the envelope before dispatch; you receive the plan state in context\r\n\r\n**System context is auto-provided when CSPeach is connected to a SAP system.** A `<session_context>` block exposes `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>`. Treat it as ground truth — never ask about facts present there.\r\n\r\n---\r\n\r\n## Required Behavior — Mode 1: Create Plan\r\n\r\n0. **Read `<session_context>` FIRST.** Platform and release determine which layers and delegate skills are even possible (no RAP phases on ECC < 7.50; no SEGW phases on public cloud). Record platform/release in `project.source`.\r\n\r\n1. **Gate on input quality.** If no spec-gap envelope was provided AND the goal leaves blocker-grade ambiguity (undefined scope, unknown trigger, unknown data source), do NOT invent a plan. Tell the user to run `/abap-spec-gap \"<goal>\"` first, answer the blockers, then re-run `/abap-plan --from @<that file>`. End the turn. A plan seeded on guesses multiplies the guess across every phase.\r\n\r\n2. **Consume the spec-gap envelope when provided.** Use answered gaps as facts. Unanswered `[BLOCKER]` items block plan creation — same rule as 1. Unanswered `[IMPORTANT]` items become `approval: true` flags or exit-gate conditions on the affected phases, not silent assumptions. Record the consumed file in `from.specGap` (path + sha256 if known).\r\n\r\n3. **Decompose into components, then layers.** A component is a cohesive business object or interface (e.g. \"approval BO\", \"extract file builder\"). Within each component, seed phases bottom-up by layer:\r\n\r\n | Layer | Typical content | Default `delegateTo` |\r\n |---|---|---|\r\n | `types` | Domains, data elements | `abap-data-model` |\r\n | `persistence` | Tables, structures | `abap-data-model` |\r\n | `data-model` | CDS interface + projection views | `abap-rap` (RAP component) / `abap-generate` (standalone CDS) |\r\n | `behavior` | BDEF + behavior pool | `abap-rap` (scaffold) / `abap-eml` (handler logic) |\r\n | `service` | SRVD + SRVB + publish | `abap-rap` |\r\n | `orchestration` | Jobs, facades, report shells | `abap-generate` |\r\n | `integration` | File/RFC/OData consumers | `abap-generate` (`abap-segw` for ECC OData) |\r\n | `ui` | Fiori frontend (freestyle or FE shell) | `abap-fiori-build` |\r\n\r\n In greenfield, a UI layer in the design seeds `ui.build` (+ `ui.deploy` unless the user opts out) — see the UI-phase guardrail for the exact phase shapes and the `ui` map fields.\r\n\r\n A design phase (`delegateTo: abap-design`, `layer` of the component's dominant layer, `component: \"project\"` for the cross-component design) is seeded FIRST when the object list is not already settled by the spec-gap answers. A test phase (`delegateTo: abap-test`) is seeded per component whose behavior layer exists.\r\n\r\n **Give every phase a short human `title`** — one or two words a non-ABAP reader can follow on the tracker board: \"Design\", \"Tables\", \"CDS views\", \"Behavior\", \"Service\", \"Tests\", \"Docs\". The machine `id` (`c1.persistence`) stays authoritative in the file, `entryCriteria`, and manifests; the `title` is what the CLI tracker renders.\r\n\r\n **The design phase MUST settle infrastructure, not just names.** Its decision register (`work.notes`) records as explicit decisions: the target **package** — VALIDATED TO EXIST (when connected, check it during seeding via `sap_search_object` / `sap_object_structure`; otherwise the design phase performs the check and blocks if missing — a nonexistent package dead-ends every later phase) — and the **transport strategy** (an existing transport number, or \"create a session transport in the first build phase\" per Rule 9). The design phase's `exitGate` must include \"package validated to exist\". If no design phase is seeded (object list settled by spec-gap answers), put the package + transport decisions in the FIRST build phase's entry instead — never leave them to be asked mid-build.\r\n\r\n **`types` layer — do NOT seed a standalone custom-domain/DE phase on systems that cannot create them.** MCP mode cannot write custom domain/data-element content (they are XML-form ADT objects; the shell creates but the content PUT does not exist — see `abap-data-model`). On modern S/4HANA via ADT — the default — a `types` phase that tries to \"create domains + DEs\" will leave empty inactive shells and **block**. Therefore:\r\n - **Default: fold `types` into `persistence`.** Do not seed a separate `c*.types` phase. The table fields are typed directly in the `define table` source using standard SAP data elements (`equnr`, `werks_d`, `matnr`, …) or built-ins (`abap.char(n)`, …). Value help that a custom domain would have provided comes from a **check-table foreign key**. The `persistence` phase records any custom-DDIC simplification in `work.notes`.\r\n - **Only seed a standalone `types` phase** when the system genuinely supports custom DOMA/DTEL creation in this run (paste-mode handoff for manual SE11, or a future `sap_create_domain`/`sap_create_data_element`). If you seed one, its `exitGate` must be checkable against what is actually built — e.g. \"Fields typed with standard DEs/built-ins; custom-DDIC substitutions recorded in work.notes\" — **never** the unmeetable \"Domains + DEs active\".\r\n\r\n **`persistence` layer — never gate on SM30/SE54 maintenance-dialog generation.** The SE54 table-maintenance generator is GUI-only; there is no ADT endpoint (see `abap-data-model`). A `persistence` phase `exitGate` must NOT demand \"SM30 maintenance view generated and tested\". Instead: create the config table `@AbapCatalog.dataMaintenance: #ALLOWED` (SE16N-maintainable), seed starter rows via an initial-load report if needed, and make the exit gate checkable — e.g. \"Table active (#ALLOWED, SE16N-maintainable), ATC clean, transport recorded; SM30/SE54 generation flagged as manual GUI step in work.notes\". The same applies to any other GUI-only operation a design assumes (SE54 view clusters, SM34, transaction codes).\r\n\r\n4. **Size phases for one session each.** A phase should be ~1 focused build session (roughly one `/abap-design` run, or one DDIC batch, or one behavior pool). If a phase's object list exceeds ~8 objects, split it. If a plan exceeds ~12 phases, the goal is a program, not a project — tell the user to split the goal itself.\r\n\r\n5. **Wire `entryCriteria` bottom-up.** Each phase lists the phase ids that must be `validated` before it may run. Reference ONLY phases that appear earlier in the list — the schema validator rejects forward references. Cross-component dependencies are allowed (c2's integration phase may require c1's service phase).\r\n\r\n6. **Declare each phase's context manifest (coarse v1).** This is what the resume session will load — nothing else:\r\n - `reads`: envelope fields (`project.goal`, `phases[<id>].work`), spec-gap item ids, or SAP object names the phase needs\r\n - `rules`: rule FILE paths needed by that layer — `.claude/rules/abap-conventions.md` (always), `.claude/rules/rap-patterns.md` (data-model/behavior/service only), `.claude/rules/safety.md` (any phase that writes)\r\n - `writesBack`: envelope fields the phase must update — at minimum `phases[<id>].work`\r\n Keep manifests minimal. Every entry costs tokens in the resume session.\r\n\r\n7. **Set `approval: true` on every phase that creates a new persistence layer** (tables, structures) and on any phase the spec-gap answers flagged as commercially or functionally sensitive. Rule 8: these phases prompt the user before executing.\r\n\r\n7a. **Declare `writes` on every phase.** `writes` tells guarded mode whether the phase touches the SAP system — it decides which phases may auto-chain and which stop for per-phase consent. Set `writes: false` ONLY for phases that touch no SAP object (pure design/analysis/decision phases); set `writes: true` for phases that create or modify SAP objects; OMIT when unsure — an omitted field is treated as writing (the safe default). Worked example — a design phase vs. a build phase:\r\n\r\n ```json\r\n {\r\n \"id\": \"c1.design\",\r\n \"title\": \"Design\",\r\n \"component\": \"project\",\r\n \"layer\": \"data-model\",\r\n \"entryCriteria\": [],\r\n \"delegateTo\": \"abap-design\",\r\n \"manifest\": {\r\n \"reads\": [\"project.goal\"],\r\n \"rules\": [\".claude/rules/abap-conventions.md\"],\r\n \"writesBack\": [\"phases[c1.design].work\"]\r\n },\r\n \"exitGate\": \"Object list settled, package validated to exist, decisions in work.notes\",\r\n \"writes\": false\r\n },\r\n {\r\n \"id\": \"c1.persistence\",\r\n \"title\": \"Tables\",\r\n \"component\": \"c1\",\r\n \"layer\": \"persistence\",\r\n \"entryCriteria\": [\"c1.design\"],\r\n \"delegateTo\": \"abap-data-model\",\r\n \"manifest\": {\r\n \"reads\": [\"phases[c1.design].work\"],\r\n \"rules\": [\".claude/rules/abap-conventions.md\", \".claude/rules/safety.md\"],\r\n \"writesBack\": [\"phases[c1.persistence].work\"]\r\n },\r\n \"exitGate\": \"Table active, ATC clean\",\r\n \"approval\": true,\r\n \"writes\": true\r\n }\r\n ```\r\n\r\n The build phase may equivalently omit `writes` — omission is treated as writing. Only `writes: false` changes behavior, so author it deliberately and never on a phase whose delegate might write.\r\n\r\n8. **Set each phase's `exitGate`** as a one-line, checkable success criterion (\"Table active, ATC clean\", \"SRVB published, OData GET returns 200\"). The resume session marks the phase `validated` only when the gate holds.\r\n\r\n **Authorization is an explicit exit-gate line — never a silent deferral.** When the requirements include an authorization rule (and on every RAP stack regardless — rap-patterns.md makes DCL mandatory on interface views; `#NOT_REQUIRED` is acceptable only on pure value-help views), the relevant phase's `exitGate` carries it explicitly: e.g. \"DCL access control active on ZI_* views; PFCG role requirement recorded in work.notes\". The ONLY way a plan ships without that auth work is a visible decision: the user explicitly waives it during the resume turn → `validated-with-waiver` + the waived item and reason in `work.notes`. A phase that leaves views `#NOT_REQUIRED` with no DCL and no recorded waiver has NOT met its gate.\r\n\r\n9. **Set `summary`**: `total` = number of phases, `validated: 0`, `blocked: 0`, `next` = first phase id.\r\n\r\n10. **Render the plan** (Output Structure below) and end with the manifest block. The harness's save prompt persists it as the plan envelope. Do NOT begin executing any phase in the create session.\r\n\r\n## Required Behavior — Mode 1: Revision Plan\r\n\r\nMode 1-REVISION is a second create path, triggered when the user's request is a **change to an existing live RAP stack** (\"add field X and show it\", \"add an Approve button\") rather than a new build. It shares the Mode-1 slot (planner judgment distinguishes create vs. revision), emits the same manifest envelope, and is consumed by the unchanged Mode-2 executor exactly as any other plan.\r\n\r\n### When to use Mode 1-REVISION (not greenfield create)\r\n\r\nThe signal is a reference to something that already exists: \"my maintenance-request app\", \"the ZC_Course projection\", \"the existing approval BO\". If the user says \"build\", \"create\", or \"new\" without referencing a live stack, use greenfield create.\r\n\r\n**Classify the change into one of 4 types** — this is planner judgment, the same interpretive step used to decompose a greenfield goal:\r\n\r\n| # | Change type | Signal words | Layers touched |\r\n|---|---|---|---|\r\n| 1 | **Add field + column** | \"add field X\", \"show new column X\" | `persistence` → `data-model` (interface CDS, projection CDS, DDLX/inline annotation) |\r\n| 2 | **Expose existing field** | \"show X in the list\", \"promote X to a column\" | `data-model` only (field already exists; column annotation only) |\r\n| 3 | **Validation / determination stub** | \"add a ValidateStatus\", \"add DetermineAmount\" | `behavior` only (BDEF declaration + empty CCIMP method) |\r\n| 4 | **Action → button** | \"add an Approve button\", \"surface the Cancel action\" | `behavior` (base BDEF action) → `behavior` (projection BDEF `use action`) → `data-model` (MDE `#FOR_ACTION` toolbar annotation) |\r\n\r\n**Gate on ambiguity before seeding anything:**\r\n- Request too vague to classify (scope unknown, trigger unknown) → route to `/abap-spec-gap` first. End the turn.\r\n- Change implies a structural addition beyond the 4 types (new composition entity, new child BO, new service binding, draft enablement) → these are greenfield additions; route to `/abap-rap`. End the turn.\r\n\r\n### Discovery — anchor → live stack map\r\n\r\nFrom the **anchor** (the service binding name, or the root CDS entity name if the user provides it), traverse the live system deterministically using `sap_object_structure` + `sap_get_source`. Each artifact names its dependencies in source, so the traversal resolves without guessing:\r\n\r\n```\r\nbinding (SRVB) → service def (SRVD, via expose)\r\n → projection view (DDLS, as projection on ZI_)\r\n → interface view (DDLS, select from)\r\n → table (TABL, persistent table / define table)\r\n → BDEF (BDEF, for ZI_)\r\n → behavior pool class (CLAS, implementation in class)\r\n → DDLX metadata extension (DDLX, for ZC_)\r\n```\r\n\r\nFrom this traversal build the top-level `revision` map that travels in the envelope:\r\n\r\n```json\r\n\"revision\": {\r\n \"anchor\": \"ZUI_TCRSCOURSE_O4\",\r\n \"uiKind\": \"fiori-elements\",\r\n \"binding\": \"ZUI_TCRSCOURSE_O4\",\r\n \"layers\": {\r\n \"table\": \"ZTCRS_COURSE\",\r\n \"interfaceView\": \"ZI_TCRSCourse\",\r\n \"projectionView\": \"ZC_TCRSCourse\",\r\n \"bdef\": \"ZI_TCRSCourse\",\r\n \"behaviorPool\": \"ZBP_TCRSCourse\",\r\n \"mde\": \"ZC_TCRSCourse\",\r\n \"serviceDef\": \"ZTCRSCourse_SD\"\r\n }\r\n}\r\n```\r\n\r\nSet `uiKind`:\r\n- `fiori-elements` — a published service with `@UI` annotations, no hand-authored freestyle app in scope\r\n- `freestyle` — a freestyle SAPUI5 app fronts the service (detected from app metadata or user confirmation)\r\n- `none` — backend-only service (API consumer, no UI)\r\n\r\n**When `uiKind` resolves to `freestyle`, capture the app directory here.** A freestyle app is an arbitrary local folder on the developer's laptop consuming an OData URL — it is **not** discoverable from the binding or the backend object graph (discovery walks only the SRVB→…→TABL backend graph). So ASK the developer for the app directory through the same `ask_question` mechanism as the other revision questions, in this classification step alongside `uiKind`, and store it as **`revision.app.dir`**. `revision.app.dir` is **mandatory for a freestyle revision** — the schema enforces it (required whenever `revision.uiKind === 'freestyle'`), so the `ui` phase has an `appDir` to amend against. Do not autodiscover it; do not defer it.\r\n\r\n**Discovery contract — block-and-report (do not guess) in three situations:**\r\n\r\n1. The binding exposes **more than one projection** over the interface view — ambiguous which projection to edit. Report all projections found and ask the user to name the one to change.\r\n2. The change targets a **child or parent entity** in a composition (`to parent` / `composition of`) — v1 maps a single table/interface/projection only. Multi-entity revision is out of scope; route to `/abap-rap` or `/abap-spec-gap`.\r\n3. The projection's `@UI` cannot be resolved to **either** a DDLX or inline projection annotations — the annotation placement is ambiguous. Report what was found and block.\r\n\r\n`revision.layers` records exactly ONE entity's object names (or `null` for absent layers). Never guess missing names.\r\n\r\n### Seed only changed layers — bottom-up, one phase per layer\r\n\r\nAfter classifying and discovering, seed phases **only for the layers the change actually touches** (the §6.5 \"only changed layers\" guarantee). An untouched layer produces **no phase**. The structural proof of this guarantee is the emitted plan's phase list — a change-type #2 (expose existing field) emits no `persistence` or `behavior` phase.\r\n\r\n**Phases are seeded bottom-up**, each `delegateTo: abap-extend-model`, with the specific `extend_model_insert` kind resolved from the change type and the discovered object name in `manifest.reads`. Phase `id`s disambiguate same-layer phases (e.g. `c1.data-model-interface`, `c1.data-model-projection`, `c1.data-model-annotation`).\r\n\r\n**Phase templates by change type:**\r\n\r\n| Change type | Phase (layer · `delegateTo` · `extend_model_insert` kind) |\r\n|---|---|\r\n| 1 — Add field + column | `persistence` · extend-model · `table-field` → `data-model` · extend-model · `cds-field` (interface) → `data-model` · extend-model · `cds-field` / `cds-lineitem` (projection) → `data-model` · extend-model · `ddlx-lineitem` or `cds-lineitem` (annotation; DDLX present → `ddlx-lineitem`, inline `@UI` → `cds-lineitem`). FE-Elements: complete. Freestyle: + `ui` phase (C2 seam). |\r\n| 2 — Expose existing field | `data-model` · extend-model · `cds-lineitem` (promote annotation). (+ `ui` if freestyle.) |\r\n| 3 — Validation / determination stub | `behavior` · extend-model · `bdef-stub` (base BDEF + CCIMP). Backend-only; no UI. |\r\n| 4 — Action → button | `behavior` · extend-model · `bdef-stub` (action on base BDEF) → `behavior` · extend-model · `projection-use` (expose action on projection BDEF) → `data-model` · extend-model · `mde-action-button` (FE toolbar annotation on MDE). FE-Elements: complete. Freestyle: + `ui` phase (C2 seam). |\r\n\r\n**Layer naming is strictly enum-valid.** Use only the frozen `PLAN_LAYERS` values:\r\n- CDS edits, DDLX edits, MDE annotation edits → `data-model`\r\n- BDEF edits (base or projection) → `behavior`\r\n- Table field adds → `persistence`\r\n\r\nDo **NOT** invent layer values like `ui-annotation`, `projection`, or `mde`. These are not in `PLAN_LAYERS` and will fail schema validation.\r\n\r\n**No standalone `republish` phase.** \"Republish the binding if the exposed surface changed\" is **not a separate phase** — fold it into the **exit gate of the last `data-model` (annotation) phase**. `abap-extend-model` already owns republish-if-needed (Step 7c) and performs it inside that phase's execution. There is no `republish` delegate. The exit gate for the last `data-model` phase therefore reads e.g.: \"Column active in DDLX; binding republished if the exposed field-set changed; app re-renders on next metadata fetch.\"\r\n\r\n**`mde-action-button` anchor rule.** When seeding change-type #4's last `data-model` phase, the `anchorField` (the existing projection field the `#FOR_ACTION` annotation attaches above) must be a **stable, already-exposed, non-`@UI.hidden` field**. Pick it from the projection's current field list (read during discovery). Do not invent a field name. The button is a **List-Report toolbar button only** (`@UI.lineItem #FOR_ACTION`); the Object-Page button (`@UI.identification #FOR_ACTION`) is deferred.\r\n\r\n**`approval: true` on persistence phases.** Any phase that adds a persistence layer (table field add, `persistence` layer phase) carries `approval: true`. Rule 8 applies.\r\n\r\n**`manifest.reads` entries are model-interpreted hints.** Entries such as `\"revision.layers.table\"` or `\"revision.layers.interfaceView\"` are hints the model uses to locate the object — they are not machine-dereferenced paths. Put the actual object name in the hint (e.g. `\"ZTCRS_COURSE\"`), or use the `revision.layers.*` key when the exact name has been discovered and recorded in `revision`.\r\n\r\n### Multi-phase failure is forward-only\r\n\r\nA revision of change-type #1 spans several phases across multiple turns. Each `abap-extend-model` phase activates **and rolls back its own edit independently** — if a CDS phase fails, it restores its own snapshot (the B1/B2a chain discipline). There is **no cross-phase rollback**: if the third phase of a four-phase plan fails, the first two phases remain `validated` with their changes live in the system.\r\n\r\nThis is the contract: \"add a field\" feels like one operation but is a forward-only multi-phase sequence. A half-applied chain (DB column live, projection not yet exposing it) is a valid intermediate state the user resumes from. State this plainly in the plan's opening if the risk is non-obvious to the user.\r\n\r\n### Emit the revision manifest\r\n\r\nEmit the **full manifest shape** (Mode-1 create), adding two fields:\r\n\r\n1. **`project.mode: \"revision\"`** — the only marker the executor uses; create plans stay `\"create\"` (or absent, for back-compat).\r\n2. **`revision`** — the top-level key (not `project.target`) carrying the discovered live stack map (shape above).\r\n\r\nThe phases array carries only the changed-layer phases, each a normal phase the Mode-2 executor processes without modification. Mode-2 resume is **unchanged** — it sees a `delegateTo: abap-extend-model` phase exactly as it would a `delegateTo: abap-data-model` phase in a create plan.\r\n\r\n### FE-Elements vs. freestyle seam\r\n\r\n`revision.uiKind` is the decision point:\r\n- `fiori-elements` or `none` → **no `ui` phase**. Backend phases (with republish folded into the last annotation phase's exit gate) are the whole plan. The FE app re-renders from the updated OData metadata.\r\n- `freestyle` → seed a `ui` phase `delegateTo: abap-fiori-build`. On resume, that phase **delegates for real to `/abap-fiori-build` amend mode** (§6 of the C2 design): it hands the amend skill the change request assembled from the envelope — the **app location** (`revision.app.dir`), the **binding** (`revision.binding` — a revision often has *no* service phase, so read `revision.binding`, **never** the create-mode service-phase `work.binding`), the **bound entity**, and the **specific UI change** (e.g. \"surface new field `Priority` as a column\", \"add an `Approve` button calling the `Approve` action\"). `abap-fiori-build` then runs its amend workflow against that app. **Cross-session re-confirm guard:** because `revision.app.dir` is captured at plan time but consumed later at `ui`-phase resume (possibly a different session, after the backend phases ran), the `ui` phase lets the amend skill's A0 detect run first; **if A0 cannot locate the app at the recorded `revision.app.dir` (absent, or non-empty-without-a-`webapp/manifest.json`), re-confirm/re-capture the path from the developer** rather than mis-detecting a create or stopping cryptically. **Invariant retained:** a freestyle app that gets no `ui` phase is a planning error, not a deferral — C2 makes the phase actionable, it does not make it optional.\r\n\r\n---\r\n\r\n## Required Behavior — Mode 2: Resume Plan\r\n\r\n0. **The envelope in context is the source of truth.** Do not ask the user what happened in previous sessions. Do not ask them to paste prior output. Everything you may rely on is in the envelope; if it isn't there, a previous session failed to write it back — say so.\r\n\r\n1. **Pick the next phase.** First phase in `phases[]` order whose status is `todo` and whose `entryCriteria` are all `validated` (or `validated-with-waiver` — a waived gate satisfies the dependency).\r\n - If every phase is `validated` / `validated-with-waiver` → report plan complete, render the final summary, exit. **A completed backend stack with no `ui` phase must chain the story forward, not end silently at the service binding:** include in your final summary the line \"Backend complete. Next: `/abap-fiori-build` — point it at `<binding>`\" with the EXACT published binding name from the service phase's `work.binding` (or `work.generated`). Also recommend `/abap-preflight` on the produced transport(s). This chain-out line is the LEGACY path for envelopes with no `ui` phases (pre-Track-1 plans, or the user declined a UI). A plan created with `ui` phases completes only when they are `validated`/`validated-with-waiver` like any other phase — the final summary then shows `App deployed: <work.deployedUrl>` instead of a hand-off hint.\r\n - If no phase is eligible but `blocked` phases exist → report which phases are blocked and why (from history), propose the unblock path, exit. Do NOT execute anything.\r\n\r\n2. **If the phase has `approval: true`** — present the phase (id, layer, objects expected, exit gate, transport impact) via `ask_question` and wait for explicit confirmation. On decline: leave status `todo`, append a history note, exit.\r\n\r\n3. **Load ONLY the phase's manifest.** Read the declared rule files and the declared envelope fields. Do not read other phases' manifests, the full spec-gap envelope, or rules not listed. Bounded context is the product — widening it silently defeats the skill.\r\n\r\n4. **Ground on the live system (INV-2).** Before building, read the ACTIVATED artefacts of the `entryCriteria` phases via SAP MCP tools (`sap_get_source`, `sap_object_structure`) — the objects listed in those phases' `work.generated`. Build against what is actually active in the system, not against the plan's sketch of it. If a prerequisite object is missing or inactive, mark this phase `blocked` with that finding and exit.\r\n\r\n4a. **Infrastructure blockers get an ESCALATION question, not a dead end.** When the blocker is a missing BASE object the plan assumed (package, transport, number range — things \"the system\" sometimes refuses to create through the API), do NOT just mark blocked and exit. Present an `ask_question` with these concrete paths:\r\n - **Create it for me now** — attempt creation via the SAP tools as part of this phase (package: `sap_create_object` DEVC; transport: `sap_transport_create`), recording everything in `work`. If an attempt fails with a system error, retry ONCE with an adjusted approach, then escalate again — never loop silently.\r\n - **I'll create it manually** — give the user the EXACT manual recipe (transaction, object name, settings — e.g. \"SE21: create package ZPM_DOWNTIME, transport layer ZDEV, then re-run --resume\") and exit with status `blocked` + the recipe in `work.unblockPath`. On the next resume, grounding re-checks and proceeds.\r\n - **Keep trying** — only offered when there IS a meaningfully different approach left to try; name it in the option text.\r\n - **Abort the phase** — blocked, full findings recorded.\r\n Whatever the user picks, the result (created objects, transport numbers, manual recipe) MUST land in the phase's `work` in the manifest block — out-of-band infrastructure that the envelope doesn't know about poisons every later phase.\r\n\r\n5. **Execute by delegating.** Run the phase's `delegateTo` skill with the bounded context. The delegate skill owns its own Forge Rule compliance (Rule 7 snapshots, Rule 9 transport assignment, Rule 10 verify-after-write). Record the session transport in `work.transport`.\r\n\r\n6. **Check the exit gate.** Activation verified (`sap_inactive_objects` clean for the phase's objects), plus whatever the `exitGate` line demands.\r\n - **Gate holds** → status `validated`. Fill `work.generated` (object names), `work.transport`, `work.snapshot` (if taken). A **service** phase additionally records the published binding name in `work.binding` (e.g. `ZUI_MAINTREQ_O4`) — the plan-complete hand-off to `/abap-fiori-build` depends on it. A **ui.build** phase additionally records `work.app.dir` + `work.service{url,path,version}`, and a **ui.deploy** phase records `work.deployedUrl` — the plan-complete summary depends on them.\r\n - **Gate cannot hold but the user EXPLICITLY waives it** (e.g. \"skip the test gate — the AUnit runner is broken, continue\") → status `validated-with-waiver`. Record WHAT was waived and the user's stated reason in `work.notes`. This status is ONLY valid on an explicit user waiver given this turn — a gate you decided to skip yourself is `blocked`, and an unverified gate is NEVER `validated`.\r\n - **Gate fails after a reasonable fix attempt** → status `blocked`. Record the exact failure in the history entry. Do not retry indefinitely; do not move on to another phase.\r\n\r\n7. **Write back — the LAST thing in the turn.** Emit the COMPACT manifest block (Output Structure below): a `statuses` entry for EVERY phase + a `changed` entry for the executed phase carrying its complete updated `work` (`generated`/`transport`/`snapshot`; for phases whose output is decisions rather than SAP objects — design phases — put the compact decision register into `work.notes`, since downstream phases load it via their manifest `reads`). Do NOT re-emit `content` or the full phase list — the CLI holds the full plan, merges your `changed` entries into it, and recomputes the `summary`. The CLI persists the merge as a new envelope version (history is append-only — never rewrite past entries). If the harness resume prompt in your context specifies a manifest shape, follow the shape shown in the resume prompt — it is authoritative for your CLI version. **A turn that ends without the manifest block LOSES the phase.**\r\n\r\n8. **End the turn — the CLI owns continuation.** Emit the manifest block, then END THE TURN. Do NOT ask a continuation question, do NOT call `dispatch_skill`, and NEVER execute a second phase in the same turn. After the manifest is persisted, the CLI itself asks the user whether to run the next eligible phase and — on consent — re-enters `--resume` with the exact saved file in a fresh, reset context (phase end = turn end; this is what keeps every phase bounded). An `approval: true` next phase still asks for approval at the start of its own resume turn. If no eligible next phase exists (plan complete, or remainder blocked), report the state before the manifest block — the CLI shows the final tracker either way.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Be specific.** \"Phase c1.persistence validated — ZAPPR_REQUEST active on transport S4HK903412\" beats \"phase done\".\r\n- For Mode 1, the Top 3 are the highest-risk phases or sharpest decisions in the plan. For Mode 2, lead with the executed phase's outcome, then what unblocked, then what's next.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n### Mode 1 — create\r\n\r\n```\r\n## Project Plan — [PROJECT TITLE]\r\n\r\n**Goal:** [1-sentence goal]\r\n**Source system:** [from <session_context>]\r\n**Built from:** [spec-gap file consumed, or \"goal only\"]\r\n**Phases:** [N] across [M] components\r\n\r\n---\r\n\r\n### Phase Sequence\r\n\r\n| # | Phase id | Layer | Delegates to | Needs | Exit gate | Approval |\r\n|---|----------|-------|--------------|-------|-----------|----------|\r\n| 1 | c1.persistence | persistence | abap-data-model | — | Table active (fields typed via standard DEs/built-ins), ATC clean | ✋ |\r\n| 2 | c1.data-model | data-model | abap-rap | c1.persistence | CDS views active | |\r\n...\r\n\r\n(Note: no standalone `c1.types` phase — typing is folded into `c1.persistence` because custom domains/DEs cannot be created in MCP mode. See the `types`-layer rule in Required Behavior 3.)\r\n\r\n---\r\n\r\n### How to run this plan\r\n\r\n /abap-plan --resume @<plan base name — no version needed>\r\n\r\nEach run executes exactly one phase in a fresh, bounded context. When it\r\nfinishes, the CLI asks whether to run the next phase now (auto-dispatched,\r\nstill fresh context) or exit and resume later.\r\n\r\n---\r\n\r\n### Risks / open items carried from spec-gap\r\n\r\n- [items that became approval flags or exit-gate conditions]\r\n```\r\n\r\n### Mode 2 — resume\r\n\r\n```\r\n## Plan Resume — [PROJECT TITLE] — phase [id]\r\n\r\n[TL;DR block]\r\n\r\n### Phase executed\r\n\r\n**Phase:** [id] ([layer], delegated to /[skill])\r\n**Grounding:** [objects read live before building]\r\n**Result:** [validated | validated-with-waiver | blocked] — [exit gate check result; for a waiver: what was waived and the user's reason]\r\n**Objects:** [created/changed, with activation status]\r\n**Transport:** [transport number]\r\n\r\n### Plan state\r\n\r\n[validated]/[total] phases validated. Next: [next phase id, or \"plan complete\"].\r\n\r\n→ Next: /abap-plan --resume @<plan base name, no version> (resolves to the latest automatically)\r\n\r\n(On plan complete, a backend-only stack chains instead:\r\n \"Backend complete. Next: /abap-fiori-build — point it at <exact binding name>.\")\r\n```\r\n\r\n### Manifest block (BOTH modes — MANDATORY, last thing in the output)\r\n\r\nThe machine-readable plan state. Emitted as a hidden HTML comment wrapping ONE JSON object. The CLI extracts, validates (Zod — invalid JSON or schema violations make the save FAIL), and persists it. **Output that is not in this block does not survive the session.**\r\n\r\nThere are TWO shapes. Mode 1 (create) emits the FULL shape — no envelope exists yet. Mode 2 (resume) emits the COMPACT shape — the envelope already holds the full plan; re-emitting unchanged phases is pure output-token waste.\r\n\r\n**FULL shape (Mode 1 create; also the Mode 2 fallback when the plan changes structurally — phase added/removed/re-sequenced):**\r\n\r\n```\r\n<!-- cspeach:plan-manifest\r\n{\r\n \"title\": \"<project title>\",\r\n \"statuses\": { \"<phase id>\": \"todo | designing | building | verifying | validated | validated-with-waiver | blocked\", ... },\r\n \"content\": {\r\n \"project\": { \"goal\": \"...\", \"source\": \"...\", \"target\": \"...\" },\r\n \"from\": {},\r\n \"phases\": [\r\n {\r\n \"id\": \"c1.persistence\",\r\n \"title\": \"Tables\",\r\n \"component\": \"c1\",\r\n \"layer\": \"persistence\",\r\n \"entryCriteria\": [],\r\n \"delegateTo\": \"abap-data-model\",\r\n \"manifest\": {\r\n \"reads\": [\"project.goal\"],\r\n \"rules\": [\".claude/rules/abap-conventions.md\", \".claude/rules/safety.md\"],\r\n \"writesBack\": [\"phases[c1.persistence].work\"]\r\n },\r\n \"exitGate\": \"Table active (fields typed via standard DEs/built-ins), ATC clean\",\r\n \"approval\": true,\r\n \"writes\": true,\r\n \"work\": { \"generated\": [\"ZAPPR_REQUEST\"], \"transport\": \"S4HK903412\", \"notes\": \"Intended custom domain ZAPPR_STATUS (CHAR1 fixed values) -> abap.char(1); value help via check-table FK. Reason: custom DOMA/DTEL content-write not available via MCP.\" }\r\n }\r\n ],\r\n \"summary\": { \"total\": 5, \"validated\": 1, \"blocked\": 0, \"next\": \"c1.data-model\" }\r\n }\r\n}\r\n-->\r\n```\r\n\r\n**COMPACT shape (Mode 2 resume — the normal phase write-back):**\r\n\r\n```\r\n<!-- cspeach:plan-manifest\r\n{\r\n \"title\": \"<project title — unchanged>\",\r\n \"statuses\": { \"<EVERY phase id>\": \"todo | designing | building | verifying | validated | validated-with-waiver | blocked\", ... },\r\n \"changed\": {\r\n \"<executed phase id>\": {\r\n \"work\": { \"generated\": [\"ZAPPR_REQUEST\"], \"transport\": \"S4HK903412\", \"notes\": \"<decisions / substitutions / blocker details worth keeping>\" }\r\n }\r\n }\r\n}\r\n-->\r\n```\r\n\r\nThe CLI merges each `changed` entry onto that phase in the envelope (the entry's `work` replaces the prior `work` wholesale), copies every other phase from the envelope verbatim, and recomputes `summary` from `statuses`. A `changed` key that names an unknown phase id fails the save.\r\n\r\nHard rules for the block:\r\n- One phase per turn — emit the block exactly once, as the LAST thing in the output. (The CLI persists the LAST block if several appear, so any block emitted must be self-sufficient.)\r\n- `statuses` must have an entry for EVERY phase id in BOTH shapes; on create, all `\"todo\"`.\r\n- FULL shape: `phases[]` is the COMPLETE phase list — never a delta. COMPACT shape: `changed` carries ONLY the phase(s) touched this turn (normally exactly one), and never includes `content`.\r\n- `entryCriteria` may only name phases that appear earlier in `phases[]`.\r\n- FULL shape: `summary.total` must equal `phases.length`; `summary.next` must be a real phase id or `null`. COMPACT shape: no `summary` — the CLI recomputes it.\r\n- The `\"from\"` key is ALWAYS present in `content` and ALWAYS exactly `\"from\": {}`. NEVER fill `from.specGap` yourself — you cannot know the consumed file's real path or sha256; the CLI records spec-gap provenance in the envelope's `promotedFrom` automatically when `--from` was used.\r\n- `delegateTo` must be one of: `abap-design`, `abap-data-model`, `abap-rap`, `abap-eml`, `abap-generate`, `abap-segw`, `abap-test`, `abap-fiori-build`, `abap-extend-model`.\r\n- `writes` (optional boolean, FULL shape / plan creation only): `false` ONLY on phases that touch no SAP object (pure design/analysis/decision phases); `true` on phases that create or modify SAP objects; omit when unsure — an omitted `writes` is treated as writing (the safe default). Never re-author `writes` in the COMPACT shape — resume preserves phase fields.\r\n- FULL shape: every phase carries a short human `\"title\"` (\"Design\", \"Tables\", \"Service\", …) — the CLI tracker renders it; the `id` stays the machine key everywhere else. Optional for backward compatibility, mandatory for new plans.\r\n- A service phase's `work` records the published binding name in `\"binding\"` — the plan-complete chain to `/abap-fiori-build` reads it.\r\n- A `ui` phase (greenfield) carries a `ui` map: `{ \"flavor\": \"fiori-elements\" | \"freestyle\", \"floorplan\"?, \"appId\"?, \"appTitle\"? }` — set at plan create, never edited on resume.\r\n- A `ui.build` phase's `work` records `app: { dir }` and `service: { url, path, version }` the moment the app is scaffolded; a `ui.deploy` phase's `work` records `deployedUrl`. The completion summary and the deploy phase both read these — omitting them is a gate failure, same class as a service phase omitting `work.binding`.\r\n\r\n## Guardrails\r\n\r\n- **Never chain phases.** One phase per turn, no exceptions. Continuation is CLI-owned: after the manifest is persisted, the CLI offers the next phase and dispatches the resume itself in a fresh context. Never call `dispatch_skill` with `/abap-plan --resume` (the CLI rejects it) and never start a second phase in the same turn — in-turn chaining defeats the bounded-context design.\r\n- **The envelope is the only memory.** Anything worth keeping goes into the manifest block — `work.generated`, transport numbers, blocking reasons. Chat text that isn't in the block is gone next session.\r\n- **Do not widen the manifest silently.** If a phase genuinely needs context it didn't declare, that is a plan defect: record it in the history (via the manifest), load the minimum extra needed, and note the manifest correction in the updated block.\r\n- **Do not re-implement delegate skills.** The plan routes to `/abap-data-model`, `/abap-rap`, etc. — it never inlines their job. If a delegate skill can't handle the phase, the phase is mis-specified; block it and say why.\r\n- **Seed UI phases in greenfield whenever the design includes a UI layer.** When the design (or the user's goal) calls for a Fiori app, seed after the service phase: (1) `ui.build` — `layer: ui`, `delegateTo: abap-fiori-build`, with a `ui` map carrying `flavor` (`fiori-elements` | `freestyle`, from the design's UI Layer Decision — ask if the design is silent), plus `floorplan`/`appId`/`appTitle` when known; `entryCriteria` = the service phase id; `exitGate`: \"App builds clean; fiori_render_smoke passes (or the phase records verification: manual with the reason)\". Valid `floorplan` values: listReport, worklist, ovp, alp. (2) `ui.deploy` — `layer: ui`, `delegateTo: abap-fiori-build`, `entryCriteria: [ui.build]`, `exitGate`: \"BSP deploy succeeded; app URL opens (recorded in work.deployedUrl)\". `ui.deploy` is seeded by default; the user may drop it at plan time (\"local preview only\"). On a ui phase's resume turn, the harness injects the published binding, flavor, and any recorded app directory into the prompt (authoritative — use them, do not re-derive). For floorplan alp, seed the analytical backend phases (dimension/cube view + analytical query) before the service phase — see abap-rap's analytical-layer template. A greenfield plan whose goal includes an app but which ends at the service binding with no `ui` phase is a planning error. **Revision mode is unchanged:** a freestyle `ui` phase (`delegateTo: abap-fiori-build`, amend mode) is seeded when `revision.uiKind` is `freestyle` — this is the named C2 seam; that phase delegates to `/abap-fiori-build` amend mode for the UI change it carries; it never silently disappears. A **Fiori Elements** revision plan never seeds a `ui` phase (annotations are the UI, no frontend code changes).\r\n- **Blocked is a valid outcome.** A `blocked` phase with an exact failure note beats a `validated` phase with an unverified exit gate. Rule 10 applies: activation must be VERIFIED, not assumed.\r\n- **Respect Forge Rule 8.** Mode 1 never executes; `approval: true` phases never run without explicit confirmation; a mid-phase failure stops the session — never \"continue to the next phase to make progress\".\r\n- **Vague goal → spec-gap first.** Refusing to plan on guesses is correct behavior, not unhelpfulness.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-plan --from @hr-payroll-extract-spec-gap-3f2a-v4.cspeach.json\r\n\r\nGoal: Extract HR master data (PA0001/PA0002/PA0008 deltas) from S/4HANA to a\r\npayroll-provider SFTP file, nightly, with a staging table, error reprocessing,\r\nand a monitoring report.\r\n```\r\n\r\n## Example Output Outline (Mode 1)\r\n\r\n```\r\n## Project Plan — HR Payroll Extract\r\n\r\n## TL;DR\r\n**Headline:** 6 phases across 2 components (extract core, monitoring) seeded from 14 answered spec-gap items.\r\n**Top 3:**\r\n1. c1.persistence creates staging table ZHR_PAY_STG (fields typed via standard DEs/built-ins — no custom DDIC) — approval-gated, new persistence\r\n2. Delta logic depends on spec-gap answer #9 (change pointers vs. date-based) — baked into c1.orchestration exit gate\r\n3. Monitoring report (c2) is independent after c1.persistence — can be resequenced if c1 blocks\r\n**Verdict:** Plan saved. Run /abap-plan --resume @<file> — first phase is c1.persistence via /abap-data-model.\r\n\r\n---\r\n\r\n### Phase Sequence\r\n\r\n| # | Phase id | Layer | Delegates to | Needs | Exit gate | Approval |\r\n|---|----------|-------|--------------|-------|-----------|----------|\r\n| 1 | c1.persistence | persistence | abap-data-model | — | ZHR_PAY_STG active (fields typed via standard DEs/built-ins), ATC clean | ✋ |\r\n| 2 | c1.orchestration | orchestration | abap-generate | c1.persistence | Extract class + job report active, unit-testable delta selection | |\r\n| 3 | c1.integration | integration | abap-generate | c1.orchestration | Provider file format verified against sample, SFTP delivery stubbed | |\r\n| 4 | c1.test | behavior | abap-test | c1.orchestration | Unit tests green on delta + mapping logic | |\r\n| 5 | c2.data-model | data-model | abap-generate | c1.persistence | CDS view on staging table active | |\r\n| 6 | c2.orchestration | orchestration | abap-generate | c2.data-model | Monitoring ALV report active | |\r\n\r\n[... how-to-run + risks + manifest block ...]\r\n```\r\n",
163
170
  "sha256": "cbeab4207a80d488e9d839c363111c9d35d362ba7aef3c35d1f53b3895004e65",
164
171
  "signature": "",
165
- "signedAt": "2026-09-17T20:19:05.330Z"
172
+ "signedAt": "2026-09-28T14:37:04.847Z"
166
173
  },
167
174
  "abap-preflight": {
168
175
  "name": "abap-preflight",
169
176
  "body": "---\r\nname: abap-preflight\r\ndescription: >\r\n Pre-release checklist for ABAP changes — analyzes the change set, identifies\r\n dependencies and affected areas, verifies tests, assesses rollback feasibility,\r\n and produces a go/no-go release package. Read-only. The decision to release\r\n stays with the developer and release manager.\r\nphase: SHIP\r\nrequires_mcp: optional\r\nforge_rules: [2, 5, 6, 9]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-preflight\r\n\r\n## Purpose\r\n\r\nGenerate a comprehensive pre-release checklist for a set of ABAP changes before\r\nthey are released to a target system (QA, PRE, or PROD). The checklist covers\r\nchange summary, affected objects, dependency watchlist, regression risk,\r\ntest coverage, authorization/configuration touchpoints, release risk rating,\r\npost-import watchpoints, and rollback/fallback options.\r\n\r\nForge Rule 5: No \"done\" without a preflight. This skill implements that rule.\r\n\r\nThis skill is read-only. It produces a checklist and risk assessment. It does\r\nnot release transports or activate objects. Use `/abap-transport` for transport\r\noperations.\r\n\r\n## When to Use\r\n\r\n- Before releasing a transport to QA or production\r\n- When handing over a change to a release manager\r\n- When assessing risk of a hotfix deployment\r\n- As the final verification step before any go-live\r\n- When `/abap-transport validate` requires a preflight sign-off\r\n\r\nWhen NOT to use: a TR-number-scoped object/ATC/conflict scan — `/abap-transport-analysis`; creating or releasing the transport itself — `/abap-transport`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Description of what changed (feature description or change request text)\r\n- List of changed objects (class names, program names, tables, etc.)\r\n\r\nUseful (will be asked or inferred from MCP):\r\n- Transport request number\r\n- Target system and target release date\r\n- Dependent systems or downstream processes\r\n- Test results (from `/abap-test` or manual testing notes)\r\n- Authorization implications\r\n\r\nIf MCP is connected, the skill can retrieve the transport object list and\r\nperform dependency lookups automatically.\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Change Summary\r\n\r\nSummarize what changed in plain language. Identify:\r\n- Type of change: new feature / bug fix / enhancement / configuration / refactor\r\n- Business process(es) affected\r\n- Technical layer: UI / business logic / data / integration / authorization\r\n\r\n### Step 2 — Object Inventory\r\n\r\nList all changed objects with their type and change category (new / modified /\r\ndeleted). If MCP is connected, retrieve from transport request automatically.\r\n\r\n### Step 3 — Dependency Analysis\r\n\r\nFor each object:\r\n- What does it call or depend on? (imports, method calls, table reads)\r\n- What calls it? (where-used, if available from MCP)\r\n- Are there implicit dependencies (shared tables, shared enhancements)?\r\n\r\nFlag any dependency on:\r\n- SAP standard objects (potential impact from SAP patches)\r\n- Shared utilities used by other custom objects\r\n- Workflow definitions\r\n- Job scheduling definitions\r\n\r\n### Step 4 — Affected Areas\r\n\r\nIdentify which business transactions, jobs, interfaces, and reports are\r\npotentially affected by the change. Even if the change is isolated, confirm\r\nthis with a where-used analysis.\r\n\r\n### Step 5 — Regression Checklist\r\n\r\nBased on the affected areas, propose a regression checklist: the minimum set\r\nof scenarios the tester should verify before approving the release.\r\n\r\n### Step 6 — Test Checklist\r\n\r\nVerify (or prompt verification of):\r\n- [ ] Unit tests exist and pass\r\n- [ ] Integration test in DEV completed\r\n- [ ] QA test sign-off obtained (if releasing to PROD)\r\n- [ ] Performance test done (if data volume change expected)\r\n- [ ] Security test done (if auth object changes involved)\r\n\r\n### Step 7 — Authorization and Configuration Touchpoints\r\n\r\nList any:\r\n- New or changed authorization objects\r\n- New or changed Customizing entries (IMG activities)\r\n- New or changed job definitions\r\n- New or changed RFC destinations or communication arrangements\r\n\r\n### Step 8 — Release Risk Rating\r\n\r\nAssign an overall risk: Low / Medium / High / Critical\r\n\r\nFactors:\r\n- Number of objects changed\r\n- Whether standard tables are touched\r\n- Whether the change affects a core business process\r\n- Whether rollback is straightforward\r\n- Whether unit/integration tests pass\r\n\r\n### Step 9 — Post-Import Watchpoints\r\n\r\nList what must be monitored immediately after import:\r\n- Jobs to check\r\n- Error logs to monitor\r\n- Transaction codes to spot-check\r\n- SM50/SM66 for performance indicators\r\n\r\n### Step 10 — Rollback / Fallback\r\n\r\nDescribe the rollback plan:\r\n- Is the change reversible by re-importing the previous transport?\r\n- Are there data changes that cannot be rolled back (table structure changes)?\r\n- Is there a fallback configuration (feature flag, parameter)?\r\n- Who authorizes a rollback?\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Pre-Release Checklist — {change_title}\r\n\r\n**Change Request / Transport:** {cr_or_transport}\r\n**Target System:** {system}\r\n**Release Date Planned:** {date}\r\n**Risk Rating:** {Low | Medium | High | Critical}\r\n\r\n---\r\n\r\n## Change Summary\r\n\r\n**Type:** {new_feature | bug_fix | enhancement | configuration | refactor}\r\n**Business Area:** {area}\r\n**Technical Layer:** {layer}\r\n\r\n{2-3 sentence description of what changed and why}\r\n\r\n---\r\n\r\n## Changed Objects\r\n\r\n| Object | Type | Package | Change Type | Notes |\r\n|--------|------|---------|-------------|-------|\r\n| {name} | Class/Report/Table/etc. | {package} | New/Modified/Deleted | {notes} |\r\n\r\n---\r\n\r\n## Affected Areas\r\n\r\n| Area | Impact | Evidence |\r\n|------|--------|----------|\r\n| {business_process} | Direct / Indirect | {reason} |\r\n| {transaction} | Direct / Indirect | {reason} |\r\n\r\n---\r\n\r\n## Dependency Watchlist\r\n\r\n| Dependency | Type | Risk | Action Required |\r\n|------------|------|------|-----------------|\r\n| {object} | Internal/External/Standard | Low/Medium/High | {action} |\r\n\r\n---\r\n\r\n## Regression Checklist\r\n\r\n- [ ] {test_scenario_1}\r\n- [ ] {test_scenario_2}\r\n- [ ] {test_scenario_3}\r\n\r\n---\r\n\r\n## Test Checklist\r\n\r\n- [ ] Unit tests created and passing (see /abap-test)\r\n- [ ] ATC checks clean (see /abap-atc-fix)\r\n- [ ] DEV integration test completed by {role}\r\n- [ ] QA sign-off obtained by {role}\r\n- [ ] Performance tested with production-like volume\r\n- [ ] Security review completed (if auth changes)\r\n\r\n---\r\n\r\n## Authorization and Configuration Touchpoints\r\n\r\n| Item | Type | Action |\r\n|------|------|--------|\r\n| {auth_object} | Authorization | Add to roles: {roles} |\r\n| {customizing} | IMG | Verify setting in target system |\r\n\r\n---\r\n\r\n## Release Risk\r\n\r\n**Overall:** {Low | Medium | High | Critical}\r\n\r\n**Key Risk Factors:**\r\n- {risk_1}\r\n- {risk_2}\r\n\r\n**Mitigations Applied:**\r\n- {mitigation_1}\r\n\r\n---\r\n\r\n## Post-Import Watchpoints\r\n\r\nImmediately after import to {target_system}:\r\n- [ ] Run transaction {tx} to verify basic functionality\r\n- [ ] Check SLG1 for application log entries\r\n- [ ] Check SM21 for system log errors\r\n- [ ] Monitor job {job_name} for next scheduled run\r\n- [ ] Spot-check {business_process} end-to-end\r\n\r\n---\r\n\r\n## Rollback / Fallback\r\n\r\n**Rollback Feasibility:** {Easy | Conditional | Difficult | Not Possible}\r\n\r\n**Method:** {re-import previous transport | deactivate BAdI impl | toggle config | manual SQL}\r\n\r\n**Data Risk:** {None | Low — no data structure changes | High — irreversible table changes}\r\n\r\n**Authorization:** Rollback decision by {release_manager / project_lead}\r\n\r\n**Estimated Rollback Time:** {estimate}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER state that a release is safe — only state what was verified and what remains\r\n- NEVER skip the rollback section — if rollback is not feasible, say so explicitly\r\n- If MCP is not connected and object list is manually provided, note that\r\n dependency analysis is based on provided information only\r\n- Do not mark Test Checklist items as complete unless evidence is provided\r\n- If a changed object touches a standard SAP table directly, escalate to High risk\r\n- Forge Rule 5: this checklist must be completed before any transport release\r\n — if the user tries to skip it, remind them\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-preflight\r\n\r\nChange: Added email notification to the order approval workflow. When an order\r\nis approved or rejected, the requester now receives an email via CL_BCS.\r\n\r\nChanged objects:\r\n- ZCL_ORDER_APPROVAL (class) — modified: added send_notification method\r\n- ZCL_ORDER_APPROVAL_TEST (test class) — new: unit tests added\r\n- ZORDER_WORKFLOW (workflow definition) — task exit changed\r\n\r\nTransport: DEVK912345\r\nTarget: PRD (after QA sign-off)\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Pre-Release Checklist — Order Approval Email Notification\r\n\r\n**Change Request / Transport:** DEVK912345\r\n**Target System:** PRD\r\n**Release Date Planned:** 2026-03-31\r\n**Risk Rating:** Medium\r\n\r\n---\r\n\r\n## Change Summary\r\n\r\n**Type:** Enhancement (new feature to existing function)\r\n**Business Area:** Order Management / Workflow\r\n**Technical Layer:** Business Logic + Email Integration\r\n\r\nThe order approval class ZCL_ORDER_APPROVAL has been extended with a new\r\nprivate method send_notification that sends an email to the order requester\r\non approval or rejection. The workflow task exit in ZORDER_WORKFLOW now\r\ntriggers this notification. Email delivery uses CL_BCS (C1 Released).\r\n\r\n---\r\n\r\n## Changed Objects\r\n\r\n| Object | Type | Package | Change Type | Notes |\r\n|----------------------|------------------|----------------|-------------|-----------------------------|\r\n| ZCL_ORDER_APPROVAL | Global Class | ZORD_APPROVAL | Modified | New method send_notification|\r\n| ZCL_ORDER_APPROVAL_TEST | Test Class | ZORD_APPROVAL | New | 3 new unit tests for notify |\r\n| ZORDER_WORKFLOW | Workflow Def | ZORD_WORKFLOW | Modified | Task exit binding updated |\r\n\r\n---\r\n\r\n## Affected Areas\r\n\r\n| Area | Impact | Evidence |\r\n|-------------------------------|----------|---------------------------------------------|\r\n| Order approval workflow | Direct | ZORDER_WORKFLOW modified |\r\n| Email infrastructure (SMTP) | Direct | CL_BCS sends via configured SMTP server |\r\n| All requester email addresses | Indirect | Email sent to order requester on every decision |\r\n\r\n---\r\n\r\n## Dependency Watchlist\r\n\r\n| Dependency | Type | Risk | Action Required |\r\n|---------------------|----------|--------|--------------------------------------------------|\r\n| CL_BCS | SAP std | Low | Verify email server configured in SCOT |\r\n| SMTP server config | External | Medium | Confirm SCOT profile active in PRD |\r\n| ZORDER_WORKFLOW | Custom | Medium | Workflow re-activation may be required after import |\r\n\r\n---\r\n\r\n## Regression Checklist\r\n\r\n- [ ] Approve an order in QA — verify requester receives approval email\r\n- [ ] Reject an order in QA — verify requester receives rejection email with reason\r\n- [ ] Approve an order where requester has no email address — verify graceful handling (no dump)\r\n- [ ] Approve an order where SMTP server is temporarily down — verify no exception blocks approval\r\n- [ ] Verify existing order approval behavior unchanged (non-email path)\r\n\r\n---\r\n\r\n## Test Checklist\r\n\r\n- [x] Unit tests created and passing (ZCL_ORDER_APPROVAL_TEST — 3 tests, all green)\r\n- [x] ATC checks clean (0 findings after /abap-atc-fix run)\r\n- [ ] DEV integration test completed — PENDING\r\n- [ ] QA sign-off obtained — PENDING\r\n- [ ] Performance tested — N/A (email is async, no volume concern)\r\n- [ ] Security review — N/A (no auth object changes)\r\n\r\n---\r\n\r\n## Authorization and Configuration Touchpoints\r\n\r\n| Item | Type | Action |\r\n|-----------------------|----------------|----------------------------------------------|\r\n| SCOT email profile | Configuration | Verify PRD SCOT is active and has SMTP route |\r\n| Sender email address | Configuration | Check default sender in SCOT matches domain |\r\n\r\n---\r\n\r\n## Release Risk\r\n\r\n**Overall:** Medium\r\n\r\n**Key Risk Factors:**\r\n- Workflow definition re-activation needed after import — if not activated,\r\n new tasks will use old code\r\n- Email failure (SMTP down) could be silent if exception handling is incomplete\r\n\r\n**Mitigations Applied:**\r\n- send_notification catches CX_BCS_EXCEPTION and logs to SLG1 — approval is\r\n not blocked by email failure\r\n- Unit tests cover the no-email-address edge case\r\n\r\n---\r\n\r\n## Post-Import Watchpoints\r\n\r\nImmediately after import to PRD:\r\n- [ ] Re-activate ZORDER_WORKFLOW in SWI5 or SWDD\r\n- [ ] Create a test order and approve it — verify email received\r\n- [ ] Check SLG1 for any BCS errors (log object: ZORD)\r\n- [ ] Check SM21 for any system errors in the 10 minutes after import\r\n\r\n---\r\n\r\n## Rollback / Fallback\r\n\r\n**Rollback Feasibility:** Easy\r\n\r\n**Method:** Re-import the previous transport containing ZCL_ORDER_APPROVAL and\r\nZORDER_WORKFLOW versions. No table structure changes were made.\r\n\r\n**Data Risk:** None — no DDIC changes, no persistent data written by the notification logic.\r\n\r\n**Authorization:** Rollback decision by Release Manager.\r\n\r\n**Estimated Rollback Time:** 15 minutes (import + workflow re-activation).\r\n```\r\n",
170
177
  "sha256": "71b08c84329fe02f3f1ffe6017f5523f4a37bc789187c7151a27ee54685a047e",
171
178
  "signature": "",
172
- "signedAt": "2026-09-17T20:19:05.404Z"
179
+ "signedAt": "2026-09-28T14:37:04.888Z"
173
180
  },
174
181
  "abap-radar": {
175
182
  "name": "abap-radar",
176
- "body": "---\nname: abap-radar\ndescription: \"Understand an ABAP object quickly — dependencies, tables, risk areas, modernization notes. Use when encountering unfamiliar ABAP code.\"\nuser-invocable: true\nmin_cli_version: \"0.3.0\"\nwidget: dep-graph\n---\n\n# Skill: abap-radar\n\n## Purpose\n\nRapidly orient on an unfamiliar ABAP object. Given source code or an object name, produce a structured intelligence briefing covering what the object does, what it depends on, what business process it belongs to, where the risk areas are, and how it sits against modern ABAP standards. This skill reads and analyzes — it never modifies anything.\n\n## When to Use\n\n- You have just inherited or been handed unfamiliar legacy ABAP code\n- You need to assess an object before touching it (refactor, extend, debug)\n- A code review or impact assessment requires fast context\n- You are onboarding to a new SAP system and need to understand its custom development\n- A ticket references a program/class/FM and you need background before estimating\n\nWhen NOT to use: blast radius of a planned change — `/abap-impact`; a graded quality review — `/abap-review`; plain-language teaching — `/abap-explain`.\n\n## Inputs Expected\n\nProvide ONE of the following:\n\n1. **Source code pasted directly** — the full or partial ABAP source\n2. **Object name** — e.g., `ZREP_PRICING_ANALYSIS`, `ZCL_ORDER_PROCESSOR`, `ZFM_GET_OPEN_ORDERS` (requires MCP connection to retrieve source)\n3. **Object name + type** — e.g., `PROG ZREP_PRICING_ANALYSIS` or `CLAS ZCL_ORDER_PROCESSOR`\n\nIf only a name is given and no MCP connection is available, the skill will explain what it cannot determine and ask for the source.\n\n## Required Behavior\n\n1. **Read source** — If source is pasted, use it directly. If an object name is given and MCP is available, call `sap_get_source` to retrieve the source. Do not invent source code.\n\n2. **Identify object type and probable purpose** — Determine the ABAP object type (report, class, function module, include, CDS view, BAdI implementation, etc.) from structural cues. State the probable business purpose in 2–3 plain sentences. Mark this as `[INFERRED]` if deduced from code patterns, `[CONFIRMED]` if the object has a clear title/description, or `[UNKNOWN]` if it cannot be determined.\n\n3. **Extract all dependencies** — Scan for:\n - Database tables accessed via SELECT, UPDATE, INSERT, DELETE, MODIFY\n - Function modules called via CALL FUNCTION\n - Classes instantiated or called\n - BAPIs, RFCs, and remote-enabled FMs\n - Enhancement spots, BAdIs, user exits\n - CDS views referenced\n - Message classes\n - Authorization objects in AUTHORITY-CHECK statements\n - External programs called via SUBMIT, CALL TRANSACTION, CALL SCREEN\n\n4. **Identify business process area** — Map the tables and function modules to an SAP module (SD, MM, FI, PP, WM, HR, PS, etc.). State confidence level.\n\n5. **Flag risk areas** — Identify patterns that represent quality, performance, security, or stability risks:\n - SELECT inside LOOP (N+1 queries)\n - SELECT * (unspecified field list)\n - TABLES statement (obsolete, coupling risk)\n - Missing AUTHORITY-CHECK before sensitive data access\n - Hard-coded clients, company codes, plant values\n - Unhandled sy-subrc after critical calls\n - Direct UPDATE/INSERT/DELETE on SAP-delivered tables\n - Use of obsolete statements (PERFORM, MOVE...TO, COMPUTE)\n - Missing empty-check before FOR ALL ENTRIES\n - Nested LOOPs deeper than 2 levels\n\n6. **Produce modernization notes** — Compare the code against CSPeach ABAP conventions. Note what could be modernized (OO migration, CDS view extraction, inline declarations, string templates, FILTER/REDUCE expressions, etc.). Do not rewrite the code — only observe.\n\n7. **List open questions** — Things that cannot be determined from the code alone: undocumented business logic branches, unclear parameter meanings, dependencies that could not be resolved, authorization objects that may be missing.\n\n8. **Forge Rules compliance** — This skill is read-only (Forge Rule 6). No writes are performed. All inferences are labeled. Missing information is stated explicitly — nothing is invented.\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\nProduce the following markdown output exactly:\n\n```\n## ABAP Radar — [OBJECT NAME]\n\n**Object Type:** [e.g., Report / Global Class / Function Module / CDS View / BAdI Impl]\n**Analyzed:** [Paste / MCP Retrieved / Partial — first N lines only]\n\n---\n\n### Probable Purpose\n[2–3 sentence plain-language summary of what this object does.]\n[INFERRED / CONFIRMED / UNKNOWN]\n\n---\n\n### Dependencies\n\n| Type | Object | Notes |\n|------|--------|-------|\n| DB Table | VBAK | Sales order header — SELECT |\n| DB Table | VBAP | Sales order items — SELECT |\n| DB Table | KONV | Condition values — SELECT in LOOP ⚠️ |\n| Function Module | CONVERSION_EXIT_ALPHA_INPUT | Input conversion — standard FM |\n| Auth Object | V_VBAK_AAT | Authority check for order type |\n| Message Class | ZSD_MESSAGES | Custom SD messages |\n\n---\n\n### Business Process Area\n[e.g., Sales and Distribution (SD) — Order-to-Cash]\nConfidence: [High / Medium / Low] — [reason]\n\n---\n\n### Risk Areas\n\n| Severity | Pattern | Location | Detail |\n|----------|---------|----------|--------|\n| HIGH | SELECT inside LOOP | Line ~47 | SELECT on KONV inside LOOP AT lt_vbap — N+1 queries |\n| HIGH | Missing empty check | Line ~43 | FOR ALL ENTRIES without IS NOT INITIAL guard |\n| MEDIUM | SELECT * | Line ~31 | VBAK read fetches all columns — unspecified field list |\n| MEDIUM | TABLES statement | Line ~12 | Obsolete coupling — use typed parameters |\n| LOW | Hard-coded plant | Line ~88 | WERKS = '0001' — should be parameterized |\n\n---\n\n### Modernization Notes\n- [ ] Migrate TABLES statement to typed parameter passing\n- [ ] Extract KONV query from inside loop — use FOR ALL ENTRIES with empty check\n- [ ] Replace SELECT * with explicit field list\n- [ ] Consider CDS view for VBAK/VBAP join instead of ABAP-level join\n- [ ] Replace CONCATENATE with string template |...|\n- [ ] Hard-coded plant '0001' should come from selection screen or config table\n\n---\n\n### Open Questions\n1. [Question about unclear logic or missing context]\n2. [Question about a dependency that could not be resolved]\n3. [Question about authorization coverage]\n\n---\n\n### Summary Assessment\n[2–3 sentence overall verdict: is this code stable, risky, ready to touch, needs refactor before extending, etc.]\n```\n\n## Guardrails\n\n- **Never invent SAP objects.** If a dependency cannot be found in the source, do not guess it exists.\n- **Label all inferences.** Use `[INFERRED]`, `[CONFIRMED]`, or `[UNKNOWN]` where certainty matters.\n- **State missing information explicitly.** \"The source only shows lines 1–50; dependency analysis is incomplete\" is better than a confident but wrong answer.\n- **Read only.** This skill never calls `sap_set_source`, `sap_create_object`, `sap_delete_object`, or any write tool.\n- **Do not rewrite the code.** Modernization notes are observations, not proposals requiring immediate action. Use `/abap-refactor` for actual rewrites.\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind code generation), Rule 2 (no change without impact thinking), Rule 6 (read only by default).\n\n## Example Prompt\n\n```\n/abap-radar\n\nREPORT zrep_pricing_analysis.\n\nTABLES: vbak, vbap.\n\nDATA: lt_orders TYPE TABLE OF vbak,\n ls_order TYPE vbak,\n lt_items TYPE TABLE OF vbap,\n ls_item TYPE vbap,\n lt_konv TYPE TABLE OF konv,\n ls_konv TYPE konv,\n lv_total TYPE p DECIMALS 2.\n\nSTART-OF-SELECTION.\n\n AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'\n ID 'AUART' FIELD 'TA'\n ID 'ACTVT' FIELD '03'.\n IF sy-subrc <> 0.\n MESSAGE 'No authorization' TYPE 'E'.\n ENDIF.\n\n SELECT * FROM vbak INTO TABLE lt_orders\n WHERE erdat = sy-datum\n AND auart = 'TA'.\n\n LOOP AT lt_orders INTO ls_order.\n SELECT * FROM vbap INTO TABLE lt_items\n WHERE vbeln = ls_order-vbeln.\n\n LOOP AT lt_items INTO ls_item.\n SELECT * FROM konv INTO TABLE lt_konv\n WHERE knumv = ls_item-knumv\n AND kposn = ls_item-posnr.\n\n LOOP AT lt_konv INTO ls_konv\n WHERE kschl = 'PR00'.\n lv_total = lv_total + ls_konv-kwert.\n ENDLOOP.\n ENDLOOP.\n ENDLOOP.\n\n WRITE: / 'Total pricing value:', lv_total.\n```\n\n## Example Output Outline\n\n```\n## ABAP Radar — ZREP_PRICING_ANALYSIS\n\n**Object Type:** Report (executable program)\n**Analyzed:** Paste\n\n---\n\n### Probable Purpose\nThis report calculates a total pricing value across all standard sales orders (type TA) created today. It reads order headers, their line items, and condition records to accumulate the base price (condition type PR00) into a running total. The result is written to the screen using WRITE.\n[INFERRED] — no title or documentation found in the source.\n\n---\n\n### Dependencies\n\n| Type | Object | Notes |\n|------|--------|-------|\n| DB Table | VBAK | Sales order header — SELECT * all columns |\n| DB Table | VBAP | Sales order items — SELECT * inside LOOP ⚠️ |\n| DB Table | KONV | Condition values — SELECT * inside nested LOOP ⚠️⚠️ |\n| Auth Object | V_VBAK_AAT | Authority check for order type TA, activity 03 (display) |\n\n---\n\n### Business Process Area\nSales and Distribution (SD) — Pricing / Order-to-Cash reporting.\nConfidence: High — VBAK, VBAP, and KONV are core SD tables; condition type PR00 is the standard base price condition.\n\n---\n\n### Risk Areas\n\n| Severity | Pattern | Location | Detail |\n|----------|---------|----------|--------|\n| HIGH | SELECT inside LOOP | Line ~31 | SELECT on VBAP inside LOOP AT lt_orders — one query per order |\n| HIGH | SELECT inside nested LOOP | Line ~36 | SELECT on KONV inside LOOP AT lt_items — one query per line item. For 100 orders × 10 items = 1,000 KONV queries |\n| HIGH | SELECT * | Lines 28, 31, 36 | All three SELECTs fetch every column from VBAK, VBAP, KONV. Only a handful of fields are used |\n| MEDIUM | TABLES statement | Line 3 | TABLES: vbak, vbap — obsolete. Creates implicit global work areas and tight coupling |\n| MEDIUM | WRITE output | Line ~49 | WRITE statement used for output — not suitable for background jobs or integration |\n| LOW | Hard-coded order type | Line ~30 | AUART = 'TA' hard-coded — should be a selection screen parameter |\n| LOW | Hard-coded condition type | Line ~42 | KSCHL = 'PR00' hard-coded — should be configurable |\n| LOW | No date range selection | N/A | Only today's orders (sy-datum) — no selection screen for flexibility |\n\n---\n\n### Modernization Notes\n- [ ] Replace three-level nested SELECT-in-LOOP with a single CDS view joining VBAK, VBAP, KONV, then one SELECT into an internal table\n- [ ] Remove TABLES statement — use typed local structures\n- [ ] Replace SELECT * with explicit field lists: only VBELN, AUART, ERDAT from VBAK; VBELN, POSNR, KNUMV from VBAP; KNUMV, KPOSN, KSCHL, KWERT from KONV\n- [ ] Add selection screen with date range (ERDAT), order type (AUART), sales org (VKORG)\n- [ ] Replace WRITE output with ALV grid (CL_SALV_TABLE) for usability\n- [ ] Consider using standard FM SD_SALESDOCUMENT_READ or CDS view I_SalesOrder as the access layer instead of direct table reads\n- [ ] lv_total declared as TYPE P DECIMALS 2 — may lose precision for large values; consider DECFLOAT34 or CURR with currency key\n\n---\n\n### Open Questions\n1. Is this report run interactively or as a background job? WRITE output does not work in background — if it runs in batch, output is silently lost.\n2. What currency should lv_total be in? KONV-KWERT is in condition currency (KONV-WAERS). The code does not handle currency conversion or mixed currencies.\n3. Who consumes the total? Is this a one-off script or a production report? Its current state suggests it is not production-ready.\n4. Is authorization coverage sufficient? The check only covers order type TA with activity 03. If other order types are added later the auth check needs updating.\n\n---\n\n### Summary Assessment\nThis is a fragile legacy report with three levels of nested database queries that will perform extremely poorly at scale — 100 orders with 10 items each generates over 1,100 individual SELECT statements. It is readable and functionally correct for small datasets but must not be promoted to a high-volume system without a full rewrite of the data access layer. Before extending or modifying this program, extract the data access into a CDS view and flatten the loop structure.\n```\n",
177
- "sha256": "3968c4ec88324ee656d7c27fa791307fffb93495bbfa441af6f1c67cd038cfad",
183
+ "body": "---\r\nname: abap-radar\r\ndescription: \"Understand an ABAP object quickly — dependencies, tables, risk areas, modernization notes. Use when encountering unfamiliar ABAP code.\"\r\nuser-invocable: true\r\nmin_cli_version: \"0.3.0\"\r\nwidget: dep-graph\r\n---\r\n\r\n# Skill: abap-radar\r\n\r\n## Purpose\r\n\r\nRapidly orient on an unfamiliar ABAP object. Given source code or an object name, produce a structured intelligence briefing covering what the object does, what it depends on, what business process it belongs to, where the risk areas are, and how it sits against modern ABAP standards. This skill reads and analyzes — it never modifies anything.\r\n\r\n## When to Use\r\n\r\n- You have just inherited or been handed unfamiliar legacy ABAP code\r\n- You need to assess an object before touching it (refactor, extend, debug)\r\n- A code review or impact assessment requires fast context\r\n- You are onboarding to a new SAP system and need to understand its custom development\r\n- A ticket references a program/class/FM and you need background before estimating\r\n\r\nWhen NOT to use: blast radius of a planned change — `/abap-impact`; a graded quality review — `/abap-review`; plain-language teaching — `/abap-explain`.\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE of the following:\r\n\r\n1. **Source code pasted directly** — the full or partial ABAP source\r\n2. **Object name** — e.g., `ZREP_PRICING_ANALYSIS`, `ZCL_ORDER_PROCESSOR`, `ZFM_GET_OPEN_ORDERS` (requires MCP connection to retrieve source)\r\n3. **Object name + type** — e.g., `PROG ZREP_PRICING_ANALYSIS` or `CLAS ZCL_ORDER_PROCESSOR`\r\n\r\nIf only a name is given and no MCP connection is available, the skill will explain what it cannot determine and ask for the source.\r\n\r\n## Required Behavior\r\n\r\n1. **Read source** — If source is pasted, use it directly. If an object name is given and MCP is available, call `sap_get_source` to retrieve the source. Do not invent source code.\r\n\r\n2. **Identify object type and probable purpose** — Determine the ABAP object type (report, class, function module, include, CDS view, BAdI implementation, etc.) from structural cues. State the probable business purpose in 2–3 plain sentences. Mark this as `[INFERRED]` if deduced from code patterns, `[CONFIRMED]` if the object has a clear title/description, or `[UNKNOWN]` if it cannot be determined.\r\n\r\n3. **Extract all dependencies** — Scan for:\r\n - Database tables accessed via SELECT, UPDATE, INSERT, DELETE, MODIFY\r\n - Function modules called via CALL FUNCTION\r\n - Classes instantiated or called\r\n - BAPIs, RFCs, and remote-enabled FMs\r\n - Enhancement spots, BAdIs, user exits\r\n - CDS views referenced\r\n - Message classes\r\n - Authorization objects in AUTHORITY-CHECK statements\r\n - External programs called via SUBMIT, CALL TRANSACTION, CALL SCREEN\r\n\r\n4. **Identify business process area** — Map the tables and function modules to an SAP module (SD, MM, FI, PP, WM, HR, PS, etc.). State confidence level.\r\n\r\n5. **Flag risk areas** — Identify patterns that represent quality, performance, security, or stability risks:\r\n - SELECT inside LOOP (N+1 queries)\r\n - SELECT * (unspecified field list)\r\n - TABLES statement (obsolete, coupling risk)\r\n - Missing AUTHORITY-CHECK before sensitive data access\r\n - Hard-coded clients, company codes, plant values\r\n - Unhandled sy-subrc after critical calls\r\n - Direct UPDATE/INSERT/DELETE on SAP-delivered tables\r\n - Use of obsolete statements (PERFORM, MOVE...TO, COMPUTE)\r\n - Missing empty-check before FOR ALL ENTRIES\r\n - Nested LOOPs deeper than 2 levels\r\n\r\n6. **Produce modernization notes** — Compare the code against CSPeach ABAP conventions. Note what could be modernized (OO migration, CDS view extraction, inline declarations, string templates, FILTER/REDUCE expressions, etc.). Do not rewrite the code — only observe.\r\n\r\n7. **List open questions** — Things that cannot be determined from the code alone: undocumented business logic branches, unclear parameter meanings, dependencies that could not be resolved, authorization objects that may be missing.\r\n\r\n8. **Forge Rules compliance** — This skill is read-only (Forge Rule 6). No writes are performed. All inferences are labeled. Missing information is stated explicitly — nothing is invented.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\nProduce the following markdown output exactly:\r\n\r\n```\r\n## ABAP Radar — [OBJECT NAME]\r\n\r\n**Object Type:** [e.g., Report / Global Class / Function Module / CDS View / BAdI Impl]\r\n**Analyzed:** [Paste / MCP Retrieved / Partial — first N lines only]\r\n\r\n---\r\n\r\n### Probable Purpose\r\n[2–3 sentence plain-language summary of what this object does.]\r\n[INFERRED / CONFIRMED / UNKNOWN]\r\n\r\n---\r\n\r\n### Dependencies\r\n\r\n| Type | Object | Notes |\r\n|------|--------|-------|\r\n| DB Table | VBAK | Sales order header — SELECT |\r\n| DB Table | VBAP | Sales order items — SELECT |\r\n| DB Table | KONV | Condition values — SELECT in LOOP ⚠️ |\r\n| Function Module | CONVERSION_EXIT_ALPHA_INPUT | Input conversion — standard FM |\r\n| Auth Object | V_VBAK_AAT | Authority check for order type |\r\n| Message Class | ZSD_MESSAGES | Custom SD messages |\r\n\r\n---\r\n\r\n### Business Process Area\r\n[e.g., Sales and Distribution (SD) — Order-to-Cash]\r\nConfidence: [High / Medium / Low] — [reason]\r\n\r\n---\r\n\r\n### Risk Areas\r\n\r\n| Severity | Pattern | Location | Detail |\r\n|----------|---------|----------|--------|\r\n| HIGH | SELECT inside LOOP | Line ~47 | SELECT on KONV inside LOOP AT lt_vbap — N+1 queries |\r\n| HIGH | Missing empty check | Line ~43 | FOR ALL ENTRIES without IS NOT INITIAL guard |\r\n| MEDIUM | SELECT * | Line ~31 | VBAK read fetches all columns — unspecified field list |\r\n| MEDIUM | TABLES statement | Line ~12 | Obsolete coupling — use typed parameters |\r\n| LOW | Hard-coded plant | Line ~88 | WERKS = '0001' — should be parameterized |\r\n\r\n---\r\n\r\n### Modernization Notes\r\n- [ ] Migrate TABLES statement to typed parameter passing\r\n- [ ] Extract KONV query from inside loop — use FOR ALL ENTRIES with empty check\r\n- [ ] Replace SELECT * with explicit field list\r\n- [ ] Consider CDS view for VBAK/VBAP join instead of ABAP-level join\r\n- [ ] Replace CONCATENATE with string template |...|\r\n- [ ] Hard-coded plant '0001' should come from selection screen or config table\r\n\r\n---\r\n\r\n### Open Questions\r\n1. [Question about unclear logic or missing context]\r\n2. [Question about a dependency that could not be resolved]\r\n3. [Question about authorization coverage]\r\n\r\n---\r\n\r\n### Summary Assessment\r\n[2–3 sentence overall verdict: is this code stable, risky, ready to touch, needs refactor before extending, etc.]\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Never invent SAP objects.** If a dependency cannot be found in the source, do not guess it exists.\r\n- **Label all inferences.** Use `[INFERRED]`, `[CONFIRMED]`, or `[UNKNOWN]` where certainty matters.\r\n- **State missing information explicitly.** \"The source only shows lines 1–50; dependency analysis is incomplete\" is better than a confident but wrong answer.\r\n- **Read only.** This skill never calls `sap_set_source`, `sap_create_object`, `sap_delete_object`, or any write tool.\r\n- **Do not rewrite the code.** Modernization notes are observations, not proposals requiring immediate action. Use `/abap-refactor` for actual rewrites.\r\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind code generation), Rule 2 (no change without impact thinking), Rule 6 (read only by default).\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-radar\r\n\r\nREPORT zrep_pricing_analysis.\r\n\r\nTABLES: vbak, vbap.\r\n\r\nDATA: lt_orders TYPE TABLE OF vbak,\r\n ls_order TYPE vbak,\r\n lt_items TYPE TABLE OF vbap,\r\n ls_item TYPE vbap,\r\n lt_konv TYPE TABLE OF konv,\r\n ls_konv TYPE konv,\r\n lv_total TYPE p DECIMALS 2.\r\n\r\nSTART-OF-SELECTION.\r\n\r\n AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'\r\n ID 'AUART' FIELD 'TA'\r\n ID 'ACTVT' FIELD '03'.\r\n IF sy-subrc <> 0.\r\n MESSAGE 'No authorization' TYPE 'E'.\r\n ENDIF.\r\n\r\n SELECT * FROM vbak INTO TABLE lt_orders\r\n WHERE erdat = sy-datum\r\n AND auart = 'TA'.\r\n\r\n LOOP AT lt_orders INTO ls_order.\r\n SELECT * FROM vbap INTO TABLE lt_items\r\n WHERE vbeln = ls_order-vbeln.\r\n\r\n LOOP AT lt_items INTO ls_item.\r\n SELECT * FROM konv INTO TABLE lt_konv\r\n WHERE knumv = ls_item-knumv\r\n AND kposn = ls_item-posnr.\r\n\r\n LOOP AT lt_konv INTO ls_konv\r\n WHERE kschl = 'PR00'.\r\n lv_total = lv_total + ls_konv-kwert.\r\n ENDLOOP.\r\n ENDLOOP.\r\n ENDLOOP.\r\n\r\n WRITE: / 'Total pricing value:', lv_total.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## ABAP Radar — ZREP_PRICING_ANALYSIS\r\n\r\n**Object Type:** Report (executable program)\r\n**Analyzed:** Paste\r\n\r\n---\r\n\r\n### Probable Purpose\r\nThis report calculates a total pricing value across all standard sales orders (type TA) created today. It reads order headers, their line items, and condition records to accumulate the base price (condition type PR00) into a running total. The result is written to the screen using WRITE.\r\n[INFERRED] — no title or documentation found in the source.\r\n\r\n---\r\n\r\n### Dependencies\r\n\r\n| Type | Object | Notes |\r\n|------|--------|-------|\r\n| DB Table | VBAK | Sales order header — SELECT * all columns |\r\n| DB Table | VBAP | Sales order items — SELECT * inside LOOP ⚠️ |\r\n| DB Table | KONV | Condition values — SELECT * inside nested LOOP ⚠️⚠️ |\r\n| Auth Object | V_VBAK_AAT | Authority check for order type TA, activity 03 (display) |\r\n\r\n---\r\n\r\n### Business Process Area\r\nSales and Distribution (SD) — Pricing / Order-to-Cash reporting.\r\nConfidence: High — VBAK, VBAP, and KONV are core SD tables; condition type PR00 is the standard base price condition.\r\n\r\n---\r\n\r\n### Risk Areas\r\n\r\n| Severity | Pattern | Location | Detail |\r\n|----------|---------|----------|--------|\r\n| HIGH | SELECT inside LOOP | Line ~31 | SELECT on VBAP inside LOOP AT lt_orders — one query per order |\r\n| HIGH | SELECT inside nested LOOP | Line ~36 | SELECT on KONV inside LOOP AT lt_items — one query per line item. For 100 orders × 10 items = 1,000 KONV queries |\r\n| HIGH | SELECT * | Lines 28, 31, 36 | All three SELECTs fetch every column from VBAK, VBAP, KONV. Only a handful of fields are used |\r\n| MEDIUM | TABLES statement | Line 3 | TABLES: vbak, vbap — obsolete. Creates implicit global work areas and tight coupling |\r\n| MEDIUM | WRITE output | Line ~49 | WRITE statement used for output — not suitable for background jobs or integration |\r\n| LOW | Hard-coded order type | Line ~30 | AUART = 'TA' hard-coded — should be a selection screen parameter |\r\n| LOW | Hard-coded condition type | Line ~42 | KSCHL = 'PR00' hard-coded — should be configurable |\r\n| LOW | No date range selection | N/A | Only today's orders (sy-datum) — no selection screen for flexibility |\r\n\r\n---\r\n\r\n### Modernization Notes\r\n- [ ] Replace three-level nested SELECT-in-LOOP with a single CDS view joining VBAK, VBAP, KONV, then one SELECT into an internal table\r\n- [ ] Remove TABLES statement — use typed local structures\r\n- [ ] Replace SELECT * with explicit field lists: only VBELN, AUART, ERDAT from VBAK; VBELN, POSNR, KNUMV from VBAP; KNUMV, KPOSN, KSCHL, KWERT from KONV\r\n- [ ] Add selection screen with date range (ERDAT), order type (AUART), sales org (VKORG)\r\n- [ ] Replace WRITE output with ALV grid (CL_SALV_TABLE) for usability\r\n- [ ] Consider using standard FM SD_SALESDOCUMENT_READ or CDS view I_SalesOrder as the access layer instead of direct table reads\r\n- [ ] lv_total declared as TYPE P DECIMALS 2 — may lose precision for large values; consider DECFLOAT34 or CURR with currency key\r\n\r\n---\r\n\r\n### Open Questions\r\n1. Is this report run interactively or as a background job? WRITE output does not work in background — if it runs in batch, output is silently lost.\r\n2. What currency should lv_total be in? KONV-KWERT is in condition currency (KONV-WAERS). The code does not handle currency conversion or mixed currencies.\r\n3. Who consumes the total? Is this a one-off script or a production report? Its current state suggests it is not production-ready.\r\n4. Is authorization coverage sufficient? The check only covers order type TA with activity 03. If other order types are added later the auth check needs updating.\r\n\r\n---\r\n\r\n### Summary Assessment\r\nThis is a fragile legacy report with three levels of nested database queries that will perform extremely poorly at scale — 100 orders with 10 items each generates over 1,100 individual SELECT statements. It is readable and functionally correct for small datasets but must not be promoted to a high-volume system without a full rewrite of the data access layer. Before extending or modifying this program, extract the data access into a CDS view and flatten the loop structure.\r\n```\r\n",
184
+ "sha256": "4a202cbfd24a2eac7de612d48bfb634d1b861c22d7a357899214076b24eaa230",
178
185
  "signature": "",
179
- "signedAt": "2026-09-17T20:19:05.448Z"
186
+ "signedAt": "2026-09-28T14:37:04.919Z"
180
187
  },
181
188
  "abap-rap": {
182
189
  "name": "abap-rap",
183
190
  "body": "---\r\nname: abap-rap\r\ndescription: >\r\n Scaffold a complete RAP application stack — database table, CDS interface\r\n view, CDS projection view, behavior definition, behavior implementation,\r\n metadata extension, service definition, and service binding. Requires an\r\n MCP connection for actual creation in SAP. Follows all Forge Rules,\r\n especially Rule 1 (no blind generation) and Rule 8 (no batch without plan).\r\nphase: BUILD\r\nrequires_mcp: required_for_creation\r\nforge_rules: [1, 2, 7, 8, 9, 10]\r\nversion: \"1.2\"\r\n---\r\n\r\n# abap-rap\r\n\r\n## Purpose\r\n\r\nGenerate a complete, consistent RAP (RESTful Application Programming) stack\r\nfrom a set of business requirements. This skill handles the full creation\r\nsequence: database table (via CDS define table), CDS interface and projection\r\nviews, behavior definition, implementation class, service definition, service\r\nbinding, metadata extension, and access control.\r\n\r\nAll objects are consistent in naming (single prefix), field list, and associations.\r\nThe skill distinguishes managed vs unmanaged scenarios and generates the correct\r\nbehavior type accordingly. Templates in `templates/` provide the patterns.\r\n\r\n## When to Use\r\n\r\n- You need a new Fiori Elements app backed by OData V4\r\n- You are building a new business entity from scratch in S/4HANA or BTP ABAP\r\n- You need a complete, consistent object stack — not just one CDS view\r\n- You want to scaffold quickly and fill in business logic afterward\r\n\r\nDo NOT use this skill if:\r\n- You only need a single CDS view (use `/abap-generate`)\r\n- You are extending an existing RAP object (use `/abap-enhance`)\r\n- You are working in ECC without ABAP Platform 1909+ (RAP not supported)\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Business entity name (e.g. \"Travel Booking\", \"Purchase Requisition Item\")\r\n- Scenario type: managed or unmanaged\r\n- Field list with types (or a description to derive them)\r\n- Target system: S/4HANA on-prem version or BTP ABAP\r\n\r\nOptional (will be asked):\r\n- Naming prefix (Z_ or Y_ or customer namespace)\r\n- Package name and transport request\r\n- Whether draft handling is needed\r\n- Parent entity for sub-node scenarios\r\n- Which standard operations to enable (Create / Read / Update / Delete)\r\n- Whether OData V4 service binding is needed\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Gather Requirements\r\n\r\nAsk the following before generating anything:\r\n\r\n1. What is the business entity? (name and short description)\r\n2. Managed or unmanaged implementation?\r\n3. What fields does the entity need? (name, type, label — or describe the domain)\r\n4. What CRUD operations are needed?\r\n5. Is draft handling required?\r\n6. What naming prefix? (e.g. Z, Y, ZTRV, ZMMT)\r\n7. Package and transport?\r\n8. Should I create the objects in SAP via MCP?\r\n\r\n**Database design questions** (ask if not already clear from the field list):\r\n\r\n9. Which fields form the primary key?\r\n10. Are there foreign key relationships to other tables? (e.g., company code → T001, customer → KNA1)\r\n11. Are there currency amount or quantity fields? (each needs a paired currency key / unit of measure field)\r\n12. Client-dependent (needs `key client : abap.clnt not null` as first key field) or client-independent?\r\n13. Draft enabled? (needs a separate draft table — fields named after the CDS view ELEMENT names, not the DB columns, plus the sych_bdl_draft_admin_inc admin include; see managed-scenario template)\r\n\r\n**How to ask:** use the `ask_question` tool and batch related questions — up to 4 per call via the `questions` array. The user answers each batch in a single form; one form beats a 13-round-trip interrogation. Natural batches: entity + managed/unmanaged + fields + CRUD, then prefix + package/transport + draft + create-via-MCP, then the database-design set. Keep each question's `header` ≤12 chars. Skip anything already answered by the user's prompt or `<session_context>`.\r\n\r\n### Step 2 — Derive and Show the Full Object Plan\r\n\r\nBased on answers, derive the complete object list. Present a table:\r\n\r\n| Step | Object Type | Object Name | Type Code | Description |\r\n|------|-------------------|-------------------------|-----------|----------------------------------|\r\n| 1 | Database Table | {PREFIX}TRAVEL | TABL/DT | CDS define table + activate |\r\n| 1b | Draft Table | {TABLE}_D | TABL/DT | **Only when draft = Yes.** Created BEFORE the BDEF activates; fields = CDS element names (NOT DB column names) + `sych_bdl_draft_admin_inc` |\r\n| 1c | Value-help view(s)| ZVH_{CODED_FIELD} | DDLS/DF | **Only when any field is coded** (status/type/category with a fixed code list) — one ZVH_ view per coded field, active before ANY view that references it (the interface view when the text association lives there, otherwise the projection) |\r\n| 2 | CDS Root View | I_{PREFIX}Travel | DDLS/DF | Interface / BO view |\r\n| 3 | CDS Projection | C_{PREFIX}Travel | DDLS/DF | Consumption / UI view |\r\n| 4 | Behavior Def (BO) | I_{PREFIX}Travel | BDEF/BN | BO behavior with locking |\r\n| 5 | Behavior Def (P) | C_{PREFIX}Travel | BDEF/BN | Projection behavior |\r\n| 6 | Impl Class | ZBP_{PREFIX}Travel | CLAS/OC | Handler and saver class |\r\n| 7 | Service Def. | UI_{PREFIX}Travel_O4 | SRVD/SRV | OData V4 service definition (activate before step 8) |\r\n| 8 | Service Binding | UI_{PREFIX}Travel_O4 | SRVB/SVB | OData V4 service binding (needs active SRVD) |\r\n| 9 | Metadata Ext. | C_{PREFIX}Travel | DDLX/EX | UI annotations |\r\n| 10 | Access Control | I_{PREFIX}Travel | DCLS/DL | DCL source (MANDATORY on interface views — rap-patterns.md §10; skipping requires an explicit user waiver recorded in the plan) |\r\n\r\nWait for confirmation before creating anything.\r\n\r\n### Step 3 — Generate or Create\r\n\r\nIf MCP is NOT connected: output all source code in order (table DDL, each CDS\r\nsource, BDEF source, ABAP class, service def/binding) as markdown code blocks.\r\n\r\nIf MCP IS connected, follow this exact creation order:\r\n\r\n```\r\n1. Package (DEVC/K) — if not using $TMP\r\n2. Database Table (TABL/DT) — create shell via sap_create_object, then write CDS\r\n define table source via sap_set_source (objectType: TABL), then activate\r\n → NO domains or data elements — use built-in ABAP types (abap.char, etc.)\r\n2b. Draft Table ({TABLE}_D, TABL/DT) — ONLY when draft = Yes. It is NOT generated\r\n automatically: create it yourself, and it must exist and be active BEFORE the\r\n BDEF activates (step 5). Fields are named after the CDS view ELEMENT names —\r\n NOT the DB column names — plus the `sych_bdl_draft_admin_inc` admin include.\r\n See templates/managed-scenario.md \"Notes on Draft Tables\".\r\n2c. Value-help view(s) (ZVH_*, DDLS/DF) — one per coded field. ONLY when the entity has\r\n any coded field (status/type/category with a fixed code list). Must be active before\r\n ANY view that references it — the interface view when the text association or\r\n @ObjectModel.text.element lives there, the projection view when only\r\n @Consumption.valueHelpDefinition does. Creating it here, ahead of both, is always safe.\r\n See templates/projection-layer.md §6 (incl. the DD07T fixed-values variant).\r\n3. CDS Interface View (DDLS/DF) — create + write define root view entity source + activate\r\n4. CDS Projection View (DDLS/DF) — create + write define root view entity as projection on + activate\r\n5. Behavior Definition (BDEF/BN) — create + write managed/unmanaged source + activate\r\n6. Behavior Pool Class (CLAS/OC) — create shell + write the GLOBAL class to main\r\n via sap_set_source, THEN write the handler (lhc_* local class) to the CCIMP\r\n include via sap_set_class_include (includeType: locals_imp) — see \"Behavior\r\n pool handler\" below — then activate\r\n7. Service Definition (SRVD/SRV) — create + write expose source + activate\r\n8. >>> ACTIVATE Service Definition — sap_activate must succeed before step 9 <<<\r\n9. Service Binding (SRVB/SVB) — create with serviceDefinition parameter set to the\r\n SRVD name from step 7 (SRVB creation requires an already-active service definition),\r\n then PUBLISH with sap_service_binding_publish (it activates the binding AND registers\r\n the OData endpoint — verify the result is success)\r\n10. Metadata Extension (DDLX/EX) — create + write @UI annotations + activate. MUST carry\r\n @EndUserText.label for EVERY exposed element that is not DTEL-backed (with built-in\r\n types that is ALL of them).\r\n11. Access Control (DCLS/DL) — create + write DCL source + activate\r\n```\r\n\r\n**Authorization is part of the stack — two hard rules (D25):**\r\n\r\n1. **DCL body — never improvise, use this shape** (adapt authorization object/field; when no field-level rule exists, use the inheriting form per rap-patterns.md §10):\r\n\r\n```cds\r\n@EndUserText.label: 'Access Control: <Entity>'\r\n@MappingRole: true\r\ndefine role ZI_<Entity> {\r\n grant select on ZI_<Entity>\r\n where ( <AuthField> ) = aspect pfcg_auth( <AuthObject>, <AuthObjField>, ACTVT = '03' );\r\n}\r\n```\r\n\r\nNever leave an exposed view `@AccessControl.authorizationCheck: #NOT_REQUIRED` (value-help-only views excepted).\r\n\r\n2. **BDEF authorization must be real:** default to `authorization master ( instance )` with `get_instance_authorizations` implemented in the behavior pool. `authorization master ( global )` that grants everything with no implemented check is the D25 failure mode — it requires the same explicit plan-level waiver as a missing DCL (`validated-with-waiver` + reason in `work.notes`), never a silent default.\r\n\r\n**⚠ ONE OBJECT AT A TIME — mandatory, not a fallback.** Build *and activate* each\r\nobject individually in the dependency order above: create → write source →\r\nactivate THAT object alone → verify → only then move to the next. Do this from\r\nthe start, not only after a batch fails.\r\n\r\n**NEVER pass more than one object to a single `sap_activate` call.** Batch\r\nactivation fails as a *cascade*: the first object's error (e.g. a CDS syntax\r\nissue) makes the framework report \"Activation was cancelled / Editing canceled\"\r\nfor every other object in the batch, so you cannot tell which objects are\r\nactually fine — and the rapid-fire write/activate churn is what stalls the SAP\r\nconnection (a single `sap_set_source`/`sap_activate` can then hang for minutes).\r\nSequential activation also lets each object's dependency (interface before\r\nprojection before metadata-extension/DCL) be active when the next one activates.\r\n\r\nFor each object, in order:\r\n- Call `sap_create_object` with the correct type and subtype codes above\r\n- Call `sap_set_source` to write the source (not needed for SRVB — no source)\r\n- Call `sap_activate` for **this one object only**\r\n- Call `sap_inactive_objects` to verify activation (Rule 10)\r\n- On any syntax/activation error, FIX this object and re-activate it before\r\n touching the next — do not continue to the next object with this one broken\r\n- After all objects are created, report the full status table\r\n- Service binding: it has no source — after `sap_create_object` (with the\r\n `serviceDefinition` param), call `sap_service_binding_publish`. That one call\r\n activates the binding AND registers the OData endpoint (publishjobs). Confirm\r\n the result is success — a published binding is required before the service URL\r\n returns data. (No separate `sap_activate`/`sap_set_source` for the SRVB.)\r\n\r\n**Behavior pool handler — it lives in the CCIMP include, NOT main.** A managed/\r\nunmanaged behavior pool's handler is a local class `lhc_<entity>` that MUST live\r\nin the class's CCIMP (\"Local Types\") include. It does NOT compile in the class\r\nmain source (main holds only the global `FOR BEHAVIOR OF` class), and SAP rejects\r\nit appended after the global ENDCLASS (\"CLASS … DEFINITION is unexpected\"). Write\r\nit with the dedicated tool — do NOT report this as a manual-paste blocker:\r\n\r\n1. `sap_create_object` the behavior class (CLAS/OC).\r\n2. `sap_set_source` the GLOBAL class to main — a minimal shell:\r\n `CLASS zbp_<...> DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF Z<Root>.`\r\n `ENDCLASS.` `CLASS zbp_<...> IMPLEMENTATION. ENDCLASS.`\r\n3. `sap_set_class_include` with `includeType: locals_imp` and `source` = the full\r\n handler: `CLASS lhc_<entity> DEFINITION INHERITING FROM cl_abap_behavior_handler.`\r\n `… ENDCLASS.` + `CLASS lhc_<entity> IMPLEMENTATION. … ENDCLASS.` (the\r\n determinations, validations, and authorizations declared in the BDEF).\r\n3b. **CHECK the write result — `written:true` is read-back verified.** The tool\r\n writes the canonical ADT path (`/includes/implementations`) and reads the\r\n include back before returning, so `written:true` (with `verified:true`) means\r\n the handler is genuinely persisted — trust it and proceed to step 4. Only if\r\n `written:false`:\r\n - The PUT did not persist on this kernel (rare — same class as SE11 tech-\r\n settings / SM30 GUI-only objects). Do NOT activate the empty class and claim\r\n success — that produces a broken BO with no handler.\r\n - FALL BACK: report the phase BLOCKED, give the exact ADT manual-paste recipe\r\n (open the behavior pool class → \"Local Types\" tab → paste the handler → save →\r\n activate), and record it in `work.notes`. Let the developer paste, then re-run.\r\n - (Optional defense-in-depth: call `sap_class_includes` (includeType\r\n `locals_imp`) and confirm your `lhc_<entity>` class is in the source.)\r\n4. Once `written:true` (or 3b's read-back confirms the handler): `sap_activate`\r\n the class, then `sap_inactive_objects` to verify (Rule 10).\r\n\r\n**Type code reference:**\r\n\r\n| Object | objectType | subObjectType |\r\n|--------|-----------|---------------|\r\n| Package | DEVC | K |\r\n| Database Table | TABL | DT |\r\n| CDS View (Interface / Projection) | DDLS | DF |\r\n| Behavior Definition | BDEF | BN |\r\n| Implementation Class | CLAS | OC |\r\n| Service Definition | SRVD | SRV |\r\n| Service Binding | SRVB | SVB |\r\n| Metadata Extension | DDLX | EX |\r\n| Access Control | DCLS | DL |\r\n\r\n### Step 4 — Post-Creation Guidance\r\n\r\nAfter all code is generated or created:\r\n- List any manual steps needed in ADT (activate service binding, publish)\r\n- Note which objects need business logic filled in\r\n- Recommend running `/abap-test` to scaffold unit tests\r\n- Recommend running `/abap-review` once logic is added\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## RAP Stack Plan — {Entity Name}\r\n\r\n**Scenario:** {Managed | Unmanaged}\r\n**Prefix:** {prefix}\r\n**Package:** {package}\r\n**Draft Handling:** {Yes | No}\r\n**Operations:** {CRUD subset}\r\n\r\n### Object List\r\n| Step | Type | Name | Notes |\r\n|------|------|------|-------|\r\n| ... | ... | ... | ... |\r\n\r\n---\r\n\r\n## Object Sources\r\n\r\n### 1. Database Table: {TABLE_NAME}\r\n```ddl\r\n{table_ddl}\r\n```\r\n\r\n### 2. CDS Interface View: {I_VIEW_NAME}\r\n```cds\r\n{interface_view_source}\r\n```\r\n\r\n### 3. CDS Projection View: {C_VIEW_NAME}\r\n```cds\r\n{projection_view_source}\r\n```\r\n\r\n### 4. Behavior Definition (BO): {BDEF_NAME}\r\n```bdef\r\n{bo_bdef_source}\r\n```\r\n\r\n### 5. Behavior Definition (Projection): {PROJ_BDEF_NAME}\r\n```bdef\r\n{projection_bdef_source}\r\n```\r\n\r\n### 6. Implementation Class: {CLASS_NAME}\r\n```abap\r\n{class_source}\r\n```\r\n\r\n### 7. Metadata Extension: {DDLX_NAME}\r\n```ddlx\r\n{metadata_extension_source}\r\n```\r\n\r\n### 8. Service Definition: {SRVD_NAME} (SRVD/SRV)\r\n```srvd\r\n{service_definition_source}\r\n```\r\n**Note: Activate this service definition before creating the service binding.**\r\n\r\n### 9. Service Binding: {SRVB_NAME} (SRVB/SVB)\r\nBinding type: OData V4 — UI\r\nCreated via sap_create_object with `serviceDefinition` parameter set to {SRVD_NAME}.\r\nThe service definition (step 8) must be active before this step.\r\n\r\n---\r\n\r\n## Manual Steps Required\r\n\r\n1. All CDS views and BDEFs are activated during MCP creation — verify in ADT if any show inactive\r\n2. Activate service binding in ADT (right-click → Activate) — cannot be done via MCP\r\n3. Publish the service binding (right-click → Publish Local Service Endpoint)\r\n4. Fill in validation / determination / action logic in {CLASS_NAME}\r\n5. Verify the service is visible in the Service Binding preview before testing in Fiori Elements\r\n\r\n## Creation Log (MCP Mode)\r\n| Object | Status | Notes |\r\n|--------|--------|-------|\r\n| {name} | Created / Failed | {syntax_check_result} |\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER generate the full stack without confirming the object plan first\r\n- NEVER create objects in SAP without explicit per-object or blanket confirmation\r\n- NEVER claim a service is ready for Fiori without noting that binding activation\r\n is a manual ADT step\r\n- NEVER mix managed and unmanaged patterns in the same BDEF\r\n- Do not invent CDS annotations that do not exist in the target release\r\n- If BTP ABAP: only use released APIs and released DDIC types\r\n- If on-premise < 1909: warn that RAP is not supported\r\n- Distinguish what is generated skeleton from what requires business logic\r\n- Always state which fields are key vs non-key explicitly\r\n- NEVER expose a field without a label. Built-in-type fields (the default here — no\r\n domains/DTELs) have no label source, so every exposed element gets an explicit\r\n @EndUserText.label in the CDS view or the MDE.\r\n- Coded fields never ship raw. Any field with a fixed code list gets a value-help view\r\n (+ @Consumption.valueHelpDefinition) and text display (@ObjectModel.text.element +\r\n #TEXT_ONLY) as part of the standard stack — plan the ZVH_ view in Step 2 when any\r\n field is coded.\r\n- Draft tables are never auto-generated. When draft = Yes, create {TABLE}_D yourself\r\n BEFORE activating the BDEF, with CDS ELEMENT names for its fields.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-rap\r\n\r\nBuild a managed RAP app for travel bookings. Entity: Travel. Fields:\r\n- TravelId (NUMC 8, key)\r\n- AgencyId (NUMC 6)\r\n- CustomerId (NUMC 8)\r\n- BeginDate (DATS)\r\n- EndDate (DATS)\r\n- TotalPrice (CURR 16,2) with currency key CurrencyCode\r\n- Status (CHAR 1) — open/accepted/rejected\r\n- Description (CHAR 1024)\r\n\r\nOperations: full CRUD. Draft enabled. Prefix: ZTRV. Package: ZRAP_TRAVEL.\r\nS/4HANA 2023 on-premise. Create in SAP via MCP.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## RAP Stack Plan — Travel\r\n\r\n**Scenario:** Managed (with draft)\r\n**Prefix:** ZTRV\r\n**Package:** ZRAP_TRAVEL\r\n**Draft Handling:** Yes\r\n**Operations:** Create, Read, Update, Delete\r\n\r\n### Object List\r\n| Step | Type | Name | Type Code | Notes |\r\n|------|--------------------|---------------------------|-----------|--------------------------------|\r\n| 1 | Database Table | ZTRV_TRAVEL | TABL/DT | CDS define table + activate |\r\n| 1a | Code-list Table | ZTRV_STATUS_T | TABL/DT | Status code + text — the source ZVH_TRVSTATUS reads (no domain, so no DD07T entries) |\r\n| 1b | Draft Table | ZTRV_TRAVEL_D | TABL/DT | Draft = Yes → created manually BEFORE the BDEF activates; fields = CDS element names + sych_bdl_draft_admin_inc |\r\n| 1c | Value-help view | ZVH_TRVSTATUS | DDLS/DF | Status is coded (O/A/R) → code-list view + text for OverallStatus. BEFORE ZI_TRVTRAVEL: the interface view associates to it |\r\n| 2 | CDS Interface View | ZI_TRVTRAVEL | DDLS/DF | Root view entity |\r\n| 3 | CDS Projection | ZC_TRVTRAVEL | DDLS/DF | UI consumption view |\r\n| 4 | BDEF (BO) | ZI_TRVTRAVEL | BDEF/BN | Managed + draft |\r\n| 5 | BDEF (Projection) | ZC_TRVTRAVEL | BDEF/BN | Use ETag master |\r\n| 6 | Impl Class | ZBP_TRVTRAVEL | CLAS/OC | Validation + determination |\r\n| 7 | Service Definition | ZUI_TRVTRAVEL_O4 | SRVD/SRV | Expose ZC_TRVTRAVEL — activate before SRVB |\r\n| 8 | Service Binding | ZUI_TRVTRAVEL_O4 | SRVB/SVB | OData V4 — UI binding (needs active SRVD) |\r\n| 9 | Metadata Extension | ZC_TRVTRAVEL | DDLX/EX | Fiori field labels + layout |\r\n| 10 | Access Control | ZI_TRVTRAVEL | DCLS/DL | DCL for ZI_TRVTRAVEL |\r\n\r\nConfirmed — proceeding to creation.\r\n\r\n---\r\n\r\n## Object Sources\r\n\r\n### 1. Database Table: ZTRV_TRAVEL\r\n```ddl\r\n@EndUserText.label: 'Travel booking persistence table'\r\n@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE\r\n@AbapCatalog.tableCategory: #TRANSPARENT\r\n@AbapCatalog.deliveryClass: #A\r\n@AbapCatalog.dataMaintenance: #RESTRICTED\r\ndefine table ztrv_travel {\r\n key client : abap.clnt not null;\r\n key travel_id : abap.numc(8) not null;\r\n agency_id : abap.numc(6);\r\n customer_id : abap.numc(8);\r\n begin_date : abap.dats;\r\n end_date : abap.dats;\r\n @Semantics.amount.currencyCode: 'currency_code'\r\n total_price : abap.curr(16,2);\r\n currency_code : abap.cuky;\r\n overall_status : abap.char(1); // coded field (O/A/R) → needs a ZVH_ code-list view + text display\r\n description : abap.char(1024);\r\n created_by : abp_creation_user;\r\n created_at : abp_creation_tstmpl;\r\n local_last_changed_by : abp_locinst_lastchange_user;\r\n local_last_changed_at : abp_locinst_lastchange_tstmpl;\r\n last_changed_at : abp_lastchange_tstmpl;\r\n}\r\n```\r\n\r\n### 2. CDS Interface View: ZI_TRVTRAVEL\r\n```cds\r\n@AccessControl.authorizationCheck: #CHECK\r\n@EndUserText.label: 'Travel BO Interface View'\r\ndefine root view entity ZI_TRVTRAVEL\r\n as select from ztrv_travel\r\n association [0..1] to ZVH_TRVSTATUS as _Status on $projection.OverallStatus = _Status.Code\r\n{\r\n @EndUserText.label: 'Travel ID'\r\n key travel_id as TravelId,\r\n @EndUserText.label: 'Agency ID'\r\n agency_id as AgencyId,\r\n @EndUserText.label: 'Customer ID'\r\n customer_id as CustomerId,\r\n @EndUserText.label: 'Begin Date'\r\n begin_date as BeginDate,\r\n @EndUserText.label: 'End Date'\r\n end_date as EndDate,\r\n @EndUserText.label: 'Total Price'\r\n @Semantics.amount.currencyCode: 'CurrencyCode'\r\n total_price as TotalPrice,\r\n @EndUserText.label: 'Currency'\r\n currency_code as CurrencyCode,\r\n @EndUserText.label: 'Status'\r\n @ObjectModel.text.element: ['OverallStatusText']\r\n overall_status as OverallStatus,\r\n @EndUserText.label: 'Status Description'\r\n _Status.Description as OverallStatusText,\r\n @EndUserText.label: 'Description'\r\n description as Description,\r\n @EndUserText.label: 'Created By'\r\n created_by as CreatedBy,\r\n @EndUserText.label: 'Created At'\r\n created_at as CreatedAt,\r\n @EndUserText.label: 'Last Changed By'\r\n local_last_changed_by as LocalLastChangedBy,\r\n @EndUserText.label: 'Last Changed At'\r\n local_last_changed_at as LocalLastChangedAt,\r\n @EndUserText.label: 'Last Changed At (ETag)'\r\n last_changed_at as LastChangedAt,\r\n\r\n _Status\r\n}\r\n```\r\n\r\nEvery exposed element carries an explicit `@EndUserText.label` — the fields are typed\r\nwith built-in ABAP types (no DTEL), so there is no label to inherit. `OverallStatus` is\r\na coded field: it gets the `ZVH_TRVSTATUS` code-list view (association `_Status`), a\r\ntext element via `@ObjectModel.text.element`, and — in the projection —\r\n`@Consumption.valueHelpDefinition` plus `@UI.textArrangement: #TEXT_ONLY` so the list\r\nshows \"Accepted\", not `A`.\r\n\r\n`ZVH_TRVSTATUS` reads a small Z code-list table (`ZTRV_STATUS_T`: status code + text,\r\ncreated alongside the main table) — neither §6 variant fits here, because\r\n`overall_status` is a plain `abap.char(1)` with no domain and no check table, so it has\r\nno fixed values in `DD07T` and no check table to read. **Because `ZI_TRVTRAVEL`\r\nassociates to it, `ZVH_TRVSTATUS` must be created and ACTIVE before the interface view\r\nactivates** — hence step 1c, not a later step.\r\n\r\n*(projection view, BDEF, impl class, metadata ext, service def follow same pattern — see templates/)*\r\n\r\nFor analytical stacks (ALP), see templates/analytical-layer.md — dimension/cube/query pattern and phase order.\r\n\r\n---\r\n\r\n## Creation Log (MCP Mode)\r\n| Step | Object | Type | Status | Syntax Check |\r\n|------|------------------|---------|---------|--------------|\r\n| 1 | ZTRV_TRAVEL | TABL/DT | Created | Passed (CDS table activated) |\r\n| 1a | ZTRV_STATUS_T | TABL/DT | Created | Passed — status code list source |\r\n| 1b | ZTRV_TRAVEL_D | TABL/DT | Created | Passed — draft twin, CDS element names, created before the BDEF |\r\n| 1c | ZVH_TRVSTATUS | DDLS/DF | Created | Passed — status code list, activated before ZI_TRVTRAVEL |\r\n| 2 | ZI_TRVTRAVEL | DDLS/DF | Created | Passed |\r\n| 3 | ZC_TRVTRAVEL | DDLS/DF | Created | Passed |\r\n| 4 | ZI_TRVTRAVEL | BDEF/BN | Created | Passed (BDEF)|\r\n| 5 | ZC_TRVTRAVEL | BDEF/BN | Created | Passed (BDEF)|\r\n| 6 | ZBP_TRVTRAVEL | CLAS/OC | Created | Passed |\r\n| 7 | ZUI_TRVTRAVEL_O4 | SRVD/SRV| Created | Passed — activated before SRVB |\r\n| 8 | ZUI_TRVTRAVEL_O4 | SRVB/SVB| Created | N/A (no source) |\r\n| 9 | ZC_TRVTRAVEL | DDLX/EX | Created | Passed (DDLX)|\r\n| 10 | ZI_TRVTRAVEL | DCLS/DL | Created | Passed (DCL) |\r\n\r\n## Manual Steps Required\r\n1. Open ZUI_TRVTRAVEL_O4 service binding in ADT → right-click → Activate\r\n2. Right-click → Publish Local Service Endpoint\r\n3. Add validation logic in ZBP_TRVTRAVEL for date range and status transitions\r\n4. Run /abap-test to generate test class for ZBP_TRVTRAVEL\r\n```\r\n",
184
191
  "sha256": "58aa508d576bbe7d85f6fbd846b65ef041c75aab751f763c8d1a62ab86ab940b",
185
192
  "signature": "",
186
- "signedAt": "2026-09-17T20:19:05.495Z"
193
+ "signedAt": "2026-09-28T14:37:04.948Z"
187
194
  },
188
195
  "abap-refactor": {
189
196
  "name": "abap-refactor",
190
197
  "body": "---\r\nname: abap-refactor\r\ndescription: >\r\n Transform legacy ABAP code to modern patterns — procedural to object-oriented,\r\n classic syntax to new ABAP syntax, SELECT with work areas to inline\r\n declarations, FORM routines to methods, nested IF to guard clauses. Also the\r\n skill for modifying or adding a feature to EXISTING custom (Z/Y) code in place —\r\n including small in-place changes like adding output colouring or a new column.\r\n Analyzes first, proposes a plan, generates only on confirmation.\r\nphase: BUILD\r\nrequires_mcp: optional\r\nforge_rules: [1, 2, 6, 7, 10]\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.3.0\"\r\nwidget: diff-viewer\r\n---\r\n\r\n# abap-refactor\r\n\r\n## Purpose\r\n\r\nSystematically modernize legacy ABAP code by identifying specific refactoring\r\nopportunities, explaining the benefit of each change, and producing a clean\r\nmodern version. The skill never silently changes behavior — every transformation\r\nis justified and any behavior-affecting change is flagged explicitly.\r\n\r\nTarget improvements include:\r\n- FORM routines → local/global class methods\r\n- SELECT into work area with loop → SELECT into internal table with inline declaration\r\n- MOVE/COMPUTE → direct assignment\r\n- Nested IF → guard clauses / early RETURN\r\n- String CONCATENATE → string templates\r\n- FIELD-SYMBOLS typed as ANY → typed FIELD-SYMBOLS or VALUE expressions\r\n- CREATE OBJECT → NEW\r\n- Classic ALV (REUSE_ALV_*) → CL_SALV_TABLE or cl_gui_alv_grid\r\n- Exception text ID strings → exception class CX_\r\n- Hard-coded magic values → named constants or enum-like class attributes\r\n\r\n## When to Use\r\n\r\n- You have a working FORM-based report and want to convert it to OO\r\n- You want to prepare legacy code for an S/4HANA migration\r\n- ATC is reporting performance warnings or syntax warnings on old code\r\n- A code review identified specific anti-patterns to address\r\n- You want to reduce technical debt before adding new features\r\n\r\nDo NOT use this skill to generate new code from scratch — use `/abap-generate`.\r\nDo NOT use this skill to review code without changing it — use `/abap-review`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- The ABAP source code to be refactored (paste it directly)\r\n\r\nUseful (will be asked):\r\n- Target environment (ECC, S/4HANA, BTP ABAP) — affects which syntax is valid\r\n- Specific concerns: performance, testability, readability, cloud-readiness\r\n- Whether the refactored code should be a replacement or a new object\r\n- Whether to generate unit tests alongside the refactored code\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Analyze the Code\r\n\r\nRead the provided code. Identify:\r\n- Total lines, structure (report / class / function group / include)\r\n- FORM routines count and purpose\r\n- SELECT patterns (SELECT SINGLE, SELECT...ENDSELECT loops, SELECT * usage)\r\n- String operations (CONCATENATE, old TEXT-xxx literals)\r\n- Object creation patterns (CREATE OBJECT)\r\n- Error handling (CALL FUNCTION with EXCEPTIONS sy-subrc, or exception classes)\r\n- Performance risks (SELECT inside loops, unbuffered table reads)\r\n- Dead code (unreachable branches, unused variables)\r\n\r\n### Step 2 — Produce Refactoring Opportunity List\r\n\r\nPresent a numbered list of findings grouped by category:\r\n\r\n**Structural:**\r\n- Number of FORM routines that should become methods\r\n- Whether a class wrapper is needed\r\n\r\n**Syntax:**\r\n- Specific deprecated statements found\r\n\r\n**Performance:**\r\n- SELECT inside loops\r\n- Missing WHERE clause restrictions\r\n\r\n**Testability:**\r\n- Hard dependencies that block unit testing\r\n- Missing interfaces\r\n\r\n### Step 3 — Propose a Refactoring Plan\r\n\r\nFor each category, list:\r\n- What will change\r\n- Why (readability / performance / cloud readiness / testability)\r\n- Whether the change affects runtime behavior (flag any that do)\r\n- Estimated complexity: Low / Medium / High\r\n\r\nAsk: \"Shall I proceed with the full refactoring? Or select specific items?\"\r\n\r\n### Step 4 — Generate Refactored Code\r\n\r\nProduce the modernized code with:\r\n- Side-by-side change summary at the top\r\n- Full refactored source\r\n- Inline comments where non-obvious changes were made\r\n\r\n### Step 5 — Highlight Behavior-Affecting Changes\r\n\r\nAfter the code, explicitly list any changes that alter observable behavior\r\n(e.g. exception raised instead of sy-subrc check, different field selection\r\nin SELECT). These MUST be tested before the refactored code goes to production.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Refactoring Analysis\r\n\r\n**Object:** {object_name}\r\n**Type:** {report | class | function group | include}\r\n**Lines:** {line_count}\r\n**Environment:** {environment}\r\n\r\n---\r\n\r\n## Findings\r\n\r\n### Structural\r\n- {finding_1}\r\n- {finding_2}\r\n\r\n### Syntax (deprecated / old patterns)\r\n- {finding_3}\r\n- {finding_4}\r\n\r\n### Performance\r\n- {finding_5}\r\n\r\n### Testability\r\n- {finding_6}\r\n\r\n---\r\n\r\n## Refactoring Plan\r\n\r\n| # | Change | Category | Behavior Impact | Complexity |\r\n|---|-------------------------------|---------------|-----------------|------------|\r\n| 1 | {change_description} | Structural | None | Medium |\r\n| 2 | {change_description} | Syntax | None | Low |\r\n| 3 | {change_description} | Performance | Possible | Medium |\r\n\r\n---\r\n\r\n## Refactored Code\r\n\r\n### Change Summary\r\n- FORM routines → methods of class {new_class_name}\r\n- SELECT * → explicit field list\r\n- {other_changes}\r\n\r\n```abap\r\n{full_refactored_source}\r\n```\r\n\r\n---\r\n\r\n## Behavior-Affecting Changes — Must Test\r\n\r\n| Change | Old Behavior | New Behavior |\r\n|-------------------------------------|-------------------------|---------------------------|\r\n| {change} | {old} | {new} |\r\n\r\n## Recommended Next Steps\r\n- [ ] Run syntax check on refactored code\r\n- [ ] Run /abap-test to generate unit tests\r\n- [ ] Run /abap-review on the refactored version\r\n- [ ] Run /abap-atc-fix to verify no new ATC findings\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method logic — use `sap_update_method` per method instead (Rule 7a)\r\n- NEVER silently change behavior — always flag behavior-affecting changes\r\n- NEVER remove error handling code — only modernize it\r\n- NEVER replace a working SELECT with a released API without confirming the API\r\n exists in the target system\r\n- If the code uses function modules that are not released in cloud environments,\r\n note this but do not remove them unless a direct replacement is confirmed\r\n- Do not over-engineer: a short FORM routine that is only called once may not\r\n need to be a class — flag but do not force\r\n- If the code is ECC-specific and the target is S/4HANA cloud, run\r\n `/abap-cloud-check` first to understand API compatibility\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-refactor\r\n\r\nRefactor this legacy ALV report. It's a standalone report (REPORT ZALV_PO_LIST)\r\nused to list open purchase orders. Target: S/4HANA 2023 on-prem. I want better\r\ntestability and modern syntax.\r\n\r\nREPORT zalv_po_list.\r\n\r\nTABLES: ekko, ekpo.\r\n\r\nDATA: gt_po TYPE TABLE OF ekko,\r\n gs_po TYPE ekko,\r\n gt_items TYPE TABLE OF ekpo,\r\n gs_item TYPE ekpo.\r\n\r\nSELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.\r\nPARAMETERS: p_bukrs TYPE bukrs OBLIGATORY,\r\n p_erdat TYPE erdat.\r\nSELECTION-SCREEN END OF BLOCK b1.\r\n\r\nSTART-OF-SELECTION.\r\n PERFORM read_data.\r\n PERFORM display_alv.\r\n\r\nFORM read_data.\r\n SELECT * FROM ekko INTO TABLE gt_po\r\n WHERE bukrs = p_bukrs\r\n AND erdat >= p_erdat\r\n AND loekz = space.\r\n IF sy-subrc <> 0.\r\n MESSAGE 'No purchase orders found' TYPE 'I'.\r\n ENDIF.\r\n\r\n LOOP AT gt_po INTO gs_po.\r\n SELECT * FROM ekpo INTO TABLE gt_items\r\n WHERE ebeln = gs_po-ebeln.\r\n \"process items\r\n ENDLOOP.\r\nENDFORM.\r\n\r\nFORM display_alv.\r\n CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'\r\n EXPORTING\r\n i_structure_name = 'EKKO'\r\n TABLES\r\n t_outtab = gt_po\r\n EXCEPTIONS\r\n OTHERS = 1.\r\nENDFORM.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Refactoring Analysis\r\n\r\n**Object:** ZALV_PO_LIST\r\n**Type:** Report (procedural, FORM-based)\r\n**Lines:** ~45 (provided excerpt)\r\n**Environment:** S/4HANA 2023 on-premise\r\n\r\n---\r\n\r\n## Findings\r\n\r\n### Structural\r\n- 2 FORM routines (read_data, display_alv) — should become class methods\r\n- Global data declared at report level — prevents unit testing\r\n- TABLES statement (ekko, ekpo) — obsolete, header lines not needed\r\n\r\n### Syntax (deprecated / old patterns)\r\n- SELECT * FROM ekko/ekpo — should select explicit fields\r\n- MOVE not present but implicit TABLES header line pattern\r\n- REUSE_ALV_GRID_DISPLAY — classic function module, not released in cloud\r\n\r\n### Performance\r\n- SELECT * FROM ekpo inside LOOP AT gt_po — N+1 query problem\r\n Should be replaced with a single join or a FOR ALL ENTRIES select\r\n\r\n### Testability\r\n- read_data FORM directly accesses DB — cannot be unit tested without DB\r\n- display_alv FORM not testable (no output abstraction)\r\n\r\n---\r\n\r\n## Refactoring Plan\r\n\r\n| # | Change | Category | Behavior Impact | Complexity |\r\n|---|-------------------------------------|---------------|-----------------------|------------|\r\n| 1 | Wrap in ZCL_PO_LIST_REPORT class | Structural | None | Medium |\r\n| 2 | FORM read_data → METHOD read_data | Structural | None | Low |\r\n| 3 | Remove TABLES statement | Syntax | None | Low |\r\n| 4 | SELECT * → explicit fields | Syntax | None (more efficient) | Low |\r\n| 5 | Fix N+1 SELECT with FOR ALL ENTRIES | Performance | None (same result) | Medium |\r\n| 6 | REUSE_ALV → CL_SALV_TABLE | Syntax/Func | UI layout may differ | Medium |\r\n\r\n---\r\n\r\n## Refactored Code\r\n\r\n### Change Summary\r\n- Introduced ZCL_PO_LIST_REPORT wrapping all logic\r\n- Replaced N+1 SELECT with FOR ALL ENTRIES query\r\n- Replaced REUSE_ALV_GRID_DISPLAY with CL_SALV_TABLE\r\n- Explicit field lists on all SELECTs\r\n\r\n```abap\r\nREPORT zalv_po_list.\r\n\r\nSELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.\r\nPARAMETERS: p_bukrs TYPE bukrs OBLIGATORY,\r\n p_erdat TYPE erdat.\r\nSELECTION-SCREEN END OF BLOCK b1.\r\n\r\nSTART-OF-SELECTION.\r\n TRY.\r\n DATA(lo_report) = NEW zcl_po_list_report( iv_bukrs = p_bukrs\r\n iv_erdat = p_erdat ).\r\n lo_report->execute( ).\r\n CATCH zcx_po_list_error INTO DATA(lx_error).\r\n MESSAGE lx_error TYPE 'E'.\r\n ENDTRY.\r\n\r\n\"-------------------------------------------------------------------\r\n\" Local class — candidate for extraction to global class ZCL_PO_LIST_REPORT\r\n\"-------------------------------------------------------------------\r\nCLASS zcl_po_list_report DEFINITION FINAL CREATE PUBLIC.\r\n PUBLIC SECTION.\r\n METHODS constructor\r\n IMPORTING iv_bukrs TYPE bukrs\r\n iv_erdat TYPE erdat.\r\n METHODS execute RAISING zcx_po_list_error.\r\n\r\n PRIVATE SECTION.\r\n DATA mv_bukrs TYPE bukrs.\r\n DATA mv_erdat TYPE erdat.\r\n\r\n TYPES: BEGIN OF ty_po_display,\r\n ebeln TYPE ekko-ebeln,\r\n lifnr TYPE ekko-lifnr,\r\n bedat TYPE ekko-bedat,\r\n netwr TYPE ekko-netwr,\r\n waers TYPE ekko-waers,\r\n END OF ty_po_display.\r\n\r\n METHODS read_data\r\n RETURNING VALUE(rt_result) TYPE TABLE OF ty_po_display\r\n RAISING zcx_po_list_error.\r\n\r\n METHODS display_alv\r\n IMPORTING it_data TYPE TABLE.\r\nENDCLASS.\r\n\r\nCLASS zcl_po_list_report IMPLEMENTATION.\r\n\r\n METHOD constructor.\r\n mv_bukrs = iv_bukrs.\r\n mv_erdat = iv_erdat.\r\n ENDMETHOD.\r\n\r\n METHOD execute.\r\n DATA(lt_data) = read_data( ).\r\n IF lt_data IS INITIAL.\r\n MESSAGE 'No purchase orders found' TYPE 'I'.\r\n RETURN.\r\n ENDIF.\r\n display_alv( lt_data ).\r\n ENDMETHOD.\r\n\r\n METHOD read_data.\r\n SELECT ebeln, lifnr, bedat, netwr, waers\r\n FROM ekko\r\n WHERE bukrs = @mv_bukrs\r\n AND erdat >= @mv_erdat\r\n AND loekz = @space\r\n INTO TABLE @rt_result.\r\n\r\n IF sy-subrc <> 0.\r\n RETURN. \"Empty result — caller handles empty table\r\n ENDIF.\r\n ENDMETHOD.\r\n\r\n METHOD display_alv.\r\n TRY.\r\n DATA(lo_salv) = cl_salv_table=>factory(\r\n IMPORTING r_salv_table = DATA(lo_table)\r\n CHANGING t_table = it_data ).\r\n lo_table->get_functions( )->set_all( abap_true ).\r\n lo_table->get_display( )->set_striped_pattern( abap_true ).\r\n lo_table->display( ).\r\n CATCH cx_salv_msg INTO DATA(lx_salv).\r\n MESSAGE lx_salv TYPE 'E'.\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\nENDCLASS.\r\n```\r\n\r\n---\r\n\r\n## Behavior-Affecting Changes — Must Test\r\n\r\n| Change | Old Behavior | New Behavior |\r\n|-------------------------------|---------------------------------------|--------------------------------------|\r\n| REUSE_ALV → CL_SALV_TABLE | ALV uses EKKO structure field catalog | Field catalog auto-derived from type |\r\n| N+1 removed (items not shown) | Items loop present (no output shown) | Items loop removed — add back if needed |\r\n\r\n**Note:** The original code loaded ekpo items in a loop but did not display them\r\nin the ALV. The refactored version removes that dead code. If item display is\r\nneeded, add a JOIN or FOR ALL ENTRIES query and extend ty_po_display.\r\n```\r\n",
191
198
  "sha256": "1338cd109c261cb0a68a5c7700523468db0e5e6e2f225c6a078dc44d8a174f29",
192
199
  "signature": "",
193
- "signedAt": "2026-09-17T20:19:05.542Z"
200
+ "signedAt": "2026-09-28T14:37:04.976Z"
194
201
  },
195
202
  "abap-review": {
196
203
  "name": "abap-review",
197
204
  "body": "---\r\nname: abap-review\r\ndescription: >\r\n Review existing ABAP code, read-only, across selectable lenses: `quality`\r\n (default — 7 quality criteria + risk summary), `performance` (N+1/SELECT */index\r\n issues + prioritized fix list), `cloud` (clean-core / ABAP Cloud readiness).\r\n Produces a structured assessment; does not modify code. For automated fixing see\r\n `abap-atc-fix`; for refactoring see `abap-refactor`.\r\nphase: VERIFY\r\nrequires_mcp: false\r\nforge_rules: [3, 4, 6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-review\r\n\r\n## Purpose\r\n\r\nProvide a structured, honest code review of ABAP source code against seven\r\nquality criteria used by experienced SAP developers. The output is actionable:\r\nevery finding includes severity, location, and a concrete suggestion. The skill\r\nends with a risk summary table and a suggested improvement sequence.\r\n\r\nThis skill is read-only. It produces findings and recommendations, never modified\r\nsource code. Use `/abap-refactor` to apply changes and `/abap-atc-fix` for\r\nautomated ATC remediation.\r\n\r\nThis skill is the single read-only assessment pass. It carries three combinable\r\n**lenses** (see the Lens section): `quality` (default — the 7-criteria senior\r\nreview), `performance` (the deep performance hunt: N+1, SELECT *, index issues →\r\nprioritized fix list with impact), and `cloud` (clean-core / ABAP Cloud readiness →\r\nOverall Assessment with modernization guidance). It owns all three — performance\r\nand cloud-readiness assessment are lenses here, not separate skills.\r\n\r\n## When to Use\r\n\r\n- Before merging or releasing code to a transport\r\n- As a gate before promoting to QA or production\r\n- When inheriting code from another developer\r\n- As part of a code quality initiative or audit\r\n- After generating code with `/abap-generate` or scaffolding with `/abap-rap`\r\n- For a deep performance hunt on one object (`--lens performance`)\r\n- For a single-object clean-core / ABAP Cloud readiness check (`--lens cloud`)\r\n\r\nWhen NOT to use: auto-fixing ATC findings — `/abap-atc-fix`; actually changing the\r\ncode — `/abap-refactor`; an **estate-wide** clean-core / migration classification\r\nacross the whole custom codebase — `/abap-cca` (the `cloud` lens here is a\r\nsingle-object quick check, not a replacement for the CCA flagship).\r\n\r\n## Lens (`--lens`)\r\n\r\nThis skill reviews through one or more **lenses**. Default is `quality`. Lenses are\r\n**combinable** — pass a comma-separated list (e.g. `--lens quality,performance` runs\r\nthe senior quality review and the performance hunt in one pass; `--lens performance,cloud`\r\nruns both focused lenses without the general 7-criteria review).\r\n\r\n| Lens | What it does | Output shape |\r\n|------|--------------|--------------|\r\n| `quality` (default) | The existing 7-criteria senior review + risk summary table + improvement sequence | Per-criterion rating tables → Risk Summary Table → Suggested Improvement Sequence (the Output Structure below) |\r\n| `performance` | Deep performance hunt — N+1 queries, SELECT *, missing ORDER BY, unbuffered reads, missing indexes, unnecessary loops | Prioritized fix list with estimated impact (Performance Analysis structure) |\r\n| `cloud` | Clean-core / ABAP Cloud readiness — unreleased APIs, classic statements, direct SAP standard-table access, statements not allowed in ABAP Cloud | Overall Assessment with modernization guidance (Cloud Readiness Assessment structure) |\r\n\r\n**Boundary — `cloud` lens vs `/abap-cca`:** the `cloud` lens is a **single-object\r\nquick clean-core check**. For an **estate-wide** clean-core / migration classification\r\nacross the whole custom codebase (data-source discovery, full object inventory, usage\r\nanalysis, 20-rule classification, review worksheets), use `/abap-cca` — do not\r\nduplicate the CCA flagship here.\r\n\r\nWhen multiple lenses run, emit each lens's section in order (quality → performance →\r\ncloud), under one shared TL;DR that summarises across all lenses run.\r\n\r\n### Lens `performance` — playbook\r\n\r\n> **MCP dependency:** The performance lens requires MCP to auto-read source (and\r\n> `sap_sql_query` for table-size context) — unless the source is pasted inline.\r\n> The quality default and cloud lens do not require MCP.\r\n\r\nA deep performance hunt: read the source and detect the well-known SAP performance\r\nantipatterns, then produce a prioritized fix list with estimated impact. Read-only.\r\n\r\nOptional focus when invoked: `sql` (database only), `loop` (internal table), `all`\r\n(default). If the developer has SM50/ST05 trace output, fold it in.\r\n\r\n**Step 0 — Read source (when MCP is connected)**\r\n\r\nBefore scanning, call `sap_get_source` to fetch the live object source:\r\n```\r\nsap_get_source(objectType: <objectType>, objectName: <objectName>)\r\n```\r\nIf MCP is not connected and no source was pasted, stop and ask the user to paste the\r\nsource — do not produce findings against empty input.\r\n\r\n**Antipatterns to detect**\r\n\r\n*Critical (fix immediately):*\r\n\r\n1. **SELECT inside LOOP (N+1 query)** — any SELECT inside a LOOP/DO/WHILE block.\r\n ```abap\r\n \" BAD — executes SELECT once per row in lt_orders\r\n LOOP AT lt_orders INTO ls_order.\r\n SELECT SINGLE * FROM mara WHERE matnr = ls_order-matnr.\r\n ENDLOOP.\r\n ```\r\n Fix: extract keys into an internal table, use FOR ALL ENTRIES or JOIN.\r\n Impact: 10,000 orders = 10,000 DB roundtrips → 1 roundtrip.\r\n2. **SELECT \\*** — `SELECT *` / `SELECT SINGLE *` reading all columns when only a few\r\n are needed. Fix: list only the fields used downstream. Impact: less network\r\n transfer, enables index-only scans on HANA.\r\n3. **Missing WHERE on a large table** — SELECT on known large tables (ACDOCA, BSEG,\r\n MSEG, VBAP, EKPO, LIPS, MATDOC) without a restrictive WHERE. Fix: add key-field\r\n filters. Impact: full table scan vs index lookup — minutes vs milliseconds.\r\n4. **FOR ALL ENTRIES on empty table** — FOR ALL ENTRIES without a preceding\r\n `IS NOT INITIAL` guard. Fix: add the guard. Impact: accidental full-table dump —\r\n can crash the system.\r\n\r\n*High (fix before go-live):*\r\n\r\n5. **Nested LOOP without SORTED/HASHED table** — `LOOP AT … WHERE` on a STANDARD\r\n table inside another LOOP (O(n*m)). Fix: declare inner table as SORTED WITH\r\n NON-UNIQUE KEY, or READ TABLE … BINARY SEARCH. Impact: 10,000 × 50,000 = 500M\r\n comparisons → ~170K.\r\n6. **SELECT in IF/CASE branches (hidden N+1)** — SELECT inside IF/CASE inside LOOP.\r\n Fix: batch-read all types before the loop using type-specific key tables.\r\n7. **MODIFY/INSERT/DELETE inside LOOP** — one DB operation per row. Fix: array\r\n operation, `MODIFY ztable FROM TABLE lt_data`. Impact: 1 DB call instead of N.\r\n\r\n*Medium (optimize when possible):*\r\n\r\n8. **CONCATENATE in LOOP** instead of a string template or REDUCE.\r\n9. **Excessive DESCRIBE TABLE / `lines( )`** repeatedly on the same table in a loop —\r\n cache the count.\r\n10. **Missing buffer for customizing reads** — SELECT SINGLE from customizing tables\r\n (T001, T005, TCURR, …) inside processing loops. Fix: read once into a local table,\r\n READ TABLE with BINARY SEARCH.\r\n\r\n**Procedure:** read source → scan for each antipattern (line, pattern, severity,\r\nactual code, why it's a problem here) → check table sizes for context\r\n(`sap_sql_query: SELECT COUNT(*) FROM {table}` when MCP is connected — a SELECT * on a\r\n100-row config table is low impact; on ACDOCA it is critical) → produce the report.\r\n\r\n**Performance Analysis output shape**\r\n\r\n```\r\n## Performance Analysis: {OBJECT_NAME}\r\n\r\n### Summary\r\nLines analyzed: {N}\r\nIssues found: {critical} Critical, {high} High, {medium} Medium\r\n\r\n### Critical Issues\r\n\r\n#### 1. SELECT inside LOOP (Line {N})\r\nSeverity: CRITICAL\r\nTable: {table} (~{row_count} rows)\r\nCurrent code:\r\n {code}\r\nFix:\r\n {proposed fix}\r\nImpact: {estimated improvement}\r\n\r\n### High Issues\r\n...\r\n\r\n### Recommendations\r\n1. Fix critical issues before go-live\r\n2. {specific recommendations}\r\n\r\n### Estimated Impact\r\nCurrent: ~{N} database roundtrips per execution\r\nAfter fixes: ~{M} database roundtrips\r\nImprovement: {percentage}%\r\n```\r\n\r\n**Worked example (performance lens)**\r\n\r\nInput — a method with an N+1 SELECT and a `SELECT *`:\r\n```abap\r\nMETHOD enrich_orders.\r\n LOOP AT lt_orders INTO DATA(ls_order).\r\n SELECT * FROM mara INTO @DATA(ls_mat) WHERE matnr = @ls_order-matnr.\r\n ls_order-desc = ls_mat-meins.\r\n ENDLOOP.\r\nENDMETHOD.\r\n```\r\n\r\nOutput:\r\n```\r\n## Performance Analysis: ENRICH_ORDERS\r\n\r\n### Summary\r\nLines analyzed: 6\r\nIssues found: 1 Critical, 0 High, 1 Medium\r\n\r\n### Critical Issues\r\n\r\n#### 1. SELECT inside LOOP (Line 3)\r\nSeverity: CRITICAL\r\nTable: MARA (~2,400,000 rows)\r\nCurrent code:\r\n SELECT * FROM mara INTO @DATA(ls_mat) WHERE matnr = @ls_order-matnr.\r\nFix:\r\n Collect matnr keys first, then a single set read:\r\n DATA(lt_keys) = VALUE matnr_tab( FOR o IN lt_orders ( o-matnr ) ).\r\n IF lt_keys IS NOT INITIAL.\r\n SELECT matnr, meins FROM mara FOR ALL ENTRIES IN @lt_keys\r\n WHERE matnr = @lt_keys-table_line INTO TABLE @DATA(lt_mat).\r\n ENDIF.\r\nImpact: 10,000 orders = 10,000 roundtrips → 1 roundtrip.\r\n\r\n### Medium Issues\r\n\r\n#### 2. SELECT * on MARA (Line 3)\r\nSeverity: MEDIUM\r\nOnly MEINS is used downstream — reading 200+ columns wastes transfer and blocks\r\nindex-only access on HANA. Fix: SELECT matnr, meins.\r\n\r\n### Estimated Impact\r\nCurrent: ~10,000 database roundtrips per execution\r\nAfter fixes: ~1 database roundtrip\r\nImprovement: ~99%\r\n```\r\n\r\nGuardrails (performance): read-only; don't false-positive on small tables (a SELECT\r\nSINGLE on T001 inside a loop of 5 items is not Critical); if table size can't be\r\ndetermined, report the finding with \"unknown table size — verify manually\"; don't push\r\nFOR ALL ENTRIES where a JOIN is cleaner (it's a fallback, not best practice); HANA vs\r\nnon-HANA matters — on HANA secondary indexes are less relevant, on non-HANA they are\r\ncritical.\r\n\r\n### Lens `cloud` — playbook\r\n\r\nEvaluate the source against SAP Clean Core and the ABAP Cloud programming model.\r\nIdentify every API, function module, table access, and language statement that is\r\nunreleased, deprecated, or restricted in S/4HANA Cloud and BTP ABAP. Read-only — a\r\ngap analysis with a modernization direction, never a code change.\r\n\r\nOptional context: target environment (S/4HANA on-prem release, BTP ABAP, or cloud)\r\nand an ATC check-variant name if available.\r\n\r\n**Procedure**\r\n\r\n1. **Scan the code** for:\r\n - `CALL FUNCTION` → check release-status category\r\n - `NEW` / `CREATE OBJECT` → is the class released?\r\n - `SELECT FROM {table}` → is it an SAP standard table, and does a released CDS\r\n (VDM) view exist as successor?\r\n - Dynamic SQL (dynamic WHERE), AUTHORITY-CHECK patterns\r\n - Statements not allowed in ABAP Cloud: `CALL TRANSACTION`, `SUBMIT`, classic\r\n `MESSAGE` (type I/E/S/W without the messaging framework), `WAIT UP TO`, RFC FM\r\n calls, `WRITE`\r\n2. **Categorize each finding** — API Status (Released C1 / Released C2 partner / Not\r\n Released / Unknown), Impact (Blocker / Warning / Info), Successor (if known with\r\n confidence). Do NOT invent release status — if uncertain, say \"Unable to confirm\r\n release status without system access — verify in SNOTE or the released-API catalog.\"\r\n3. **SAP standard table access** — for each standard table accessed directly: name it,\r\n state whether a released CDS VDM view exists as successor, note that direct access\r\n is a clean-core violation if a VDM exists.\r\n4. **Overall Assessment** — rate Cloud-Ready / Requires Moderate Refactoring /\r\n Significant Blocker.\r\n5. **Modernization direction** — for each blocker, a concrete direction.\r\n\r\n**Cloud Readiness Assessment output shape**\r\n\r\n```\r\n## Cloud Readiness Assessment — {object_name}\r\n\r\n**Target Environment:** {S/4HANA version | BTP ABAP}\r\n**Assessment Date:** {date}\r\n\r\n---\r\n\r\n## Overall Assessment\r\n\r\n**Rating:** {Cloud-Ready | Requires Moderate Refactoring | Significant Blocker}\r\n\r\n{2-3 sentence summary}\r\n\r\n---\r\n\r\n## Classic API Concerns\r\n\r\n| Item | Type | Issue | Impact | Successor |\r\n|------|------|-------|--------|-----------|\r\n| {api_name} | FM / Class / Statement | {issue} | Blocker / Warning | {successor or \"None identified\"} |\r\n\r\n---\r\n\r\n## Released vs Unreleased APIs\r\n\r\n| API | Release Status | Use Allowed | Notes |\r\n|-----|----------------|-------------|-------|\r\n| {api_name} | C1 Released / Not Released / Unknown | Yes / No | {notes} |\r\n\r\n---\r\n\r\n## SAP Standard Table Access\r\n\r\n| Table | Direct Access | Released VDM View | Clean Core Status |\r\n|-------|---------------|-------------------|-------------------|\r\n| {table} | Yes | {vdm_view or N/A} | Violation / Acceptable |\r\n\r\n---\r\n\r\n## Modernization Direction\r\n\r\n### {blocker_1}\r\n**Current:** {what the code does now}\r\n**Target:** {what to use instead}\r\n**Effort:** {Low | Medium | High}\r\n**Reference:** {SAP note, blog, or released API name}\r\n\r\n---\r\n\r\n## Clean Core Guidance\r\n\r\n{3-5 bullet points of specific guidance for this code's migration path}\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n{things that could not be assessed without system access or additional context}\r\n```\r\n\r\n**Worked example (cloud lens)**\r\n\r\nInput — an order-processing method using an unreleased FM, direct VBAK/VBAP access, an\r\nexplicit commit, and a classic `MESSAGE`:\r\n```abap\r\nMETHOD process_customer_orders.\r\n CALL FUNCTION 'SD_SALESDOCUMENT_GETLIST'\r\n EXPORTING sales_org = iv_vkorg\r\n TABLES sales_documents = lt_orders\r\n EXCEPTIONS OTHERS = 1.\r\n IF sy-subrc <> 0.\r\n MESSAGE 'Error reading orders' TYPE 'E'.\r\n ENDIF.\r\n LOOP AT lt_orders INTO ls_order.\r\n SELECT * FROM vbap INTO TABLE lt_items WHERE vbeln = ls_order-vbeln.\r\n CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.\r\n ENDLOOP.\r\nENDMETHOD.\r\n```\r\n\r\nOutput:\r\n```\r\n## Cloud Readiness Assessment — process_customer_orders (method)\r\n\r\n**Target Environment:** BTP ABAP (current: S/4HANA 2023 on-prem)\r\n**Assessment Date:** 2026-06-28\r\n\r\n---\r\n\r\n## Overall Assessment\r\n\r\n**Rating:** Significant Blocker\r\n\r\nUses unreleased function modules, direct access to SAP standard tables (VBAK, VBAP),\r\nand a MESSAGE type E statement that is not allowed in ABAP Cloud classes. All flagged\r\nitems require replacement before cloud deployment.\r\n\r\n---\r\n\r\n## Classic API Concerns\r\n\r\n| Item | Type | Issue | Impact | Successor |\r\n|----------------------------|------|------------------------------------|---------|---------------------------------|\r\n| SD_SALESDOCUMENT_GETLIST | FM | Not released for C1 use | Blocker | CDS View I_SalesOrder (VDM) |\r\n| BAPI_TRANSACTION_COMMIT | FM | Not released in ABAP Cloud | Blocker | Implicit commit by framework |\r\n| MESSAGE ... TYPE 'E' | Stmt | Not allowed in ABAP Cloud classes | Blocker | RAISE EXCEPTION TYPE cx_... |\r\n\r\n---\r\n\r\n## SAP Standard Table Access\r\n\r\n| Table | Direct Access | Released VDM View | Clean Core Status |\r\n|-------|---------------|-------------------|-------------------|\r\n| VBAK | (via FM) | I_SalesOrder | Violation |\r\n| VBAP | SELECT * | I_SalesOrderItem | Violation |\r\n\r\n---\r\n\r\n## Modernization Direction\r\n\r\n### SD_SALESDOCUMENT_GETLIST\r\n**Current:** FM call returning VBAK rows into an internal table\r\n**Target:** SELECT from I_SalesOrder CDS view with ABAP SQL\r\n**Effort:** Low\r\n**Reference:** I_SalesOrder is a released C1 view in the S/4HANA SD VDM\r\n\r\n### BAPI_TRANSACTION_COMMIT\r\n**Current:** Explicit commit per loop iteration\r\n**Target:** Remove — ABAP Cloud commits at the LUW boundary; explicit COMMIT triggers\r\nan ATC error in cloud context\r\n**Effort:** Low\r\n\r\n---\r\n\r\n## Clean Core Guidance\r\n\r\n- Replace all direct SAP standard-table SELECTs with released CDS VDM views\r\n- Remove BAPI_TRANSACTION_COMMIT — not needed in ABAP Cloud\r\n- Replace MESSAGE statements with exception-class raises\r\n- Run SAP_ATC with the \"ABAP Cloud\" check variant for authoritative findings\r\n\r\n---\r\n\r\n## Open Questions\r\n\r\n- What commit strategy is expected? The one-commit-per-order loop needs redesign for\r\n ABAP Cloud.\r\n- Are there authorization requirements on I_SalesOrder in your system?\r\n```\r\n\r\nGuardrails (cloud): never claim an API is released (or unreleased) without\r\nevidence — say \"Unknown — verify in system\"; do not assess release status of Z-objects\r\n(they are custom, not SAP standard); flag direct standard-table access only for known\r\nSAP-delivered tables (KNA1, LFA1, EKKO, VBAK, …), never Z-tables; if the code already\r\nuses released APIs consistently, say so explicitly; Forge Rule 3 — no cloud-readiness\r\nclaim without evidence. Reference `reference/released-api-patterns.md` (in the\r\n`abap-cloud-check` skill) for common released-API patterns.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- ABAP source code (paste directly)\r\n\r\nUseful but optional:\r\n- Context: what does this code do in the business process?\r\n- Target environment (ECC, S/4HANA, BTP) — affects cloud-readiness criteria\r\n- Specific concerns (performance, security, testability)\r\n- Whether this will be reviewed against Clean ABAP guidelines specifically\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Parse and Understand the Code\r\n\r\nRead the full source. Note:\r\n- Object type (class, function module, report, BAdI implementation)\r\n- Approximate complexity (lines, cyclomatic depth)\r\n- Business context if inferable\r\n\r\n### Step 2 — Evaluate Against 7 Quality Criteria\r\n\r\nEvaluate each criterion independently and produce findings:\r\n\r\n**Criterion 1 — Readability and Naming**\r\n- Method names: do they describe what they do (verb + noun)?\r\n- Variable names: are they meaningful or one-letter/generic?\r\n- Inline comments: are they accurate and not redundant?\r\n- Constants: are magic numbers replaced with named constants?\r\n\r\n**Criterion 2 — Clean ABAP Compliance**\r\n- Early returns over deep nesting\r\n- No dead assignments (variables assigned but never used)\r\n- No commented-out code blocks\r\n- Avoid obsolete syntax (MOVE, COMPUTE, WRITE TO)\r\n- Prefer functional style where readable (VALUE, REDUCE, FILTER)\r\n\r\n**Criterion 3 — Testability**\r\n- Dependencies injected via interfaces?\r\n- No hard-coded class instantiation of collaborators?\r\n- Public methods focused enough to be tested independently?\r\n- Class has a FOR TESTING test include or test class?\r\n\r\n**Criterion 4 — Exception Handling**\r\n- Are all CALL FUNCTION EXCEPTIONS handled?\r\n- Are sy-subrc checks performed after every DB operation?\r\n- Are exceptions typed (CX_) rather than text messages in CALL METHOD?\r\n- Are exceptions documented in ABAP Doc?\r\n\r\n**Criterion 5 — Performance**\r\n- SELECT inside a loop (N+1 problem)?\r\n- SELECT * where only a subset is needed?\r\n- Missing PACKAGE SIZE for large reads?\r\n- Table access without client in WHERE clause?\r\n- Unnecessary full-table reads?\r\n\r\n**Criterion 6 — Dead Code**\r\n- Methods defined but never called?\r\n- Variables declared but never used?\r\n- Branches that can never be true?\r\n- Commented-out code older than the current sprint?\r\n\r\n**Criterion 7 — Security**\r\n- SQL injection risk via dynamic WHERE clauses with user input?\r\n- Authorization checks missing (AUTHORITY-CHECK)?\r\n- Sensitive data (passwords, tokens) in log messages?\r\n- RFC-enabled function modules callable without authorization?\r\n\r\n### Step 3 — Produce Findings\r\n\r\nFor each finding: assign severity (Critical / High / Medium / Low / Info),\r\nstate the location (method name or line reference), describe the issue,\r\nand give a concrete recommendation.\r\n\r\n### Step 4 — Build Risk Summary Table\r\n\r\nAggregate findings into a single table sorted by priority.\r\n\r\n### Step 5 — Suggest Improvement Sequence\r\n\r\nRecommend the order in which to address findings, considering:\r\n- Fix Critical and High first (they may mask other issues)\r\n- Structural refactoring before cosmetic cleanup\r\n- Security issues before performance issues\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Code Review — {object_name}\r\n\r\n**Reviewed:** {date}\r\n**Type:** {class | FM | report | BAdI}\r\n**Lines:** {count}\r\n**Reviewer Mode:** Offline analysis (no MCP)\r\n\r\n---\r\n\r\n## Summary\r\n\r\n{2-3 sentence executive summary of overall code quality}\r\n\r\n---\r\n\r\n## Criterion 1 — Readability and Naming\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 2 — Clean ABAP\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 3 — Testability\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 4 — Exception Handling\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 5 — Performance\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 6 — Dead Code\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Criterion 7 — Security\r\n\r\n**Rating:** {Good | Acceptable | Needs Work | Poor}\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|---------|----------------|\r\n| {sev} | {location} | {finding} | {recommendation} |\r\n\r\n---\r\n\r\n## Risk Summary Table\r\n\r\n| Priority | Severity | Issue | Location | Effort |\r\n|----------|----------|--------------------------------|-----------------------|--------|\r\n| 1 | Critical | {issue} | {location} | Low |\r\n| 2 | High | {issue} | {location} | Medium |\r\n| 3 | Medium | {issue} | {location} | Low |\r\n\r\n---\r\n\r\n## Suggested Improvement Sequence\r\n\r\n1. **Immediately:** {critical_items} — these block safe production use\r\n2. **Before next release:** {high_items}\r\n3. **Technical debt sprint:** {medium_items}\r\n4. **Good-to-have:** {low_items}\r\n```\r\n\r\n## Guardrails\r\n\r\n- Distinguish what is clearly a defect from what is a style preference\r\n- Label subjective style preferences as \"Info\" not \"High\"\r\n- Never claim a security vulnerability without explaining the specific attack vector\r\n- If the code is partial (missing context), say so and note what could not be assessed\r\n- Do not invent API names when suggesting replacements — say \"use a released API\"\r\n if you cannot name one with confidence\r\n- Never state that code is \"production ready\" — only note what was reviewed\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-review\r\n\r\nPlease review this PO approval class. S/4HANA 2022 on-prem. Business context:\r\ncalled from a workflow task when a PO needs approval.\r\n\r\nCLASS zcl_po_approval DEFINITION PUBLIC FINAL CREATE PUBLIC.\r\n PUBLIC SECTION.\r\n METHODS: approve IMPORTING p1 TYPE vbeln,\r\n reject IMPORTING p1 TYPE vbeln p2 TYPE string.\r\n PRIVATE SECTION.\r\n METHODS: do_stuff IMPORTING p1 TYPE vbeln RETURNING VALUE(r) TYPE i.\r\nENDCLASS.\r\n\r\nCLASS zcl_po_approval IMPLEMENTATION.\r\n METHOD approve.\r\n DATA: x TYPE i.\r\n DATA: tab TYPE TABLE OF ekko.\r\n SELECT * FROM ekko INTO TABLE tab WHERE ebeln = p1.\r\n IF sy-subrc = 0.\r\n LOOP AT tab INTO DATA(row).\r\n UPDATE ekko SET loekz = 'X' WHERE ebeln = row-ebeln.\r\n x = x + 1.\r\n ENDLOOP.\r\n CALL FUNCTION 'SO_DOCUMENT_SEND_API1'\r\n EXPORTING document_data = VALUE sofolenti1( ).\r\n ENDIF.\r\n \"TODO: add auth check\r\n ENDMETHOD.\r\n\r\n METHOD reject.\r\n \"TODO\r\n ENDMETHOD.\r\n\r\n METHOD do_stuff.\r\n r = 0.\r\n ENDMETHOD.\r\nENDCLASS.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Code Review — ZCL_PO_APPROVAL\r\n\r\n**Reviewed:** 2026-03-29\r\n**Type:** Global Class\r\n**Lines:** ~35 (provided excerpt)\r\n**Reviewer Mode:** Offline analysis\r\n\r\n---\r\n\r\n## Summary\r\n\r\nThis class has critical security, correctness, and maintainability issues.\r\nThe approve method modifies production data (UPDATE ekko) without an\r\nauthorization check, uses non-descriptive parameter and method names, sends\r\nan email with an empty structure, and performs N+1 UPDATE statements instead\r\nof a single UPDATE. The reject method is a stub. The class must not go to\r\nproduction in this state.\r\n\r\n---\r\n\r\n## Criterion 1 — Readability and Naming\r\n\r\n**Rating:** Poor\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|--------------------------------------------|---------------------------------------------|\r\n| High | approve | Parameter named p1, p2 — no semantics | Rename to iv_order_id, iv_reason |\r\n| Medium | approve | Variable x has no meaningful name | Rename to lv_updated_count |\r\n| High | Class | Method do_stuff has no meaningful name | Rename to describe what it actually does |\r\n\r\n---\r\n\r\n## Criterion 5 — Performance\r\n\r\n**Rating:** Needs Work\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|--------------------------------------------------|-----------------------------------------------|\r\n| High | approve | UPDATE ekko inside LOOP AT tab — N+1 SQL writes | Use UPDATE ekko FROM TABLE @tab or single SQL |\r\n| Medium | approve | SELECT * FROM ekko — loads all fields | Select only fields that are used |\r\n\r\n---\r\n\r\n## Criterion 7 — Security\r\n\r\n**Rating:** Poor\r\n\r\n| Severity | Location | Finding | Recommendation |\r\n|----------|----------|------------------------------------------------------|-------------------------------------------------|\r\n| Critical | approve | No AUTHORITY-CHECK before modifying PO (UPDATE ekko) | Add AUTHORITY-CHECK OBJECT 'M_BEST_BSA' before |\r\n| | | | any write; raise exception if not authorized |\r\n| High | approve | Direct UPDATE to ekko (SAP standard table) without | Use ME_CHANGE_PURCHASE_ORDER BAdI or BAPI |\r\n| | | using an API — bypasses SAP locking and workflow | instead of direct table UPDATE |\r\n\r\n---\r\n\r\n## Risk Summary Table\r\n\r\n| Priority | Severity | Issue | Location | Effort |\r\n|----------|----------|---------------------------------------------|----------|--------|\r\n| 1 | Critical | No authorization check before PO update | approve | Low |\r\n| 2 | Critical | Direct UPDATE to SAP standard table ekko | approve | High |\r\n| 3 | High | N+1 UPDATE in loop | approve | Medium |\r\n| 4 | High | reject() is a stub — workflow will silently fail | reject | Medium |\r\n| 5 | High | All parameters named p1/p2 | all | Low |\r\n| 6 | Medium | do_stuff() does nothing — dead code | do_stuff | Low |\r\n\r\n---\r\n\r\n## Suggested Improvement Sequence\r\n\r\n1. **Immediately:** Add AUTHORITY-CHECK to approve(); this is a security blocker.\r\n2. **Immediately:** Replace direct UPDATE ekko with BAPI_PO_CHANGE or appropriate\r\n released API — direct standard table updates bypass locking and auditing.\r\n3. **Before next release:** Implement reject() — it is called from workflow and\r\n currently does nothing.\r\n4. **Technical debt sprint:** Rename all parameters and methods; remove do_stuff().\r\n5. **Good-to-have:** Add unit tests; extract DB access to a repository interface.\r\n```\r\n",
198
205
  "sha256": "04229f8aeb924b2fbfb54c82446df436ebfcd015d5ae0b5fca874103a0097f3f",
199
206
  "signature": "",
200
- "signedAt": "2026-09-17T20:19:05.588Z"
207
+ "signedAt": "2026-09-28T14:37:05.003Z"
201
208
  },
202
209
  "abap-segw": {
203
210
  "name": "abap-segw",
204
- "body": "---\nname: abap-segw\ndescription: >\n Implement SEGW OData service methods. Reads the generated MPC/DPC base\n classes to understand the entity model, then generates or modifies the\n DPC_EXT method implementations for CRUD operations, function imports,\n deep entities, and navigation. The SEGW project itself is created in\n transaction SEGW — this skill handles the ABAP implementation work.\nphase: BUILD\nrequires_mcp: required\nforge_rules: [7, 10]\nversion: \"1.1\"\n---\n\n# abap-segw\n\n## Purpose\n\nHandle the ABAP implementation side of SEGW OData services. SEGW projects\ndefine the data model and generate skeleton classes. The real development\nwork is implementing the DPC_EXT methods — reading data, creating entities,\nhandling filters, paging, error messages, and function imports. That is\nwhat this skill does.\n\nUse cases:\n- **New service implementation** — SEGW project exists, DPC_EXT is empty\n- **Add CRUD operations** — implement CREATE/UPDATE/DELETE for an entity\n- **Function imports** — implement custom actions (approve, release, etc.)\n- **Deep entity** — create parent + children in one call\n- **Fix/enhance existing service** — add filters, fix paging, handle errors\n- **Troubleshoot OData errors** — read the DPC_EXT, find the bug, fix it\n\n## When to Use\n\n- Use to implement or fix DPC_EXT method bodies on an EXISTING SEGW project (ECC / older S/4 OData).\n- NOT for greenfield services on S/4 1909+ — use `/abap-rap`; NOT to create the SEGW project itself (transaction SEGW); NOT for the frontend — use `/abap-fiori-build`.\n\n## What This Skill Does NOT Do\n\n- Create SEGW projects (use transaction SEGW for that)\n- Define entity types or associations (use SEGW model editor)\n- Generate runtime artifacts (use SEGW \"Generate\" button)\n- Register services (use /IWFND/MAINT_SERVICE)\n\nThese steps remain in SEGW or SAP GUI. Once the skeleton is generated,\nCSPeach takes over the implementation.\n\n## Required Tools\n\n| Tool | Purpose |\n|------|---------|\n| `sap_get_source` | Read DPC, DPC_EXT, MPC, MPC_EXT classes |\n| `sap_search_object` | Find the generated classes for a service |\n| `sap_update_method` | **Primary write tool** — replace ONE DPC_EXT method body (Rule 7a) |\n| `sap_set_source` | Fallback only — when adding a brand-new method that requires growing the class definition |\n| `sap_syntax_check` | Verify before writing |\n| `sap_snapshot` | Backup before writing (Forge Rule 7) |\n| `sap_activate` | Activate after writing |\n| `sap_inactive_objects` | Verify activation (Forge Rule 10) |\n| `sap_usage_references` | Find callers/dependencies |\n| `sap_odata_test` | Test the OData service endpoint |\n\n## SEGW Class Architecture\n\nEvery SEGW project generates four classes:\n\n| Class | Suffix | Role | Modify? |\n|-------|--------|------|---------|\n| Model Provider Class | `_MPC` | Generated entity definitions, types | NEVER |\n| Model Provider Extension | `_MPC_EXT` | Custom model extensions | Rarely |\n| Data Provider Class | `_DPC` | Generated method stubs for CRUD | NEVER |\n| Data Provider Extension | `_DPC_EXT` | **Your implementations go here** | YES |\n\n**Golden rule: NEVER modify _MPC or _DPC.** SEGW overwrites them on\nregeneration. All custom code goes in _MPC_EXT or _DPC_EXT.\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Required Behavior\n\n### Step 1 — Identify the Service Classes\n\nAsk for the service name or SEGW project name. Find the classes:\n\n```\nsap_search_object (query: \"*{service}*DPC_EXT*\", objType: \"CLAS/OC\")\nsap_search_object (query: \"*{service}*MPC*\", objType: \"CLAS/OC\")\n```\n\nIdentify the four classes: _MPC, _MPC_EXT, _DPC, _DPC_EXT.\n\n### Step 2 — Read the Generated Model\n\n```\nsap_get_source (objectType: \"CLAS\", objectName: \"{service}_MPC\")\n```\n\nFrom the MPC class extract:\n- Entity type names and their ABAP structure types (TS_ types)\n- Entity set names\n- Association names and cardinalities\n- Navigation properties\n- Key fields per entity\n\nThis is the contract — the DPC_EXT must work with these exact types.\n\n### Step 3 — Read the Current DPC_EXT\n\n```\nsap_get_source (objectType: \"CLAS\", objectName: \"{service}_DPC_EXT\")\n```\n\nCheck what is already implemented. Identify empty method stubs vs\ncompleted implementations. Report:\n\n```\nDPC_EXT Status for {SERVICE}:\n GET_ENTITYSET: Implemented (CustomerSet, OrderSet)\n GET_ENTITY: Empty stub (CustomerSet)\n CREATE_ENTITY: Not redefined\n UPDATE_ENTITY: Not redefined\n DELETE_ENTITY: Not redefined\n EXECUTE_ACTION: Implemented (Approve, Release)\n CREATE_DEEP_ENTITY: Not redefined\n```\n\n### Step 4 — Implement the Requested Method\n\nFollow these patterns strictly. They are the correct SEGW patterns verified\non live SAP systems.\n\n#### GET_ENTITYSET (Read List)\n\n```abap\nMETHOD {entityset}_get_entityset.\n \" 1. Get filters from request context\n DATA(lt_filters) = io_tech_request_context->get_filter(\n )->get_filter_select_options( ).\n\n \" 2. Extract filter values\n DATA: lr_{field} TYPE RANGE OF {type}.\n LOOP AT lt_filters INTO DATA(ls_filter)\n WHERE property = '{PROPERTY_NAME}'.\n APPEND LINES OF ls_filter-select_options TO lr_{field}.\n ENDLOOP.\n\n \" 3. Read data\n SELECT {fields}\n FROM {table}\n INTO TABLE @et_entityset\n WHERE {field} IN @lr_{field}.\n\n \" 4. Handle paging (if is_paging is supplied)\n IF is_paging IS NOT INITIAL.\n /iwbep/cl_mgw_data_util=>paging(\n EXPORTING is_paging = is_paging\n CHANGING ct_data = et_entityset ).\n ENDIF.\n\n \" 5. Handle ordering\n IF it_order IS NOT INITIAL.\n /iwbep/cl_mgw_data_util=>orderby(\n EXPORTING it_order = it_order\n CHANGING ct_data = et_entityset ).\n ENDIF.\nENDMETHOD.\n```\n\n#### GET_ENTITY (Read Single)\n\n```abap\nMETHOD {entityset}_get_entity.\n \" 1. Get key fields\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\n\n \" 2. Extract key values\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\n\n \" 3. Read single record\n SELECT SINGLE {fields}\n FROM {table}\n INTO @er_entity\n WHERE {key_field} = @lv_key.\n\n IF sy-subrc <> 0.\n RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\n EXPORTING textid = /iwbep/cx_mgw_busi_exception=>resource_not_found.\n ENDIF.\nENDMETHOD.\n```\n\n#### CREATE_ENTITY\n\n```abap\nMETHOD {entityset}_create_entity.\n \" 1. Read input data\n DATA: ls_data TYPE {mpc_structure_type}.\n io_data_provider->read_entry_data( IMPORTING es_data = ls_data ).\n\n \" 2. Validate\n IF ls_data-{mandatory_field} IS INITIAL.\n RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\n EXPORTING textid = /iwbep/cx_mgw_busi_exception=>resource_not_found.\n ENDIF.\n\n \" 3. Create in database\n MODIFY {table} FROM @ls_data.\n IF sy-subrc = 0.\n COMMIT WORK AND WAIT.\n ENDIF.\n\n \" 4. Return created entity\n er_entity = ls_data.\nENDMETHOD.\n```\n\n#### UPDATE_ENTITY\n\n```abap\nMETHOD {entityset}_update_entity.\n DATA: ls_data TYPE {mpc_structure_type}.\n io_data_provider->read_entry_data( IMPORTING es_data = ls_data ).\n\n \" Get keys to identify the record\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\n\n \" Update\n MODIFY {table} FROM @ls_data.\n IF sy-subrc = 0.\n COMMIT WORK AND WAIT.\n ENDIF.\n\n er_entity = ls_data.\nENDMETHOD.\n```\n\n#### DELETE_ENTITY\n\n```abap\nMETHOD {entityset}_delete_entity.\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\n\n DELETE FROM {table} WHERE {key_field} = @lv_key.\n IF sy-subrc = 0.\n COMMIT WORK AND WAIT.\n ENDIF.\nENDMETHOD.\n```\n\n#### EXECUTE_ACTION (Function Import)\n\n```abap\nMETHOD /iwbep/if_mgw_appl_srv_runtime~execute_action.\n DATA(lt_params) = io_tech_request_context->get_parameters( ).\n\n CASE iv_action_name.\n WHEN '{ACTION_NAME}'.\n \" Extract parameters\n DATA(lv_param) = lt_params[ name = '{PARAM}' ]-value.\n\n \" Execute business logic\n \" ...\n\n \" Return result\n DATA: ls_result TYPE {result_type}.\n ls_result-{field} = lv_value.\n copy_data_to_ref( EXPORTING is_data = ls_result\n CHANGING cr_data = er_data ).\n ENDCASE.\nENDMETHOD.\n```\n\n#### Error Handling Pattern\n\n```abap\n\" Get message container\nDATA(lo_msg) = mo_context->get_message_container( ).\n\n\" Add business error\nlo_msg->add_message(\n iv_msg_type = 'E'\n iv_msg_id = '{MSG_CLASS}'\n iv_msg_number = '{MSG_NO}'\n iv_msg_text = '{error text}'\n iv_add_to_response_header = abap_true ).\n\n\" Raise OData exception\nRAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\n EXPORTING message_container = lo_msg\n http_status_code = 400.\n```\n\n### Step 5 — Write with Safety (Rule 7a)\n\n**Forge Rule 7a applies to every DPC_EXT change.** Use `sap_update_method`\nper method — not `sap_set_source` for the full class. DPC_EXT carries every\nimplemented OData method; rewriting it whole risks dropping methods the AI\ndidn't recognise as worth preserving. Two paths:\n\n#### Path A — Modifying an existing method body (the common case)\n\nThis covers: filling an empty stub, fixing logic, adding paging/filters,\nchanging error handling. The class definition does NOT change.\n\n1. **Snapshot** — `sap_snapshot` (operation: take) on the DPC_EXT class.\n Snapshot failure = hard stop (Rule 7).\n2. **Diff preview** — show the developer the new method body and confirm\n \"Replace `{method_name}` with this body? [y/n]\"\n3. **Syntax check** — `sap_syntax_check` on the proposed source.\n4. **Write** — `sap_update_method` (className, methodName, body). This\n replaces ONLY the named method body; every other method, the class\n definition, the inherited interfaces, and the local definitions stay\n exactly as they were. There is no merge step. There is no full-class\n rebuild.\n5. **Activate** — `sap_activate` explicitly (do not trust auto-activation).\n6. **Verify** — `sap_inactive_objects` to confirm the class is NOT on the\n inactive list (Rule 10).\n7. **Rollback on failure** — if any step fails, `sap_snapshot` (operation:\n restore). Report what failed. Stop.\n\nRepeat steps 2–7 for each method being changed. One `sap_update_method`\ncall per method. Do NOT batch multiple method bodies into a single\n`sap_set_source`.\n\n#### Path B — Adding a brand-new method (definition has to grow)\n\nThis covers: implementing a CRUD operation that wasn't there before,\nadding a new EXECUTE_ACTION handler, adding a CREATE_DEEP_ENTITY override.\nThe class definition has to grow to declare the new method redefinition.\n\n1. **Snapshot** — `sap_snapshot` (operation: take). Hard stop on failure.\n2. **Read full current source** — `sap_get_source` on the DPC_EXT class.\n3. **Plan the diff** — show the developer two parts: the definition\n change (new METHODS / REDEFINITION line) and the new method body.\n Confirm \"Add method `{name}` to the class definition AND its\n implementation? [y/n]\"\n4. **Syntax check** — `sap_syntax_check` on the proposed full source.\n5. **Write** — `sap_set_source` with the complete class source. This is\n the ONLY case where `sap_set_source` is correct for DPC_EXT. The\n reason: `sap_update_method` cannot grow the definition; only a full\n class write can.\n6. **Activate** — `sap_activate` explicitly.\n7. **Verify** — `sap_inactive_objects`.\n8. **Rollback on failure** — `sap_snapshot` (operation: restore).\n\nIf the same change needs Path A AND Path B (e.g. add a new method AND\nmodify an existing one), do Path B first (one `sap_set_source` to grow\nthe definition + add the new body), then Path A for the existing method\nmodifications. Never collapse them into a single `sap_set_source`.\n\n#### Why Path A is mandatory for body-only changes\n\n`sap_set_source` replaces the entire class source. To use it for a\nbody-only change, the AI would have to reconstruct every other method\nfrom `sap_get_source` output, preserving every line, every comment, every\nlocal class. That reconstruction is exactly what Rule 7a forbids: it\nrisks silently corrupting the class definition, dropping parameters,\naltering untouched methods, or losing local class definitions / macros /\nincludes. `sap_update_method` does the surgical replacement the\nSAP backend offers natively. Use it.\n\n### Step 6 — Test (Optional)\n\nIf the service is registered, test via:\n```\nsap_odata_test (service: \"{service}\", entity: \"{EntitySet}\")\n```\n\n## Guardrails\n\n### Method-Only Writes\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method logic — use `sap_update_method` per method instead (Rule 7a). This is especially important for DPC_EXT classes where you are adding method implementations.\n\n### Never Touch Generated Classes\n- NEVER modify _MPC or _DPC. They are regenerated by SEGW and your changes\n will be lost. All custom code goes in _MPC_EXT or _DPC_EXT.\n- If you see `\"DO NOT MODIFY\"` or `\"GENERATED\"` in a class header, stop.\n\n### Use the Correct Types\n- Read the MPC class to find the exact structure types (TS_, TT_ prefixed).\n Use those types in DPC_EXT — not custom structures. The OData framework\n expects the generated types.\n\n### Handle Filters Properly\n- Always extract filters from `io_tech_request_context->get_filter()`.\n Never ignore filters — returning unfiltered data is a security risk\n and performance disaster.\n- Always handle `is_paging`. Without it, large entity sets return\n everything and crash the browser.\n\n### Handle Errors Correctly\n- Use `/iwbep/cx_mgw_busi_exception` for business errors (400).\n- Use `/iwbep/cx_mgw_tech_exception` for technical errors (500).\n- Always use the message container for structured error messages.\n- Never use bare `MESSAGE ... TYPE 'E'` — it terminates the OData call\n with an unstructured dump instead of a proper error response.\n\n### Key Method Interface\n\nAll DPC_EXT entity methods share this standard interface:\n\n| Parameter | Direction | Purpose |\n|-----------|-----------|---------|\n| `iv_entity_name` | Importing | Entity type name |\n| `iv_entity_set_name` | Importing | Entity set name |\n| `it_key_tab` | Importing | Key-value pairs for the entity |\n| `it_filter_select_options` | Importing | OData $filter as ranges |\n| `is_paging` | Importing | $top and $skip |\n| `it_order` | Importing | $orderby |\n| `it_navigation_path` | Importing | Navigation segments |\n| `io_tech_request_context` | Importing | Full request context |\n| `io_data_provider` | Importing | Request payload (for create/update) |\n| `er_entity` | Exporting | Single entity result |\n| `et_entityset` | Exporting | Entity set result |\n\n### COMMIT WORK\n- Always use `COMMIT WORK AND WAIT` after database changes in OData\n context. Without `AND WAIT`, the response may return before the commit\n completes, causing inconsistencies.\n- Never commit in GET methods. Read operations must not change data.\n",
205
- "sha256": "ef5278d3b281304707600b2c83adceb9308388aa7a033e47bb1b675c2060dbda",
211
+ "body": "---\r\nname: abap-segw\r\ndescription: >\r\n Implement SEGW OData service methods. Reads the generated MPC/DPC base\r\n classes to understand the entity model, then generates or modifies the\r\n DPC_EXT method implementations for CRUD operations, function imports,\r\n deep entities, and navigation. The SEGW project itself is created in\r\n transaction SEGW — this skill handles the ABAP implementation work.\r\nphase: BUILD\r\nrequires_mcp: required\r\nforge_rules: [7, 10]\r\nversion: \"1.1\"\r\n---\r\n\r\n# abap-segw\r\n\r\n## Purpose\r\n\r\nHandle the ABAP implementation side of SEGW OData services. SEGW projects\r\ndefine the data model and generate skeleton classes. The real development\r\nwork is implementing the DPC_EXT methods — reading data, creating entities,\r\nhandling filters, paging, error messages, and function imports. That is\r\nwhat this skill does.\r\n\r\nUse cases:\r\n- **New service implementation** — SEGW project exists, DPC_EXT is empty\r\n- **Add CRUD operations** — implement CREATE/UPDATE/DELETE for an entity\r\n- **Function imports** — implement custom actions (approve, release, etc.)\r\n- **Deep entity** — create parent + children in one call\r\n- **Fix/enhance existing service** — add filters, fix paging, handle errors\r\n- **Troubleshoot OData errors** — read the DPC_EXT, find the bug, fix it\r\n\r\n## When to Use\r\n\r\n- Use to implement or fix DPC_EXT method bodies on an EXISTING SEGW project (ECC / older S/4 OData).\r\n- NOT for greenfield services on S/4 1909+ — use `/abap-rap`; NOT to create the SEGW project itself (transaction SEGW); NOT for the frontend — use `/abap-fiori-build`.\r\n\r\n## What This Skill Does NOT Do\r\n\r\n- Create SEGW projects (use transaction SEGW for that)\r\n- Define entity types or associations (use SEGW model editor)\r\n- Generate runtime artifacts (use SEGW \"Generate\" button)\r\n- Register services (use /IWFND/MAINT_SERVICE)\r\n\r\nThese steps remain in SEGW or SAP GUI. Once the skeleton is generated,\r\nCSPeach takes over the implementation.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_get_source` | Read DPC, DPC_EXT, MPC, MPC_EXT classes |\r\n| `sap_search_object` | Find the generated classes for a service |\r\n| `sap_update_method` | **Primary write tool** — replace ONE DPC_EXT method body (Rule 7a) |\r\n| `sap_set_source` | Fallback only — when adding a brand-new method that requires growing the class definition |\r\n| `sap_syntax_check` | Verify before writing |\r\n| `sap_snapshot` | Backup before writing (Forge Rule 7) |\r\n| `sap_activate` | Activate after writing |\r\n| `sap_inactive_objects` | Verify activation (Forge Rule 10) |\r\n| `sap_usage_references` | Find callers/dependencies |\r\n| `sap_odata_test` | Test the OData service endpoint |\r\n\r\n## SEGW Class Architecture\r\n\r\nEvery SEGW project generates four classes:\r\n\r\n| Class | Suffix | Role | Modify? |\r\n|-------|--------|------|---------|\r\n| Model Provider Class | `_MPC` | Generated entity definitions, types | NEVER |\r\n| Model Provider Extension | `_MPC_EXT` | Custom model extensions | Rarely |\r\n| Data Provider Class | `_DPC` | Generated method stubs for CRUD | NEVER |\r\n| Data Provider Extension | `_DPC_EXT` | **Your implementations go here** | YES |\r\n\r\n**Golden rule: NEVER modify _MPC or _DPC.** SEGW overwrites them on\r\nregeneration. All custom code goes in _MPC_EXT or _DPC_EXT.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Identify the Service Classes\r\n\r\nAsk for the service name or SEGW project name. Find the classes:\r\n\r\n```\r\nsap_search_object (query: \"*{service}*DPC_EXT*\", objType: \"CLAS/OC\")\r\nsap_search_object (query: \"*{service}*MPC*\", objType: \"CLAS/OC\")\r\n```\r\n\r\nIdentify the four classes: _MPC, _MPC_EXT, _DPC, _DPC_EXT.\r\n\r\n### Step 2 — Read the Generated Model\r\n\r\n```\r\nsap_get_source (objectType: \"CLAS\", objectName: \"{service}_MPC\")\r\n```\r\n\r\nFrom the MPC class extract:\r\n- Entity type names and their ABAP structure types (TS_ types)\r\n- Entity set names\r\n- Association names and cardinalities\r\n- Navigation properties\r\n- Key fields per entity\r\n\r\nThis is the contract — the DPC_EXT must work with these exact types.\r\n\r\n### Step 3 — Read the Current DPC_EXT\r\n\r\n```\r\nsap_get_source (objectType: \"CLAS\", objectName: \"{service}_DPC_EXT\")\r\n```\r\n\r\nCheck what is already implemented. Identify empty method stubs vs\r\ncompleted implementations. Report:\r\n\r\n```\r\nDPC_EXT Status for {SERVICE}:\r\n GET_ENTITYSET: Implemented (CustomerSet, OrderSet)\r\n GET_ENTITY: Empty stub (CustomerSet)\r\n CREATE_ENTITY: Not redefined\r\n UPDATE_ENTITY: Not redefined\r\n DELETE_ENTITY: Not redefined\r\n EXECUTE_ACTION: Implemented (Approve, Release)\r\n CREATE_DEEP_ENTITY: Not redefined\r\n```\r\n\r\n### Step 4 — Implement the Requested Method\r\n\r\nFollow these patterns strictly. They are the correct SEGW patterns verified\r\non live SAP systems.\r\n\r\n#### GET_ENTITYSET (Read List)\r\n\r\n```abap\r\nMETHOD {entityset}_get_entityset.\r\n \" 1. Get filters from request context\r\n DATA(lt_filters) = io_tech_request_context->get_filter(\r\n )->get_filter_select_options( ).\r\n\r\n \" 2. Extract filter values\r\n DATA: lr_{field} TYPE RANGE OF {type}.\r\n LOOP AT lt_filters INTO DATA(ls_filter)\r\n WHERE property = '{PROPERTY_NAME}'.\r\n APPEND LINES OF ls_filter-select_options TO lr_{field}.\r\n ENDLOOP.\r\n\r\n \" 3. Read data\r\n SELECT {fields}\r\n FROM {table}\r\n INTO TABLE @et_entityset\r\n WHERE {field} IN @lr_{field}.\r\n\r\n \" 4. Handle paging (if is_paging is supplied)\r\n IF is_paging IS NOT INITIAL.\r\n /iwbep/cl_mgw_data_util=>paging(\r\n EXPORTING is_paging = is_paging\r\n CHANGING ct_data = et_entityset ).\r\n ENDIF.\r\n\r\n \" 5. Handle ordering\r\n IF it_order IS NOT INITIAL.\r\n /iwbep/cl_mgw_data_util=>orderby(\r\n EXPORTING it_order = it_order\r\n CHANGING ct_data = et_entityset ).\r\n ENDIF.\r\nENDMETHOD.\r\n```\r\n\r\n#### GET_ENTITY (Read Single)\r\n\r\n```abap\r\nMETHOD {entityset}_get_entity.\r\n \" 1. Get key fields\r\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\r\n\r\n \" 2. Extract key values\r\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\r\n\r\n \" 3. Read single record\r\n SELECT SINGLE {fields}\r\n FROM {table}\r\n INTO @er_entity\r\n WHERE {key_field} = @lv_key.\r\n\r\n IF sy-subrc <> 0.\r\n RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\r\n EXPORTING textid = /iwbep/cx_mgw_busi_exception=>resource_not_found.\r\n ENDIF.\r\nENDMETHOD.\r\n```\r\n\r\n#### CREATE_ENTITY\r\n\r\n```abap\r\nMETHOD {entityset}_create_entity.\r\n \" 1. Read input data\r\n DATA: ls_data TYPE {mpc_structure_type}.\r\n io_data_provider->read_entry_data( IMPORTING es_data = ls_data ).\r\n\r\n \" 2. Validate\r\n IF ls_data-{mandatory_field} IS INITIAL.\r\n RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\r\n EXPORTING textid = /iwbep/cx_mgw_busi_exception=>resource_not_found.\r\n ENDIF.\r\n\r\n \" 3. Create in database\r\n MODIFY {table} FROM @ls_data.\r\n IF sy-subrc = 0.\r\n COMMIT WORK AND WAIT.\r\n ENDIF.\r\n\r\n \" 4. Return created entity\r\n er_entity = ls_data.\r\nENDMETHOD.\r\n```\r\n\r\n#### UPDATE_ENTITY\r\n\r\n```abap\r\nMETHOD {entityset}_update_entity.\r\n DATA: ls_data TYPE {mpc_structure_type}.\r\n io_data_provider->read_entry_data( IMPORTING es_data = ls_data ).\r\n\r\n \" Get keys to identify the record\r\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\r\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\r\n\r\n \" Update\r\n MODIFY {table} FROM @ls_data.\r\n IF sy-subrc = 0.\r\n COMMIT WORK AND WAIT.\r\n ENDIF.\r\n\r\n er_entity = ls_data.\r\nENDMETHOD.\r\n```\r\n\r\n#### DELETE_ENTITY\r\n\r\n```abap\r\nMETHOD {entityset}_delete_entity.\r\n DATA(lt_keys) = io_tech_request_context->get_keys( ).\r\n DATA(lv_key) = lt_keys[ name = '{KEY_PROPERTY}' ]-value.\r\n\r\n DELETE FROM {table} WHERE {key_field} = @lv_key.\r\n IF sy-subrc = 0.\r\n COMMIT WORK AND WAIT.\r\n ENDIF.\r\nENDMETHOD.\r\n```\r\n\r\n#### EXECUTE_ACTION (Function Import)\r\n\r\n```abap\r\nMETHOD /iwbep/if_mgw_appl_srv_runtime~execute_action.\r\n DATA(lt_params) = io_tech_request_context->get_parameters( ).\r\n\r\n CASE iv_action_name.\r\n WHEN '{ACTION_NAME}'.\r\n \" Extract parameters\r\n DATA(lv_param) = lt_params[ name = '{PARAM}' ]-value.\r\n\r\n \" Execute business logic\r\n \" ...\r\n\r\n \" Return result\r\n DATA: ls_result TYPE {result_type}.\r\n ls_result-{field} = lv_value.\r\n copy_data_to_ref( EXPORTING is_data = ls_result\r\n CHANGING cr_data = er_data ).\r\n ENDCASE.\r\nENDMETHOD.\r\n```\r\n\r\n#### Error Handling Pattern\r\n\r\n```abap\r\n\" Get message container\r\nDATA(lo_msg) = mo_context->get_message_container( ).\r\n\r\n\" Add business error\r\nlo_msg->add_message(\r\n iv_msg_type = 'E'\r\n iv_msg_id = '{MSG_CLASS}'\r\n iv_msg_number = '{MSG_NO}'\r\n iv_msg_text = '{error text}'\r\n iv_add_to_response_header = abap_true ).\r\n\r\n\" Raise OData exception\r\nRAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception\r\n EXPORTING message_container = lo_msg\r\n http_status_code = 400.\r\n```\r\n\r\n### Step 5 — Write with Safety (Rule 7a)\r\n\r\n**Forge Rule 7a applies to every DPC_EXT change.** Use `sap_update_method`\r\nper method — not `sap_set_source` for the full class. DPC_EXT carries every\r\nimplemented OData method; rewriting it whole risks dropping methods the AI\r\ndidn't recognise as worth preserving. Two paths:\r\n\r\n#### Path A — Modifying an existing method body (the common case)\r\n\r\nThis covers: filling an empty stub, fixing logic, adding paging/filters,\r\nchanging error handling. The class definition does NOT change.\r\n\r\n1. **Snapshot** — `sap_snapshot` (operation: take) on the DPC_EXT class.\r\n Snapshot failure = hard stop (Rule 7).\r\n2. **Diff preview** — show the developer the new method body and confirm\r\n \"Replace `{method_name}` with this body? [y/n]\"\r\n3. **Syntax check** — `sap_syntax_check` on the proposed source.\r\n4. **Write** — `sap_update_method` (className, methodName, body). This\r\n replaces ONLY the named method body; every other method, the class\r\n definition, the inherited interfaces, and the local definitions stay\r\n exactly as they were. There is no merge step. There is no full-class\r\n rebuild.\r\n5. **Activate** — `sap_activate` explicitly (do not trust auto-activation).\r\n6. **Verify** — `sap_inactive_objects` to confirm the class is NOT on the\r\n inactive list (Rule 10).\r\n7. **Rollback on failure** — if any step fails, `sap_snapshot` (operation:\r\n restore). Report what failed. Stop.\r\n\r\nRepeat steps 2–7 for each method being changed. One `sap_update_method`\r\ncall per method. Do NOT batch multiple method bodies into a single\r\n`sap_set_source`.\r\n\r\n#### Path B — Adding a brand-new method (definition has to grow)\r\n\r\nThis covers: implementing a CRUD operation that wasn't there before,\r\nadding a new EXECUTE_ACTION handler, adding a CREATE_DEEP_ENTITY override.\r\nThe class definition has to grow to declare the new method redefinition.\r\n\r\n1. **Snapshot** — `sap_snapshot` (operation: take). Hard stop on failure.\r\n2. **Read full current source** — `sap_get_source` on the DPC_EXT class.\r\n3. **Plan the diff** — show the developer two parts: the definition\r\n change (new METHODS / REDEFINITION line) and the new method body.\r\n Confirm \"Add method `{name}` to the class definition AND its\r\n implementation? [y/n]\"\r\n4. **Syntax check** — `sap_syntax_check` on the proposed full source.\r\n5. **Write** — `sap_set_source` with the complete class source. This is\r\n the ONLY case where `sap_set_source` is correct for DPC_EXT. The\r\n reason: `sap_update_method` cannot grow the definition; only a full\r\n class write can.\r\n6. **Activate** — `sap_activate` explicitly.\r\n7. **Verify** — `sap_inactive_objects`.\r\n8. **Rollback on failure** — `sap_snapshot` (operation: restore).\r\n\r\nIf the same change needs Path A AND Path B (e.g. add a new method AND\r\nmodify an existing one), do Path B first (one `sap_set_source` to grow\r\nthe definition + add the new body), then Path A for the existing method\r\nmodifications. Never collapse them into a single `sap_set_source`.\r\n\r\n#### Why Path A is mandatory for body-only changes\r\n\r\n`sap_set_source` replaces the entire class source. To use it for a\r\nbody-only change, the AI would have to reconstruct every other method\r\nfrom `sap_get_source` output, preserving every line, every comment, every\r\nlocal class. That reconstruction is exactly what Rule 7a forbids: it\r\nrisks silently corrupting the class definition, dropping parameters,\r\naltering untouched methods, or losing local class definitions / macros /\r\nincludes. `sap_update_method` does the surgical replacement the\r\nSAP backend offers natively. Use it.\r\n\r\n### Step 6 — Test (Optional)\r\n\r\nIf the service is registered, test via:\r\n```\r\nsap_odata_test (service: \"{service}\", entity: \"{EntitySet}\")\r\n```\r\n\r\n## Guardrails\r\n\r\n### Method-Only Writes\r\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method logic — use `sap_update_method` per method instead (Rule 7a). This is especially important for DPC_EXT classes where you are adding method implementations.\r\n\r\n### Never Touch Generated Classes\r\n- NEVER modify _MPC or _DPC. They are regenerated by SEGW and your changes\r\n will be lost. All custom code goes in _MPC_EXT or _DPC_EXT.\r\n- If you see `\"DO NOT MODIFY\"` or `\"GENERATED\"` in a class header, stop.\r\n\r\n### Use the Correct Types\r\n- Read the MPC class to find the exact structure types (TS_, TT_ prefixed).\r\n Use those types in DPC_EXT — not custom structures. The OData framework\r\n expects the generated types.\r\n\r\n### Handle Filters Properly\r\n- Always extract filters from `io_tech_request_context->get_filter()`.\r\n Never ignore filters — returning unfiltered data is a security risk\r\n and performance disaster.\r\n- Always handle `is_paging`. Without it, large entity sets return\r\n everything and crash the browser.\r\n\r\n### Handle Errors Correctly\r\n- Use `/iwbep/cx_mgw_busi_exception` for business errors (400).\r\n- Use `/iwbep/cx_mgw_tech_exception` for technical errors (500).\r\n- Always use the message container for structured error messages.\r\n- Never use bare `MESSAGE ... TYPE 'E'` — it terminates the OData call\r\n with an unstructured dump instead of a proper error response.\r\n\r\n### Key Method Interface\r\n\r\nAll DPC_EXT entity methods share this standard interface:\r\n\r\n| Parameter | Direction | Purpose |\r\n|-----------|-----------|---------|\r\n| `iv_entity_name` | Importing | Entity type name |\r\n| `iv_entity_set_name` | Importing | Entity set name |\r\n| `it_key_tab` | Importing | Key-value pairs for the entity |\r\n| `it_filter_select_options` | Importing | OData $filter as ranges |\r\n| `is_paging` | Importing | $top and $skip |\r\n| `it_order` | Importing | $orderby |\r\n| `it_navigation_path` | Importing | Navigation segments |\r\n| `io_tech_request_context` | Importing | Full request context |\r\n| `io_data_provider` | Importing | Request payload (for create/update) |\r\n| `er_entity` | Exporting | Single entity result |\r\n| `et_entityset` | Exporting | Entity set result |\r\n\r\n### COMMIT WORK\r\n- Always use `COMMIT WORK AND WAIT` after database changes in OData\r\n context. Without `AND WAIT`, the response may return before the commit\r\n completes, causing inconsistencies.\r\n- Never commit in GET methods. Read operations must not change data.\r\n",
212
+ "sha256": "f16dfac79549e1537e848ffe7305c20024dc5f4dbc3cb3ef6b07035e9b992be4",
206
213
  "signature": "",
207
- "signedAt": "2026-09-17T20:19:05.636Z"
214
+ "signedAt": "2026-09-28T14:37:05.031Z"
208
215
  },
209
216
  "abap-spec-gap": {
210
217
  "name": "abap-spec-gap",
211
218
  "body": "---\r\nname: abap-spec-gap\r\ndescription: \"Find what is missing before coding — business questions, technical gaps, edge cases, test scenarios. Use before starting any development.\"\r\nuser-invocable: true\r\n---\r\n\r\n# Skill: abap-spec-gap\r\n\r\n## Purpose\r\n\r\nRigorously interrogate a specification, user story, ticket, or informal request to surface every missing piece of information before a single line of code is written. This skill saves days of rework by forcing ambiguity into the open while it is still cheap to resolve. It produces a categorized list of questions, assumptions to confirm, edge cases, test scenarios, and concerns — not code.\r\n\r\n## When to Use\r\n\r\n- Before starting any ABAP development task, regardless of size\r\n- When handed a vague or informal requirement (\"build a report for warehouse managers\")\r\n- When reviewing a functional spec before technical design begins\r\n- When picking up a ticket and realizing the acceptance criteria don't cover enough cases\r\n- After `/abap-radar` reveals that an existing object is more complex than the ticket assumes\r\n- Before `/abap-design` — the gaps must be closed or acknowledged before designing\r\n\r\nWhen NOT to use: requirements already clear and confirmed — go straight to `/abap-design`; sizing the work — `/abap-estimate` (after design).\r\n\r\n## Inputs Expected\r\n\r\nProvide ONE or MORE of the following:\r\n\r\n1. **User story / ticket description** — pasted directly\r\n2. **Functional spec excerpt** — partial or full\r\n3. **Acceptance criteria** — the \"given/when/then\" or bullet-list form\r\n4. **Informal request** — even a one-liner like \"we need a report showing all open delivery notes for the last 30 days\"\r\n5. **Context about system** — SAP module, release, landscape (optional but useful)\r\n\r\n**Requirement in a document?** If the requirement lives in a `.pdf` or `.docx`\r\n(a functional spec, a customer requirements doc), call `read_document(path)` to\r\nextract its text first, then analyse the extracted requirements as usual. PDFs\r\ncome back with `[page N]` markers you can cite; scanned/image PDFs return a\r\n\"no extractable text\" warning (OCR is not supported — ask for a text-based file).\r\n`read_document` requires `local_build` to be on — if the tool isn't available,\r\ntell the user to run `cspeach config set local_build on`.\r\n\r\n**When the requirement came from a document, record it.** Emit a\r\n`**Source Document:** <file name>` line in the output header (see Output\r\nStructure) naming the `.docx`/`.pdf` you read. This is the thread that ties the\r\nrunning spec to the questions: the viewer shows it to the consultant answering\r\nthe gaps, and it carries into `/abap-plan`. Use just the file name (e.g.\r\n`WarehouseReport_FunctionalSpec.docx`), not the full path. Omit the line\r\nentirely when the requirement was pasted inline (no document).\r\n\r\nThe more context provided, the more specific the questions. A one-liner will produce more generic questions. The skill will be explicit about which questions arise from the input and which are standard due-diligence questions for the domain.\r\n\r\n**System context is auto-provided when CSPeach is connected to a SAP system.** A `<session_context>` block at the top of the user message exposes a `<sap_system .../>` element with `platform`, `release`, `abap_version`, and `deployment` attributes. **Do not ask the user about any field already present there.** Treat that block as ground truth and design your recommendations around it.\r\n\r\n## Required Behavior\r\n\r\n0. **Read `<session_context>` FIRST.** Before generating any question, scan the prompt for a `<session_context>` block. If `<sap_system platform=\"...\" release=\"...\" abap_version=\"...\" deployment=\"...\"/>` is present, those facts are KNOWN — never ask about them. Use them to scope your recommendations:\r\n - `platform=\"S/4HANA\"` → recommend the clean-core path (released CDS views, F_*_BUK auth, RAP for write paths). Frame older patterns as \"OR if your team prefers direct table reads, say so.\"\r\n - `platform=\"ECC\"` → recommend FOR ALL ENTRIES + tables + ALV. Don't suggest CDS unless ECC ≥ 7.40 SP08.\r\n - `abap_version=\"7.55\"+` → recommend modern syntax (FILTER, REDUCE, FOR... NEW). Don't suggest TYPES BEGIN OF when DATA inline works.\r\n - `deployment=\"public-cloud\"` → no SE38, no classic GUI; everything must be RAP / Fiori Elements.\r\n When `<sap_system>` is missing or `platform=\"unknown\"`, the platform/release questions are still fair game.\r\n\r\n1. **Parse the input** — Identify what is stated, what is implied, and what is absent. Note the business process domain (SD, MM, WM, FI, etc.) to target questions appropriately.\r\n\r\n2. **Generate Missing Business Questions** — Questions the business owner or product owner must answer before technical work can begin. Focus on: scope, user roles, triggering conditions, output destinations, data freshness requirements, authorization model, exception handling, volume expectations.\r\n\r\n3. **Generate Missing Technical Questions** — Questions for the technical architect or lead developer. Focus on: existing objects to reuse or extend, landscape constraints, transport strategy, performance requirements, integration points, interface contracts. **DO NOT ask about SAP release / platform / cloud-vs-on-prem / ABAP version when those facts are in `<sap_system>`** — recommend instead. Example: instead of *\"What SAP release are you on? ECC, S/4HANA on-prem, or S/4 Cloud?\"*, write *\"Recommended approach (S/4HANA 2022, on-prem): query I_SalesOrder + I_SalesOrderItem CDS views with F_VBAK_VKO auth. Flag if you'd prefer direct VBAK access instead.\"*\r\n\r\n3a. **Generate Standard-vs-Custom (Fit-Gap) Questions** — The first question a senior functional consultant asks: **is there already a standard SAP report, transaction, Fiori app, BAPI, or released CDS view that does this — so why build custom at all?** Name the concrete standard candidate(s) that overlap the requirement (e.g. *\"VL06O / Outbound Delivery Monitor already lists open deliveries with plant + date selection\"*, *\"standard Fiori app F0869 'Manage Outbound Deliveries'\"*, *\"BAPI_DELIVERY_GETLIST\"*), then challenge the gap: what does standard NOT give them that justifies custom development and its lifetime maintenance cost? Mark as `[BLOCKER]` when a well-known standard clearly covers the core ask — a custom build with no documented fit-gap is a finding, not a given. If the spec already names a standard tcode (it often does — users say \"we scroll through X\"), call that out explicitly. When no standard fits, say so briefly and move on; do NOT invent a standard object you are unsure exists.\r\n\r\n4. **Generate Data Assumptions to Confirm** — Explicit statements about data that the spec assumes but has not verified: table structures, field availability, data quality, mandatory field population, date/time handling, multi-currency, multi-language.\r\n\r\n5. **Generate Authorization Questions** — Who can run this? Who can see which data? Are there row-level restrictions (org unit, company code, plant)? Does a new authorization object need to be created?\r\n\r\n6. **Generate Edge Cases Not Covered** — Scenarios the acceptance criteria do not address: empty result sets, maximum volumes, deleted/archived records, error states, concurrent execution, partial data, mid-process system failures.\r\n\r\n7. **Generate Suggested Test Scenarios** — Concrete test cases (not abstract) that a developer would need to cover in unit tests and integration tests. Include positive cases, negative cases, boundary cases, and authorization failure cases.\r\n\r\n8. **Generate Transport and Release Concerns** — Questions about the development and deployment process: which transport route, which system landscape, go-live date constraints, dependencies on other transports, cutover considerations.\r\n\r\n9. **Generate Clean Core / API Concerns** — When `<sap_system platform=\"S/4HANA\"/>` is known, RECOMMEND the clean-core path proactively (released CDS view, BAdI / RAP extension over modification, ALV native XLSX export over `cl_gui_frontend_services`). Flag the OPPOSITE direction as \"OR if you must use direct table access for X reason, document it.\" Don't open-question the platform — you already know it. If `<sap_system>` is missing or `platform=\"ECC\"`, fall back to flagging both options.\r\n\r\n10. **Produce a prioritized summary** — Mark questions as `[BLOCKER]` (cannot start without answer), `[IMPORTANT]` (should be answered before build), or `[NICE TO KNOW]` (useful but can proceed with documented assumption).\r\n\r\n11. **State what was inferred.** If the business process domain was inferred from the input rather than stated, label it. Do not invent requirements.\r\n\r\n11a. **Offer answer choices where the question has them.** For any question with a small, discrete set of likely answers, add an indented `Options:` line DIRECTLY beneath that question, pipe-separated — e.g. `Options: gross (BRGEW) | net (NTGEW)`. Use `Options (multi):` when more than one answer can apply at once (e.g. which delivery statuses count as \"open\"). Rules: keep each option short (2–6 words); 2–5 options is ideal; the answer UI adds \"write my own\" and \"skip\" automatically, so do NOT list those; OMIT the line entirely for genuinely open questions (a go-live date, a free-form scope statement). This lets a developer answer a blocker by picking, not typing.\r\n\r\n11b. **Clarify interactively in one form, not a drip.** If something must be resolved before the analysis can even start (which document to read, which of two pasted requirements is in scope), use the `ask_question` tool and batch the related questions — up to 4 in ONE call via the `questions` array; one form beats sequential round-trips. Keep each question's `header` ≤12 chars. This is for pre-analysis clarification only — the gap report itself stays a written deliverable, not an interactive quiz.\r\n\r\n12. **Forge Rules compliance.** This skill is read-only (Rule 6). Rule 1 explicitly covers this use case — no code generation before missing questions are answered.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Spec Gap Analysis — [TICKET/STORY TITLE or brief description]\r\n\r\n**Input Type:** [User Story / Functional Spec / Informal Request]\r\n**Source Document:** [file name — ONLY when the requirement came from a .docx/.pdf you read with read_document; omit this line otherwise]\r\n**Inferred Domain:** [SAP Module — e.g., Warehouse Management (WM/EWM)] [INFERRED / STATED]\r\n**Total Questions:** [N] — [X] Blockers, [Y] Important, [Z] Nice to Know\r\n\r\n---\r\n\r\n### Missing Business Questions\r\n\r\n1. [BLOCKER] [Question]\r\n Options: [choice A] | [choice B]\r\n2. [BLOCKER] [Question with several applicable answers]\r\n Options (multi): [choice A] | [choice B] | [choice C]\r\n3. [IMPORTANT] [Open question with no fixed choices — no Options line]\r\n...\r\n\r\n---\r\n\r\n### Missing Technical Questions\r\n\r\n1. [BLOCKER] [Question]\r\n2. [IMPORTANT] [Question]\r\n...\r\n\r\n---\r\n\r\n### Standard-vs-Custom (Fit-Gap)\r\n\r\n1. [BLOCKER] [Named standard candidate] already does [X] — what does it not give you that justifies a custom build?\r\n2. [IMPORTANT] [Question challenging another reuse/standard option]\r\n...\r\n\r\n---\r\n\r\n### Data Assumptions to Confirm\r\n\r\n1. [IMPORTANT] [Assumption stated as a question or hypothesis to verify]\r\n2. [NICE TO KNOW] [Assumption]\r\n...\r\n\r\n---\r\n\r\n### Authorization Questions\r\n\r\n1. [BLOCKER] [Question about who can run / see / act]\r\n2. [IMPORTANT] [Question about data restrictions]\r\n...\r\n\r\n---\r\n\r\n### Edge Cases Not Covered\r\n\r\n1. [IMPORTANT] What happens when [scenario]?\r\n2. [IMPORTANT] What happens when [scenario]?\r\n...\r\n\r\n---\r\n\r\n### Suggested Test Scenarios\r\n\r\n| # | Scenario | Type | Expected Result |\r\n|---|----------|------|----------------|\r\n| 1 | [Concrete test setup] | Positive | [What should happen] |\r\n| 2 | [Concrete test setup] | Negative | [What should happen] |\r\n| 3 | [Concrete test setup] | Boundary | [What should happen] |\r\n| 4 | [Concrete test setup] | Auth Failure | [What should happen] |\r\n\r\n---\r\n\r\n### Transport and Release Concerns\r\n\r\n1. [Question or concern about deployment]\r\n2. [Question or concern about dependencies]\r\n...\r\n\r\n---\r\n\r\n### Clean Core / API Concerns\r\n\r\n1. [Concern about modification risk or clean extension approach]\r\n...\r\n\r\n---\r\n\r\n### Recommended Next Steps\r\nBefore starting development, get answers to all [BLOCKER] items. Document assumptions for [IMPORTANT] items if answers cannot be obtained quickly. Proceed to `/abap-design` once blockers are resolved.\r\n```\r\n\r\n## Guardrails\r\n\r\n- **Do not start designing or coding.** This skill produces questions, not answers. If the temptation is to solve a gap rather than surface it, resist — surface it.\r\n- **Do not invent requirements.** Questions must be grounded in the input provided or in standard due-diligence for the identified domain. Do not ask leading questions that imply a particular technical solution.\r\n- **Label domain inferences.** If the SAP module was inferred from keywords (e.g., \"delivery note\" → WM/SD), say so.\r\n- **Be specific.** \"What authorization is needed?\" is a weak question. \"Does the user role WM_WAREHOUSE_CLERK need display access to all warehouses, or only their assigned warehouse number?\" is a strong question.\r\n- **Quality over count.** Aim for 15+ questions when system context is sparse. When `<sap_system>` is provided AND the input is detailed, fewer focused questions (8-12) are better than padding the list with platform/release questions you already know the answer to. **Padding the list with answered-by-context questions is a regression, not a feature.**\r\n- **Never ask what `<session_context>` already states.** A question like *\"Are we on S/4HANA?\"* when `<sap_system platform=\"S/4HANA\"/>` is in context is a quality bug. Instead, structure that section as *\"Recommended for your S/4HANA 2022 system: ... — flag if your team prefers an alternative.\"*\r\n- **Follow the 10 Forge Rules.** Especially Rule 1 (no blind code generation — this skill enforces that rule).\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-spec-gap\r\n\r\nUser Story: Warehouse Delivery Report\r\n\r\nAs a warehouse manager, I want to see a list of all open delivery notes so that I can plan picking activities for the day.\r\n\r\nAcceptance Criteria:\r\n- Report shows all open outbound deliveries\r\n- Can filter by warehouse number\r\n- Shows delivery number, ship-to party, planned goods issue date, total weight\r\n- Export to Excel possible\r\n- Available in SAP GUI\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Spec Gap Analysis — Warehouse Delivery Report\r\n\r\n**Input Type:** User Story with Acceptance Criteria\r\n**Inferred Domain:** Warehouse Management / Sales and Distribution (WM / SD) [INFERRED — \"delivery notes\" and \"picking\" indicate WM/LE-SHP]\r\n**Total Questions:** 27 — 6 Blockers, 15 Important, 6 Nice to Know\r\n\r\n---\r\n\r\n### Missing Business Questions\r\n\r\n1. [BLOCKER] What defines \"open\"? Deliveries with no goods issue posted? Deliveries with status \"being processed\"? Deliveries not yet fully picked? SAP delivery status fields (LIKP-KOSTK, LIKP-WBSTK) have multiple possible values — the exact filter criteria must be defined.\r\n\r\n2. [BLOCKER] Which warehouse numbers should be selectable? All warehouses in the system, or only those assigned to the user's plant? Is this a single-warehouse system or multi-warehouse?\r\n\r\n3. [BLOCKER] What is \"total weight\"? Gross weight (LIKP-BRGEW) or net weight (LIKP-NTGEW)? Which unit of measure — the delivery's base unit or always KG?\r\n\r\n4. [BLOCKER] Who are the users of this report? Only warehouse managers, or also warehouse clerks and supervisors? This determines the authorization design.\r\n\r\n5. [BLOCKER] What does \"export to Excel\" mean technically? ALV built-in export (no development needed), a dedicated download button to a specific format, or integration with a file server path?\r\n\r\n6. [BLOCKER] Is this for S/4HANA, ECC, or EWM (Extended Warehouse Management)? Delivery tables and availability differ significantly. LIKP/LIPS are ECC/S4 Shipping tables; EWM has its own delivery model (WDEL).\r\n\r\n7. [IMPORTANT] Should the report show both stock transfer deliveries and customer deliveries, or only outbound customer deliveries (delivery type LF and related)?\r\n\r\n8. [IMPORTANT] Is there a time window? \"All open deliveries\" could mean hundreds of thousands of records in a large system. Should it default to deliveries with planned goods issue date in the next N days?\r\n\r\n9. [IMPORTANT] What is the planned goods issue date field — LIKP-WADAT (planned GI date) or LIKP-LDDAT (loading date)? These are often confused.\r\n\r\n10. [IMPORTANT] Should the report be runnable in background (batch job)? If so, WRITE output is not suitable — ALV with spool is required.\r\n\r\n11. [IMPORTANT] What additional fields do warehouse managers actually need? Ship-to party name (requires join to KNA1)? Route? Forwarding agent? Storage location? The acceptance criteria show only 4 fields — likely insufficient for real planning.\r\n\r\n---\r\n\r\n### Missing Technical Questions\r\n\r\n1. [BLOCKER] Does a similar report already exist (standard SAP transaction VL06O — Outbound Delivery Monitor, or LT0A for WM transfer orders)? Has standard functionality been evaluated and rejected? Avoiding duplicate development is a Forge Rule 1 concern.\r\n\r\n2. [BLOCKER] What package and transport route should custom objects be created in? Which development system, which system landscape (DEV → QAS → PRD)?\r\n\r\n3. [IMPORTANT] Is there a custom CDS view or BAPI already used for delivery data in this system? Using an existing data layer avoids direct table access and reduces maintenance.\r\n\r\n4. [IMPORTANT] What is the expected data volume? How many open deliveries exist in the system at peak? This determines whether a simple SELECT is sufficient or whether pagination/secondary indexes must be planned.\r\n\r\n5. [IMPORTANT] Should this be a classic ABAP report (SE38/SA38) or a Fiori app/analytical query? The acceptance criteria says \"SAP GUI\" but if the roadmap includes Fiori migration this affects the technical approach.\r\n\r\n6. [IMPORTANT] Who maintains the authorization roles? Is there a basis team that must create new authorization profiles, or does the developer maintain them?\r\n\r\n7. [IMPORTANT] Are there naming conventions for this customer's custom objects? (Z prefix, Y prefix, specific namespace like ZNNN_WAREHOUSE?)\r\n\r\n---\r\n\r\n### Data Assumptions to Confirm\r\n\r\n1. [IMPORTANT] Assumes LIKP (delivery header) and LIPS (delivery items) are the correct tables. Confirm that delivery data is not stored in a custom table or replicated to a reporting layer.\r\n\r\n2. [IMPORTANT] Assumes LIKP-LGNUM (warehouse number) is populated for all relevant deliveries. In some configurations, warehouse number is only assigned after TO (transfer order) creation — earlier-stage deliveries may have LGNUM blank.\r\n\r\n3. [IMPORTANT] Assumes ship-to party (LIKP-KUNNR) name is available in KNA1 and does not require a separate address lookup via ADRNR/ADR6.\r\n\r\n4. [IMPORTANT] Assumes planned goods issue date (LIKP-WADAT) is consistently populated. If the delivery was created without a planned GI date, the field is initial — the report must handle blank dates.\r\n\r\n5. [NICE TO KNOW] Assumes deliveries are not archived. If the system runs ILM/archiving for LE_DELIVERY, very old open deliveries may not be in the active tables.\r\n\r\n---\r\n\r\n### Authorization Questions\r\n\r\n1. [BLOCKER] What authorization object controls display access to deliveries? Standard is W_LIKP_WGM (warehouse number + goods movement type) for WM, or V_LIKP_VST for SD shipping point access. Which model applies here?\r\n\r\n2. [BLOCKER] Should the warehouse number filter be a hard restriction (users can only see their assigned warehouse) or a soft filter (default to their warehouse but can change it)?\r\n\r\n3. [IMPORTANT] Is there row-level security by plant or company code? Should a warehouse manager in Plant 1000 be able to see deliveries for Plant 2000?\r\n\r\n4. [IMPORTANT] Does running the report require a specific SAP role, or should it be accessible to any user who can log on to the system?\r\n\r\n---\r\n\r\n### Edge Cases Not Covered\r\n\r\n1. [IMPORTANT] What should happen when there are zero open deliveries matching the filter? Display an informational message? Show an empty ALV grid? Return to the selection screen?\r\n\r\n2. [IMPORTANT] What if the warehouse number entered on the selection screen does not exist? Validate and show an error, or silently return no results?\r\n\r\n3. [IMPORTANT] What if a delivery has multiple warehouse numbers (split deliveries to multiple warehouses)? Is it shown once or multiple times?\r\n\r\n4. [IMPORTANT] What about deliveries with a future planned goods issue date that are already fully picked? Are they \"open\" or \"complete\"?\r\n\r\n5. [IMPORTANT] What happens if the user has no authorization for the selected warehouse? Error message? Empty result? Partial result with authorized deliveries only?\r\n\r\n6. [NICE TO KNOW] What if the system is under heavy load and the query takes more than the dialogue work process time limit (typically 10 minutes)? Is background execution expected?\r\n\r\n---\r\n\r\n### Suggested Test Scenarios\r\n\r\n| # | Scenario | Type | Expected Result |\r\n|---|----------|------|----------------|\r\n| 1 | Run for warehouse 0001 with 50 open deliveries on today's date | Positive | 50 rows shown with correct fields; export to Excel works |\r\n| 2 | Run for warehouse 0001 with no open deliveries (all GI posted) | Boundary | Empty ALV grid with informational message \"No deliveries found\" |\r\n| 3 | Run with no warehouse number entered (blank filter) | Boundary | Either shows all warehouses user is authorized for, or shows validation error — depends on design decision |\r\n| 4 | Run with warehouse number that does not exist (e.g., 9999) | Negative | Validation error or empty result with message — not a dump |\r\n| 5 | Run as user with no W_LIKP_WGM authorization for warehouse 0001 | Auth Failure | Authorization error message; no data displayed |\r\n| 6 | Run as user authorized for warehouse 0001 but not 0002; filter is blank | Auth Boundary | Only warehouse 0001 deliveries shown, or auth error for 0002 deliveries only |\r\n| 7 | Delivery with LGNUM blank (not yet assigned to warehouse) | Edge Case | Not shown in results (or shown with warning) — no dump from blank LGNUM |\r\n| 8 | Delivery with WADAT (planned GI date) blank | Edge Case | Row shown with blank date — not excluded, not causing dump |\r\n| 9 | Export to Excel with 10,000+ rows | Performance | Export completes; file is valid; no time-out |\r\n\r\n---\r\n\r\n### Transport and Release Concerns\r\n\r\n1. [IMPORTANT] What is the target go-live date? ABAP development, basis configuration, and authorization role transport each have lead times. If go-live is within 2 weeks, the scope may need to be reduced.\r\n\r\n2. [IMPORTANT] Are there change freeze periods (month-end close, year-end, planned maintenance windows) that would block transport to production?\r\n\r\n3. [IMPORTANT] Does the system landscape have a Quality/UAT system where warehouse managers can test before production? User acceptance testing is implied by \"warehouse manager\" as the persona.\r\n\r\n4. [NICE TO KNOW] Is this the first custom object in this package, or does an existing transport request for the package exist that this should be added to?\r\n\r\n---\r\n\r\n### Clean Core / API Concerns\r\n\r\n1. [IMPORTANT] If the system is S/4HANA: confirm with the architect whether the agreed data source is the CDS view (e.g. I_OutboundDelivery / its projection) or direct LIKP/LIPS access. Direct table access has clean-core implications and may not survive future S/4 releases — this is a design decision that must be made before /abap-design.\r\n\r\n2. [IMPORTANT] The \"export to Excel\" requirement can be fulfilled by ALV's built-in spreadsheet export (no additional development). If a dedicated download button is requested, confirm that the ALV approach was evaluated and rejected before custom download logic is built.\r\n\r\n3. [NICE TO KNOW] If the customer's roadmap includes a move to SAP BTP or Fiori Launchpad, a classic ABAP ALV report will not be accessible in that environment. A CDS-view-based analytical app (using SAP Analytics Cloud or Fiori Elements) might be the future-proof choice.\r\n\r\n---\r\n\r\n### Recommended Next Steps\r\nBefore starting development, get answers to all 6 [BLOCKER] items — especially the definition of \"open\", the SAP system version (ECC vs S/4 vs EWM), and the authorization model. Document assumptions for [IMPORTANT] items if answers cannot be obtained quickly. Once blockers are resolved, proceed to `/abap-design` to produce the object list and architecture.\r\n```\r\n",
212
219
  "sha256": "bdebb37aa4e4c8871824cd1e32c7f522d63622166da6bc09664efb5d409f1592",
213
220
  "signature": "",
214
- "signedAt": "2026-09-17T20:19:05.680Z"
221
+ "signedAt": "2026-09-28T14:37:05.061Z"
215
222
  },
216
223
  "abap-test": {
217
224
  "name": "abap-test",
218
225
  "body": "---\r\nname: abap-test\r\ndescription: >\r\n Generate ABAP Unit test classes for existing code — analyze the class under\r\n test, design test cases covering happy path, edge cases, and error paths,\r\n create test doubles for dependencies, write assertions using\r\n CL_ABAP_UNIT_ASSERT. In MCP mode: writes the test include to SAP,\r\n executes unit tests, and reports results. Use to improve coverage,\r\n document current behavior before refactoring, or prepare for audit.\r\nphase: VERIFY\r\nrequires_mcp: optional\r\nforge_rules: [1, 4, 6, 7, 10]\r\nversion: \"1.1\"\r\n---\r\n\r\n# abap-test\r\n\r\n## Purpose\r\n\r\nGenerate complete, runnable ABAP Unit test classes for an existing class or method. The skill reads the class under test, identifies testable methods and their dependencies, designs a test strategy (what to test and why), generates test doubles using interfaces or `CL_ABAP_TESTDOUBLE`, and writes `CL_ABAP_UNIT_ASSERT`-based assertions covering happy path, negative paths, and edge cases. When MCP is connected, the skill writes the test include to the SAP system, executes the tests via `sap_run_unit_test`, and returns a pass/fail report.\r\n\r\n## When to Use\r\n\r\n- You have a class and want test coverage before making changes\r\n- You generated code with `/abap-generate` and now want tests for it\r\n- You need to verify behavior before or after a refactoring\r\n- You want to achieve a minimum coverage threshold before releasing to transport\r\n- Audit-driven coverage uplift (e.g. \"5% today, audit needs 60%\")\r\n- Custom code preparation before S/4HANA migration\r\n\r\nDo NOT use this skill on reports or FORM-based code without first refactoring\r\nto OO with `/abap-refactor` — FORM routines cannot be unit tested directly.\r\n\r\n## Inputs Expected\r\n\r\nRequired (one of):\r\n- The ABAP class source code (paste) or the class name (if MCP is connected)\r\n- A cca-assessment envelope via `--from @<file>` (v0.7 chain).\r\n\r\nUseful (will be asked):\r\n- Which methods to prioritize for testing\r\n- Whether dependencies should be mocked (interfaces / test doubles)\r\n- Target code coverage percentage (default: aim for 80%+ on critical methods)\r\n- Whether to include negative / error path tests\r\n- Scope: `public` (default — test public methods only) or `all` (include private via friend)\r\n- Write: `yes` (create test class on system) or `no` (output only). Default: `no`\r\n- Transport: required if write = yes and not `$TMP`\r\n\r\n### `--from @<cca-assessment>` chain (v0.7)\r\n\r\nWhen invoked as `/abap-test --from @<cca-assessment-file>`, the CLI adapter\r\nreads the assessment's `content.detailPath` and pre-filters the work list to\r\n**every classifiable object that's NOT marked `retire`** — keep, fix, and\r\nredesign all need behaviour locked BEFORE downstream `/abap-upgrade-fix` or\r\n`/abap-modernize` runs touch them.\r\n\r\nThe skill iterates the filtered list, generates an ABAP Unit test class per\r\ntarget, writes them to the requested transport (asked once per session),\r\nand emits a `test-coverage` manifest at the end. The intent is \"lock the\r\ncontract before changing it\" — so downstream chains can re-run the same\r\ntests after their fixes to verify no semantic regression.\r\n\r\nIf SAP's coverage tool (SCMP) is reachable, capture coverage % before and\r\nafter; otherwise emit `-` in those manifest fields and continue. The\r\ngenerated test class is the deliverable either way; coverage % is a\r\nnice-to-have, not load-bearing.\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Analyze the Class Under Test\r\n\r\nRead the class definition and identify:\r\n- Public and protected methods (these are testable)\r\n- Method signatures: importing / exporting / returning / raising\r\n- Dependencies: what does each method call (other classes, FMs, DB selects)?\r\n- Which dependencies can be injected for mocking\r\n\r\n### Step 2 — Design Test Strategy\r\n\r\nProduce a test matrix before writing any code:\r\n\r\n| Method | Scenario | Input | Expected Output | Dependency to Mock |\r\n|--------|----------|-------|-----------------|-------------------|\r\n| {method} | Happy path | {input} | {output} | {dependency} |\r\n| {method} | Edge case | {input} | {output} | {dependency} |\r\n| {method} | Error path | {input} | Exception raised | {dependency} |\r\n\r\nAsk: \"Does this test strategy look complete? Shall I generate the test class?\"\r\n\r\n### Step 3 — Generate Test Class\r\n\r\nProduce a full test class using:\r\n- `CLASS {test_class} DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT`\r\n- `CL_ABAP_TESTDOUBLE` for interface-based mocks\r\n- `CL_ABAP_UNIT_ASSERT` for all assertions: assert_equals, assert_true,\r\n assert_false, assert_initial, assert_not_initial, assert_differs,\r\n assert_table_contains, fail\r\n- `SETUP` method for object instantiation\r\n- `TEARDOWN` method if needed\r\n- One test method per scenario (not one per method)\r\n- Descriptive method names: `test_{method}_{scenario}` convention\r\n\r\n### Step 4 — Handle Dependencies\r\n\r\nFor each dependency identified:\r\n- If the class under test uses an interface for the dependency: use\r\n CL_ABAP_TESTDOUBLE=>CREATE to create a test double, configure with ALLOW\r\n- If the class directly instantiates a dependency (anti-pattern): note it and\r\n suggest injection refactoring before testing\r\n- For DB dependencies: note that unit tests should not hit the database;\r\n recommend extracting SELECT to a repository class\r\n\r\n### Step 5 — Confirm Before Writing\r\n\r\nBefore any write to the SAP system, present the complete test class source and ask explicitly:\r\n\r\n```\r\nGenerated test class for {CLASS_NAME}.\r\nMethods covered: {N} of {TOTAL} public methods.\r\nTest methods: {count}.\r\nTest doubles: {list}.\r\n\r\nWrite to system? [y/n]\r\n```\r\n\r\nWait for explicit `y` / `yes` before proceeding to MCP write. If the developer says no, output the source and stop.\r\n\r\n### Step 6 — MCP Execution (if connected)\r\n\r\nIf MCP is active:\r\n1. Write the test class to the class's **testclasses (CCAU)** include with\r\n `sap_set_class_include` (includeType: `testclasses`) — the test code goes into\r\n the test include, NOT the main class source (do NOT use `sap_set_source`). The\r\n tool creates the include first if it does not exist yet; trust `written:true`\r\n (it is read-back verified).\r\n2. Run a syntax check (`sap_syntax_check`); fix and re-write on errors.\r\n3. `sap_activate` the class and confirm it is active (`sap_inactive_objects`).\r\n4. Execute the suite with `sap_run_unit_test`, then parse the result.\r\n\r\n**⚠ Never claim the suite is green on an unverified run.** `sap_run_unit_test`\r\nmay FAIL (HTTP 400) or return ZERO executed tests (an empty result) on some\r\nkernels — AUnit execution via ADT is environment-dependent (authorization /\r\nsystem config), independent of whether the test class is correct. Treat the run\r\nas the green gate ONLY when it returns a non-zero test count with explicit\r\npass/fail per method. If it errors OR reports 0 tests:\r\n- The test class is still the deliverable — it is written, syntax-clean, and\r\n active. State that plainly.\r\n- Do NOT report the exit gate satisfied or coverage met. A 0-test or errored run\r\n is NOT a pass — that would be a false green.\r\n- Give the manual unblock: open the class in ADT/Eclipse → **Run As → ABAP Unit\r\n Test** (Ctrl+Shift+F10; \"…With Coverage\" for the % gate), confirm green, and\r\n record it as the `unblockPath`. A later resume can then verify.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Test Strategy\r\n\r\n**Class Under Test:** {class_name}\r\n**Methods Analyzed:** {count}\r\n**Test Cases Designed:** {count}\r\n\r\n### Test Matrix\r\n| Method | Scenario | Input Summary | Expected | Mock Required |\r\n|--------|----------|---------------|----------|---------------|\r\n| {method} | {scenario} | {input} | {expected} | {mock} |\r\n\r\n---\r\n\r\n## Generated Test Class\r\n\r\n```abap\r\n{full_test_class_source}\r\n```\r\n\r\n---\r\n\r\n## Coverage Estimate\r\n\r\n| Method | Scenarios Covered | Coverage Estimate |\r\n|--------|-------------------|-------------------|\r\n| {method_name} | {scenario_count} | {estimate}% |\r\n\r\n**Overall Estimated Coverage:** ~{total}%\r\n\r\n---\r\n\r\n## Test Execution Results (MCP Mode)\r\n\r\n| Test Method | Status | Duration | Message |\r\n|-------------|--------|----------|---------|\r\n| {test_method} | PASSED / FAILED | {ms} | {details} |\r\n\r\n**Summary:** {passed} passed, {failed} failed, {skipped} skipped\r\n\r\n---\r\n\r\n## Dependencies That Need Refactoring\r\n\r\n{list of any hard-coded dependencies preventing full mocking}\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER generate tests that hit the real database — unit tests must be isolated\r\n- NEVER use COMMIT WORK or ROLLBACK WORK in unit test methods\r\n- NEVER hardcode client-dependent master data keys in test data\r\n- Tests must have RISK LEVEL HARMLESS unless there is a strong reason for DANGEROUS\r\n- DURATION SHORT — flag if a test needs longer\r\n- Default scope is public methods only unless `scope=all`\r\n- All assertions must have a meaningful `msg` parameter for failure identification\r\n- If a class cannot be tested due to missing injection points, document this and\r\n recommend a refactoring step first — do not generate meaningless tests\r\n- If MCP test run fails: report the failure, do not hide it or retry silently\r\n- Code coverage is estimated — actual coverage needs ADT Coverage Tool or SLIN\r\n- Generated tests are a starting point — tell the developer to review expected values, since they were inferred from code and may not match the business rules\r\n\r\n## Test Patterns\r\n\r\n### Pattern: Constructor Injection\r\n```abap\r\n\"production code\r\nCLASS zcl_order DEFINITION.\r\n PUBLIC SECTION.\r\n METHODS constructor IMPORTING io_db TYPE REF TO zif_order_db.\r\nENDCLASS.\r\n\r\n\"test code — inject mock\r\nDATA(lo_mock_db) = NEW lcl_mock_db( ).\r\nDATA(lo_cut) = NEW zcl_order( io_db = lo_mock_db ).\r\n```\r\n\r\n### Pattern: Database Access Without Interface\r\n```abap\r\n\"If the class does direct SELECT without injection:\r\n\"Test with INSERT into test table in setup, DELETE in teardown\r\n\"Or use CL_OSQL_TEST_ENVIRONMENT for test isolation\r\n```\r\n\r\n### Pattern: Exception Testing\r\n```abap\r\nTRY.\r\n mo_cut->validate( ls_invalid_data ).\r\n cl_abap_unit_assert=>fail( msg = 'Should have raised exception' ).\r\n CATCH zcx_validation_error INTO DATA(lx).\r\n cl_abap_unit_assert=>assert_bound( act = lx ).\r\nENDTRY.\r\n```\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-test\r\n\r\nGenerate unit tests for this approval engine class. Focus on the approve() and\r\nreject() methods. I want to mock the email sender interface.\r\n\r\nCLASS zcl_order_approval DEFINITION PUBLIC FINAL CREATE PUBLIC.\r\n PUBLIC SECTION.\r\n METHODS constructor\r\n IMPORTING io_email_sender TYPE REF TO zif_email_sender.\r\n METHODS approve\r\n IMPORTING iv_order_id TYPE vbeln\r\n RAISING zcx_approval_error.\r\n METHODS reject\r\n IMPORTING iv_order_id TYPE vbeln\r\n iv_reason TYPE string\r\n RAISING zcx_approval_error.\r\n PRIVATE SECTION.\r\n DATA mo_email_sender TYPE REF TO zif_email_sender.\r\n METHODS get_order_status\r\n IMPORTING iv_order_id TYPE vbeln\r\n RETURNING VALUE(rv_status) TYPE char2.\r\nENDCLASS.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Test Strategy\r\n\r\n**Class Under Test:** ZCL_ORDER_APPROVAL\r\n**Methods Analyzed:** 2 public (approve, reject), 1 private (get_order_status)\r\n**Test Cases Designed:** 6\r\n\r\n### Test Matrix\r\n| Method | Scenario | Input | Expected | Mock Required |\r\n|---------|---------------------------|-------------------|---------------------------|--------------------|\r\n| approve | Happy path — open order | iv_order_id='001' | No exception, email sent | zif_email_sender |\r\n| approve | Order already approved | iv_order_id='002' | ZCX_APPROVAL_ERROR raised | zif_email_sender |\r\n| approve | Order not found | iv_order_id='999' | ZCX_APPROVAL_ERROR raised | zif_email_sender |\r\n| reject | Happy path — with reason | id='001',reason | No exception, email sent | zif_email_sender |\r\n| reject | Empty reason | id='001',reason=''| ZCX_APPROVAL_ERROR raised | zif_email_sender |\r\n| reject | Already rejected | iv_order_id='003' | ZCX_APPROVAL_ERROR raised | zif_email_sender |\r\n\r\n---\r\n\r\n## Generated Test Class\r\n\r\n```abap\r\nCLASS zcl_order_approval_test DEFINITION FINAL\r\n FOR TESTING\r\n RISK LEVEL HARMLESS\r\n DURATION SHORT.\r\n\r\n PRIVATE SECTION.\r\n DATA mo_cut TYPE REF TO zcl_order_approval. \"class under test\r\n DATA mo_email_double TYPE REF TO zif_email_sender. \"test double\r\n\r\n METHODS setup.\r\n METHODS teardown.\r\n\r\n \"--- approve() tests ---\r\n METHODS test_approve_open_order FOR TESTING.\r\n METHODS test_approve_already_approved FOR TESTING.\r\n METHODS test_approve_not_found FOR TESTING.\r\n\r\n \"--- reject() tests ---\r\n METHODS test_reject_with_reason FOR TESTING.\r\n METHODS test_reject_empty_reason FOR TESTING.\r\n METHODS test_reject_already_rejected FOR TESTING.\r\nENDCLASS.\r\n\r\nCLASS zcl_order_approval_test IMPLEMENTATION.\r\n\r\n METHOD setup.\r\n \"Create test double for email sender interface\r\n mo_email_double ?= cl_abap_testdouble=>create( 'ZIF_EMAIL_SENDER' ).\r\n\r\n \"Inject the double into the class under test\r\n mo_cut = NEW zcl_order_approval( io_email_sender = mo_email_double ).\r\n ENDMETHOD.\r\n\r\n METHOD teardown.\r\n CLEAR: mo_cut, mo_email_double.\r\n ENDMETHOD.\r\n\r\n METHOD test_approve_open_order.\r\n \"Arrange: configure mock to expect send_approval_email call\r\n cl_abap_testdouble=>configure_call( mo_email_double\r\n )->ignore_all_parameters( ).\r\n mo_email_double->send_approval_email( iv_order_id = '0000000001'\r\n iv_recipient = '' ).\r\n\r\n \"Act\r\n TRY.\r\n mo_cut->approve( iv_order_id = '0000000001' ).\r\n CATCH zcx_approval_error INTO DATA(lx_error).\r\n cl_abap_unit_assert=>fail(\r\n msg = |approve raised unexpected exception: { lx_error->get_text( ) }| ).\r\n ENDTRY.\r\n\r\n \"Assert: verify the email mock was called\r\n cl_abap_testdouble=>verify_expectations( mo_email_double ).\r\n ENDMETHOD.\r\n\r\n METHOD test_approve_already_approved.\r\n \"Arrange: order 0000000002 is pre-set to approved status in test data\r\n\r\n \"Act + Assert: expect exception\r\n TRY.\r\n mo_cut->approve( iv_order_id = '0000000002' ).\r\n cl_abap_unit_assert=>fail(\r\n msg = 'Expected ZCX_APPROVAL_ERROR for already-approved order' ).\r\n CATCH zcx_approval_error.\r\n \"Expected — test passes\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\n METHOD test_approve_not_found.\r\n TRY.\r\n mo_cut->approve( iv_order_id = '9999999999' ).\r\n cl_abap_unit_assert=>fail(\r\n msg = 'Expected ZCX_APPROVAL_ERROR for non-existent order' ).\r\n CATCH zcx_approval_error.\r\n \"Expected — test passes\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\n METHOD test_reject_with_reason.\r\n cl_abap_testdouble=>configure_call( mo_email_double\r\n )->ignore_all_parameters( ).\r\n mo_email_double->send_rejection_email( iv_order_id = '' iv_reason = '' ).\r\n\r\n TRY.\r\n mo_cut->reject(\r\n iv_order_id = '0000000001'\r\n iv_reason = 'Price exceeds budget limit' ).\r\n CATCH zcx_approval_error INTO DATA(lx_error).\r\n cl_abap_unit_assert=>fail(\r\n msg = |reject raised unexpected exception: { lx_error->get_text( ) }| ).\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\n METHOD test_reject_empty_reason.\r\n TRY.\r\n mo_cut->reject(\r\n iv_order_id = '0000000001'\r\n iv_reason = '' ).\r\n cl_abap_unit_assert=>fail(\r\n msg = 'Expected ZCX_APPROVAL_ERROR when reason is empty' ).\r\n CATCH zcx_approval_error.\r\n \"Expected\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\n METHOD test_reject_already_rejected.\r\n TRY.\r\n mo_cut->reject(\r\n iv_order_id = '0000000003'\r\n iv_reason = 'Duplicate' ).\r\n cl_abap_unit_assert=>fail(\r\n msg = 'Expected ZCX_APPROVAL_ERROR for already-rejected order' ).\r\n CATCH zcx_approval_error.\r\n \"Expected\r\n ENDTRY.\r\n ENDMETHOD.\r\n\r\nENDCLASS.\r\n```\r\n\r\n---\r\n\r\n## Coverage Estimate\r\n\r\n| Method | Scenarios Covered | Coverage Estimate |\r\n|---------|-------------------|-------------------|\r\n| approve | 3 | ~85% |\r\n| reject | 3 | ~80% |\r\n\r\n**Overall Estimated Coverage:** ~82%\r\n\r\n---\r\n\r\n## Dependencies That Need Refactoring\r\n\r\n- METHOD get_order_status: directly reads from VBAK table — not mockable in\r\n current form. To fully isolate unit tests, extract DB access to a repository\r\n interface ZIF_ORDER_REPOSITORY and inject it. Otherwise these tests will hit\r\n the database (acceptable for integration tests but not pure unit tests).\r\n```\r\n\r\n---\r\n\r\n## Manifest Emission (CLI integration — v0.7)\r\n\r\nWhen invoked with `--from @<cca-assessment>` (batch mode across many\r\ntargets), emit a hidden HTML-comment manifest as the last block of the\r\nresponse so the CSPeach CLI can pick it up as a `test-coverage` envelope.\r\nSingle-target interactive invocations (one class, no `--from`) skip this\r\nblock — the manifest is for batch chain visibility.\r\n\r\nExactly one block per response, after all human output:\r\n\r\n```\r\n<!-- cspeach:test-coverage-manifest\r\nartefact: test-coverage\r\ntitle: <one-line title — e.g. \"ZDPR_PAYMENTS — test coverage\">\r\ndetail_path: .cspeach/tests/<slug>/project.json\r\nbaseline_path: <project-relative path to the upstream cca-assessment>\r\nbaseline_sha256: <hex of that file's canonicalized content>\r\ntransport: <transport-id or \"-\">\r\ntest_classes_generated: <n>\r\nmethods_added: <total methods across all generated classes>\r\ncoverage_pct_before: <int or \"-\" if SCMP unavailable>\r\ncoverage_pct_after: <int or \"-\" if SCMP unavailable>\r\nskipped: <n>\r\nfailed: <n>\r\ntests:\r\n item-001 | <targetObjectName> | <targetObjectType> | <testClassName> | <methodCount> | <status>\r\n item-002 | <targetObjectName> | <targetObjectType> | <testClassName> | <methodCount> | <status>\r\n ...\r\n-->\r\n```\r\n\r\nField rules:\r\n- `status` is one of: `generated` | `skipped` | `failed` | `pending`.\r\n- `methodCount` is integer; 0 if the class was scaffolded but no methods\r\n filled in (status would then be `pending`).\r\n- `coverage_pct_before` / `coverage_pct_after`: emit \"-\" if SCMP / coverage\r\n tool is unavailable on this system. The skill works without it.\r\n- `detail_path` and `baseline_path` are project-relative.\r\n\r\n## Post-Save Chain Prompt\r\n\r\nAfter the CLI prints `Saved: ...` for the test-coverage envelope, ask once:\r\n\r\n> \"Test classes generated. Run them now to establish the green baseline,\r\n> or chain to /abap-upgrade-fix / /abap-modernize to start the actual\r\n> fixes (the tests will then verify the changes don't break behaviour)?\"\r\n>\r\n> Options:\r\n> - Run the generated tests now via /abap-test --status to confirm baseline\r\n> - Chain to /abap-upgrade-fix --from @<cca-assessment> (mechanical fixes)\r\n> - Chain to /abap-modernize --from @<cca-assessment> (structural rework)\r\n> - End turn\r\n\r\nHint pattern (no `--from` injection from test-coverage, the next skill\r\ntakes the same cca-assessment that fed THIS run):\r\n\r\n> `→ Run \\`/abap-upgrade-fix --from @<cca-assessment>\\` for mechanical\r\n> ATC fixes, or \\`/abap-modernize --from @<cca-assessment>\\` for the\r\n> structural rework. The tests just generated will run as part of\r\n> those skills' verification.`\r\n",
219
226
  "sha256": "b935658026684ca99395accab74691a75246c8a2c5d21419c97595d6b78106af",
220
227
  "signature": "",
221
- "signedAt": "2026-09-17T20:19:05.729Z"
228
+ "signedAt": "2026-09-28T14:37:05.092Z"
222
229
  },
223
230
  "abap-transport": {
224
231
  "name": "abap-transport",
225
232
  "body": "---\r\nname: abap-transport\r\ndescription: >\r\n Transport management — create transport requests, validate object lists\r\n (syntax check + ATC + dependency scan), and release transports with\r\n mandatory preflight confirmation. All write operations require explicit\r\n user confirmation. Requires MCP connection.\r\nphase: SHIP\r\nrequires_mcp: required\r\nforge_rules: [5, 6, 7, 9, 10]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-transport\r\n\r\n## Purpose\r\n\r\nManage the full transport lifecycle: create transport requests, add objects,\r\nvalidate the transport content (syntax, ATC, dependencies), and release\r\ntransports after preflight sign-off. Every destructive action (release,\r\nobject deletion from transport) requires explicit confirmation.\r\n\r\nForge Rule 9: Transport isolation — each development session should use its\r\nown dedicated transport, not a shared catch-all request.\r\n\r\n## When to Use\r\n\r\n- Creating a new transport for a development session\r\n- Validating a transport before releasing to QA or production\r\n- Releasing a transport after all checks pass and preflight is complete\r\n- Checking what objects are in a transport request\r\n\r\nDo NOT use this skill to release a transport that has not completed a\r\npreflight check (`/abap-preflight`). Forge Rule 5 requires preflight before\r\nany transport release.\r\n\r\n## Operations\r\n\r\nThis skill supports three primary operations. Specify one when invoking:\r\n\r\n### Operation: create\r\n\r\nCreate a new transport request.\r\n\r\n**Inputs needed:**\r\n- Description / short text for the transport\r\n- Workbench or Customizing type\r\n- Target system (if different from system default)\r\n- Optional: add specific objects at creation time\r\n\r\n**Steps:**\r\n1. Call `sap_create_object` to create the transport via ADT API\r\n2. Display the created transport number\r\n3. Optionally add specified objects\r\n\r\n### Operation: validate\r\n\r\nValidate the content of an existing transport request.\r\n\r\n**Inputs needed:**\r\n- Transport request number (e.g. DEVK912345)\r\n\r\n**Validation steps (in order):**\r\n1. Retrieve object list from transport\r\n2. For each object: run `sap_syntax_check`\r\n3. For each ABAP object: run `sap_atc_run`\r\n4. Check for missing objects: are all referenced Z-objects in this or a\r\n predecessor transport?\r\n5. Produce a validation report\r\n\r\n### Operation: release\r\n\r\nRelease a transport to the next system in the landscape.\r\n\r\n**Inputs needed:**\r\n- Transport request number\r\n- Confirmation that `/abap-preflight` has been completed (required)\r\n- Explicit confirmation to release\r\n\r\n**Release steps:**\r\n1. Confirm preflight was completed — if no, run `/abap-preflight` first\r\n2. Re-run syntax check on all objects\r\n3. Check for open ATC priority-1 findings — block if found\r\n4. Ask: \"Transport {number} is ready to release. This will make it available\r\n for import into {target_system}. Confirm release? (yes/no)\"\r\n5. Only after \"yes\": release the transport\r\n6. Report release status\r\n\r\n## Required Behavior\r\n\r\n### For ALL operations:\r\n\r\n1. State which operation is being performed\r\n2. State the transport number being operated on\r\n3. For any write operation: show what will happen before doing it\r\n4. For release: require explicit \"yes\" confirmation\r\n5. After any write: verify the result and report it\r\n6. Log result summary at the end\r\n\r\n### Handling failures:\r\n\r\n- If syntax check fails: stop and report. Do not continue to ATC or release.\r\n- If ATC has priority-1 findings: stop and report. Do not release.\r\n- If the user tries to release without preflight: refuse and explain why\r\n (Forge Rule 5), offer to run `/abap-preflight` first.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Transport Operation — {operation}\r\n\r\n**Transport:** {transport_number}\r\n**System:** {system}\r\n**Operation:** {create | validate | release}\r\n\r\n---\r\n\r\n## Transport Details\r\n\r\n**Description:** {description}\r\n**Type:** {workbench | customizing}\r\n**Owner:** {owner}\r\n**Target:** {target_system}\r\n\r\n---\r\n\r\n## Object List\r\n\r\n| Object | Type | Status | Notes |\r\n|--------|------|--------|-------|\r\n| {object} | {type} | Active/Inactive | {notes} |\r\n\r\n---\r\n\r\n## Validation Results (validate / release operation)\r\n\r\n### Syntax Check\r\n| Object | Result | Details |\r\n|--------|--------|---------|\r\n| {object} | Passed / Failed | {details} |\r\n\r\n### ATC Results\r\n| Object | Priority-1 | Priority-2 | Priority-3 | Status |\r\n|--------|------------|------------|------------|--------|\r\n| {object} | {count} | {count} | {count} | Clean / Issues |\r\n\r\n### Dependency Check\r\n| Referenced Object | In Transport | Status |\r\n|-------------------|--------------|--------|\r\n| {object} | Yes / No | {ok | MISSING} |\r\n\r\n---\r\n\r\n## Validation Summary\r\n\r\n**Syntax:** {all_passed | {count} failed}\r\n**ATC:** {all_clean | {count} priority-1 findings}\r\n**Dependencies:** {all_resolved | {count} missing}\r\n\r\n**Release Eligible:** {Yes | No — {reason}}\r\n\r\n---\r\n\r\n## Release Confirmation (release operation only)\r\n\r\n⚠ You are about to release transport {transport_number} to {target_system}.\r\n\r\n- Objects in transport: {count}\r\n- Last syntax check: {timestamp} — {result}\r\n- Last ATC run: {timestamp} — {result}\r\n- Preflight completed: {Yes | Not confirmed}\r\n\r\nType \"yes\" to confirm release, or \"no\" to cancel.\r\n\r\n---\r\n\r\n## Operation Result\r\n\r\n**Status:** {Success | Failed | Partial}\r\n**Transport Number:** {transport_number}\r\n**Released At:** {timestamp}\r\n**Next Action:** Import into {target_system} via STMS or automatic import job\r\n```\r\n\r\n## Guardrails\r\n\r\n- NEVER release a transport without explicit user confirmation (\"yes\")\r\n- NEVER release a transport with open priority-1 ATC findings\r\n- NEVER release a transport if syntax check failed for any object\r\n- NEVER skip the preflight requirement for production releases\r\n- If MCP tools fail or return unexpected results, report the raw error — do\r\n not make assumptions about what happened\r\n- Forge Rule 9: Warn if the transport contains objects from multiple unrelated\r\n development areas (shared transport anti-pattern)\r\n- Forge Rule 10: After every successful release, report the result and confirm\r\n the transport number for tracking\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-transport\r\n\r\nOperation: validate and release\r\nTransport: DEVK912345\r\n\r\nThe transport contains the order approval email changes. Preflight was\r\ncompleted and approved by the release manager.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Transport Operation — validate and release\r\n\r\n**Transport:** DEVK912345\r\n**System:** DEV\r\n**Operation:** Validate then Release\r\n\r\n---\r\n\r\n## Transport Details\r\n\r\n**Description:** Order approval email notification - ZCL_ORDER_APPROVAL + workflow\r\n**Type:** Workbench\r\n**Owner:** LSIDDIQUE\r\n**Target:** QAS\r\n\r\n---\r\n\r\n## Object List\r\n\r\n| Object | Type | Status | Notes |\r\n|-------------------------|---------------|--------|----------------------------------|\r\n| ZCL_ORDER_APPROVAL | CLAS (Class) | Active | Modified — email method added |\r\n| ZCL_ORDER_APPROVAL_TEST | CLAS (Class) | Active | New — unit test class |\r\n| ZORDER_WORKFLOW | SWFD (Workflow)| Active | Modified — task exit updated |\r\n\r\n---\r\n\r\n## Validation Results\r\n\r\n### Syntax Check\r\n| Object | Result | Details |\r\n|-------------------------|--------|-------------------|\r\n| ZCL_ORDER_APPROVAL | Passed | — |\r\n| ZCL_ORDER_APPROVAL_TEST | Passed | — |\r\n| ZORDER_WORKFLOW | Passed | — |\r\n\r\n### ATC Results\r\n| Object | Priority-1 | Priority-2 | Priority-3 | Status |\r\n|-------------------------|------------|------------|------------|--------|\r\n| ZCL_ORDER_APPROVAL | 0 | 0 | 0 | Clean |\r\n| ZCL_ORDER_APPROVAL_TEST | 0 | 0 | 0 | Clean |\r\n| ZORDER_WORKFLOW | N/A | N/A | N/A | Not ABAP |\r\n\r\n### Dependency Check\r\n| Referenced Object | In Transport | Status |\r\n|----------------------------|--------------|--------|\r\n| ZIF_ORDER_APPROVAL | No | OK — interface is in predecessor DEVK912300 |\r\n| ZCX_APPROVAL_ERROR | No | OK — in predecessor DEVK912301 |\r\n| CL_BCS (SAP standard) | N/A | OK — standard object, no transport needed |\r\n\r\n---\r\n\r\n## Validation Summary\r\n\r\n**Syntax:** All passed\r\n**ATC:** All clean (0 priority-1, 0 priority-2, 0 priority-3)\r\n**Dependencies:** All resolved\r\n\r\n**Release Eligible:** Yes\r\n\r\n---\r\n\r\n## Release Confirmation\r\n\r\nYou are about to release transport DEVK912345 to QAS.\r\n\r\n- Objects in transport: 3\r\n- Last syntax check: 2026-03-29 14:45:00 — All passed\r\n- Last ATC run: 2026-03-29 14:45:30 — All clean\r\n- Preflight completed: Yes (confirmed by user)\r\n\r\nConfirm release? (yes/no)\r\n\r\n*(User responds: yes)*\r\n\r\n---\r\n\r\n## Operation Result\r\n\r\n**Status:** Success\r\n**Transport Number:** DEVK912345\r\n**Released At:** 2026-03-29 14:46:02\r\n**Next Action:** Import into QAS via STMS or automatic import job.\r\nNote: Activate ZORDER_WORKFLOW manually in QAS after import (see preflight checklist).\r\n```\r\n",
226
233
  "sha256": "caf90742d93f4ddb3c22fdfede672062d348770fb6394158e54ef710033e2fa2",
227
234
  "signature": "",
228
- "signedAt": "2026-09-17T20:19:05.774Z"
235
+ "signedAt": "2026-09-28T14:37:05.124Z"
229
236
  },
230
237
  "abap-transport-analysis": {
231
238
  "name": "abap-transport-analysis",
232
- "body": "---\nname: abap-transport-analysis\ndescription: >\n Pre-release transport analysis. Lists all objects in a transport, runs ATC\n on each, checks cross-dependencies, identifies conflicts with other open\n transports, and produces a release readiness report. Use before releasing\n any transport to QA or production.\nphase: ANALYZE\nrequires_mcp: required\nversion: \"1.0\"\n---\n\n# abap-transport-analysis\n\n## Purpose\n\nAnswer the question: \"Is this transport safe to release?\"\n\nBefore releasing a transport, this skill examines every object in it: runs\nquality checks, traces dependencies between objects, checks for conflicts\nwith other open transports, and produces a go/no-go recommendation.\n\nUse cases:\n- **Pre-release gate** — mandatory check before releasing to QA/production\n- **Transport review** — reviewer needs to understand what's in a transport\n- **Conflict detection** — two developers modified the same object in different transports\n- **Quality gate** — all objects must pass ATC before release\n\n## When to Use\n\n- Use when you have a TRANSPORT NUMBER and want a read-only release-safety scan — objects, ATC per object, conflicts, go/no-go.\n- NOT for the broader release checklist on a change set (no TR# needed) — use `/abap-preflight`; NOT to create or release transports — use `/abap-transport`.\n\n## Required Tools\n\n| Tool | Purpose |\n|------|---------|\n| `sap_transport` (list) | List open transports |\n| `sap_sql_query` | Query E071 (objects in transport), E070 (transport header) |\n| `sap_get_source` | Read source of objects in transport |\n| `sap_atc_run` | Run quality checks on each object |\n| `sap_usage_references` | Check dependencies between objects |\n| `sap_inactive_objects` | Check if any objects are still inactive |\n| `sap_syntax_check` | Verify syntax for each object |\n\n## Inputs\n\nRequired:\n- Transport number (e.g. S4HK903334)\n\nOptional:\n- Depth: `quick` (object list + ATC) or `deep` (+ dependencies + conflict check). Default: `quick`\n- ATC variant: which variant to use for quality checks. Default: `DEFAULT`\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Required Behavior\n\n### Step 1 — Read Transport Contents\n\n```sql\nSELECT a~pgmid, a~object, a~obj_name, a~objfunc,\n b~as4text, b~trstatus\n FROM e071 AS a\n INNER JOIN e070 AS b ON a~trkorr = b~trkorr\n WHERE a~trkorr = '{TRANSPORT}'\n```\n\nAlso get tasks under the transport:\n```sql\nSELECT trkorr, as4text, as4user\n FROM e070\n WHERE strkorr = '{TRANSPORT}'\n```\n\nList all objects:\n\n```\nTransport: {NUMBER} — {DESCRIPTION}\nOwner: {USER} | Status: {modifiable/released} | Created: {DATE}\nTasks: {count}\n\nObjects ({count}):\n| # | Type | Name | Operation |\n|---|------|------|-----------|\n| 1 | PROG | ZLEGACY_FI_REPORT | Modify |\n| 2 | CLAS | ZCL_ORDER_SERVICE | Insert |\n| 3 | DDLS | ZI_SALESORDER | Modify |\n```\n\n### Step 2 — Check for Inactive Objects\n\n```\nsap_inactive_objects\n```\n\nCross-reference with objects in the transport. If any transport object\nis still inactive: flag as **blocker**.\n\n### Step 3 — Run ATC on Each Object\n\nFor each object in the transport:\n```\nsap_atc_run (objectType, objectName, variant)\n```\n\nAggregate results:\n- Total findings across all objects\n- Priority 1 (errors) — blockers\n- Priority 2 (warnings) — review\n- Priority 3 (info) — informational\n\n### Step 4 — Check Dependencies (deep mode)\n\nFor each object, check if it depends on objects NOT in this transport:\n\n```\nsap_usage_references (objectType, objectName)\n```\n\nIf object A in the transport calls object B which is also modified but\nin a DIFFERENT transport: flag as **dependency conflict**. Both transports\nneed to be released together or in the right order.\n\n### Step 5 — Check for Transport Conflicts\n\n```sql\nSELECT a~trkorr, a~object, a~obj_name, b~as4user\n FROM e071 AS a\n INNER JOIN e070 AS b ON a~trkorr = b~trkorr\n WHERE a~obj_name IN ({OBJECTS_IN_TRANSPORT})\n AND a~trkorr <> '{TRANSPORT}'\n AND ( b~trstatus = 'D' OR b~trstatus = 'L' )\n```\n\nIf the same object exists in another modifiable transport: flag as\n**conflict**. Two developers may have different versions of the same object.\n\n### Step 6 — Produce Release Readiness Report\n\n```\n## Transport Analysis: {TRANSPORT}\n### {DESCRIPTION}\n\nOwner: {USER} | Objects: {COUNT} | Tasks: {TASK_COUNT}\n\n### Release Decision: {GO / NO-GO / REVIEW}\n\n### Blockers ({count})\n{List of issues that must be resolved before release}\n- ZLEGACY_FI_REPORT is inactive — activate before release\n- ZCL_ORDER_SERVICE has 2 Priority 1 ATC findings\n\n### Warnings ({count})\n{Issues that should be reviewed but don't block release}\n- ZLEGACY_FI_REPORT also in transport S4HK903328 (owner: JSMITH)\n- 14 Priority 2 ATC findings across 3 objects\n\n### Object Quality Summary\n| Object | Type | ATC P1 | ATC P2 | ATC P3 | Syntax | Active |\n|--------|------|--------|--------|--------|--------|--------|\n| ZLEGACY_FI_REPORT | PROG | 0 | 3 | 8 | Clean | Yes |\n| ZCL_ORDER_SERVICE | CLAS | 2 | 1 | 0 | Clean | No |\n\n### Dependencies\n{Objects in this transport that depend on each other — release order matters}\nZI_SALESORDER must be activated before ZC_SALESORDER (CDS dependency)\n\n### Conflicts with Other Transports\n{Same object in multiple open transports}\n| Object | This Transport | Other Transport | Other Owner |\n|--------|---------------|-----------------|-------------|\n| ZLEGACY_FI_REPORT | S4HK903334 | S4HK903328 | JSMITH |\n\n### Recommendation\n{Specific actions needed before release}\n1. Activate ZCL_ORDER_SERVICE\n2. Fix 2 Priority 1 findings in ZCL_ORDER_SERVICE\n3. Resolve conflict: ZLEGACY_FI_REPORT is in 2 transports\n```\n\n### Release Decision Logic\n\n| Condition | Decision |\n|-----------|----------|\n| Any inactive objects | NO-GO |\n| Any Priority 1 ATC findings | NO-GO |\n| Object conflicts with other transports | REVIEW |\n| Priority 2 findings > 10 | REVIEW |\n| All clean, no conflicts | GO |\n\n## Guardrails\n\n- Read-only skill. Never modify, release, or delete transports.\n- If the transport is already released (trstatus = 'R'): report its\n contents but note \"already released — analysis is historical.\"\n- If ATC runs fail on some objects (authorization, object type not\n supported): note which objects couldn't be checked and downgrade\n the decision to REVIEW.\n- Never recommend releasing a transport with inactive objects.\n- For large transports (50+ objects): run ATC on the first 20, report\n count for the rest, and recommend running the full check in ATC directly.\n",
233
- "sha256": "bb6d758f7277ec1eca7d32dbbf43982276b9a65e7f908d6803f8b6aff23493ef",
239
+ "body": "---\r\nname: abap-transport-analysis\r\ndescription: >\r\n Pre-release transport analysis. Lists all objects in a transport, runs ATC\r\n on each, checks cross-dependencies, identifies conflicts with other open\r\n transports, and produces a release readiness report. Use before releasing\r\n any transport to QA or production.\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-transport-analysis\r\n\r\n## Purpose\r\n\r\nAnswer the question: \"Is this transport safe to release?\"\r\n\r\nBefore releasing a transport, this skill examines every object in it: runs\r\nquality checks, traces dependencies between objects, checks for conflicts\r\nwith other open transports, and produces a go/no-go recommendation.\r\n\r\nUse cases:\r\n- **Pre-release gate** — mandatory check before releasing to QA/production\r\n- **Transport review** — reviewer needs to understand what's in a transport\r\n- **Conflict detection** — two developers modified the same object in different transports\r\n- **Quality gate** — all objects must pass ATC before release\r\n\r\n## When to Use\r\n\r\n- Use when you have a TRANSPORT NUMBER and want a read-only release-safety scan — objects, ATC per object, conflicts, go/no-go.\r\n- NOT for the broader release checklist on a change set (no TR# needed) — use `/abap-preflight`; NOT to create or release transports — use `/abap-transport`.\r\n\r\n## Required Tools\r\n\r\n| Tool | Purpose |\r\n|------|---------|\r\n| `sap_transport` (list) | List open transports |\r\n| `sap_sql_query` | Query E071 (objects in transport), E070 (transport header) |\r\n| `sap_get_source` | Read source of objects in transport |\r\n| `sap_atc_run` | Run quality checks on each object |\r\n| `sap_usage_references` | Check dependencies between objects |\r\n| `sap_inactive_objects` | Check if any objects are still inactive |\r\n| `sap_syntax_check` | Verify syntax for each object |\r\n\r\n## Inputs\r\n\r\nRequired:\r\n- Transport number (e.g. S4HK903334)\r\n\r\nOptional:\r\n- Depth: `quick` (object list + ATC) or `deep` (+ dependencies + conflict check). Default: `quick`\r\n- ATC variant: which variant to use for quality checks. Default: `DEFAULT`\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Read Transport Contents\r\n\r\n```sql\r\nSELECT a~pgmid, a~object, a~obj_name, a~objfunc,\r\n b~as4text, b~trstatus\r\n FROM e071 AS a\r\n INNER JOIN e070 AS b ON a~trkorr = b~trkorr\r\n WHERE a~trkorr = '{TRANSPORT}'\r\n```\r\n\r\nAlso get tasks under the transport:\r\n```sql\r\nSELECT trkorr, as4text, as4user\r\n FROM e070\r\n WHERE strkorr = '{TRANSPORT}'\r\n```\r\n\r\nList all objects:\r\n\r\n```\r\nTransport: {NUMBER} — {DESCRIPTION}\r\nOwner: {USER} | Status: {modifiable/released} | Created: {DATE}\r\nTasks: {count}\r\n\r\nObjects ({count}):\r\n| # | Type | Name | Operation |\r\n|---|------|------|-----------|\r\n| 1 | PROG | ZLEGACY_FI_REPORT | Modify |\r\n| 2 | CLAS | ZCL_ORDER_SERVICE | Insert |\r\n| 3 | DDLS | ZI_SALESORDER | Modify |\r\n```\r\n\r\n### Step 2 — Check for Inactive Objects\r\n\r\n```\r\nsap_inactive_objects\r\n```\r\n\r\nCross-reference with objects in the transport. If any transport object\r\nis still inactive: flag as **blocker**.\r\n\r\n### Step 3 — Run ATC on Each Object\r\n\r\nFor each object in the transport:\r\n```\r\nsap_atc_run (objectType, objectName, variant)\r\n```\r\n\r\nAggregate results:\r\n- Total findings across all objects\r\n- Priority 1 (errors) — blockers\r\n- Priority 2 (warnings) — review\r\n- Priority 3 (info) — informational\r\n\r\n### Step 4 — Check Dependencies (deep mode)\r\n\r\nFor each object, check if it depends on objects NOT in this transport:\r\n\r\n```\r\nsap_usage_references (objectType, objectName)\r\n```\r\n\r\nIf object A in the transport calls object B which is also modified but\r\nin a DIFFERENT transport: flag as **dependency conflict**. Both transports\r\nneed to be released together or in the right order.\r\n\r\n### Step 5 — Check for Transport Conflicts\r\n\r\n```sql\r\nSELECT a~trkorr, a~object, a~obj_name, b~as4user\r\n FROM e071 AS a\r\n INNER JOIN e070 AS b ON a~trkorr = b~trkorr\r\n WHERE a~obj_name IN ({OBJECTS_IN_TRANSPORT})\r\n AND a~trkorr <> '{TRANSPORT}'\r\n AND ( b~trstatus = 'D' OR b~trstatus = 'L' )\r\n```\r\n\r\nIf the same object exists in another modifiable transport: flag as\r\n**conflict**. Two developers may have different versions of the same object.\r\n\r\n### Step 6 — Produce Release Readiness Report\r\n\r\n```\r\n## Transport Analysis: {TRANSPORT}\r\n### {DESCRIPTION}\r\n\r\nOwner: {USER} | Objects: {COUNT} | Tasks: {TASK_COUNT}\r\n\r\n### Release Decision: {GO / NO-GO / REVIEW}\r\n\r\n### Blockers ({count})\r\n{List of issues that must be resolved before release}\r\n- ZLEGACY_FI_REPORT is inactive — activate before release\r\n- ZCL_ORDER_SERVICE has 2 Priority 1 ATC findings\r\n\r\n### Warnings ({count})\r\n{Issues that should be reviewed but don't block release}\r\n- ZLEGACY_FI_REPORT also in transport S4HK903328 (owner: JSMITH)\r\n- 14 Priority 2 ATC findings across 3 objects\r\n\r\n### Object Quality Summary\r\n| Object | Type | ATC P1 | ATC P2 | ATC P3 | Syntax | Active |\r\n|--------|------|--------|--------|--------|--------|--------|\r\n| ZLEGACY_FI_REPORT | PROG | 0 | 3 | 8 | Clean | Yes |\r\n| ZCL_ORDER_SERVICE | CLAS | 2 | 1 | 0 | Clean | No |\r\n\r\n### Dependencies\r\n{Objects in this transport that depend on each other — release order matters}\r\nZI_SALESORDER must be activated before ZC_SALESORDER (CDS dependency)\r\n\r\n### Conflicts with Other Transports\r\n{Same object in multiple open transports}\r\n| Object | This Transport | Other Transport | Other Owner |\r\n|--------|---------------|-----------------|-------------|\r\n| ZLEGACY_FI_REPORT | S4HK903334 | S4HK903328 | JSMITH |\r\n\r\n### Recommendation\r\n{Specific actions needed before release}\r\n1. Activate ZCL_ORDER_SERVICE\r\n2. Fix 2 Priority 1 findings in ZCL_ORDER_SERVICE\r\n3. Resolve conflict: ZLEGACY_FI_REPORT is in 2 transports\r\n```\r\n\r\n### Release Decision Logic\r\n\r\n| Condition | Decision |\r\n|-----------|----------|\r\n| Any inactive objects | NO-GO |\r\n| Any Priority 1 ATC findings | NO-GO |\r\n| Object conflicts with other transports | REVIEW |\r\n| Priority 2 findings > 10 | REVIEW |\r\n| All clean, no conflicts | GO |\r\n\r\n## Guardrails\r\n\r\n- Read-only skill. Never modify, release, or delete transports.\r\n- If the transport is already released (trstatus = 'R'): report its\r\n contents but note \"already released — analysis is historical.\"\r\n- If ATC runs fail on some objects (authorization, object type not\r\n supported): note which objects couldn't be checked and downgrade\r\n the decision to REVIEW.\r\n- Never recommend releasing a transport with inactive objects.\r\n- For large transports (50+ objects): run ATC on the first 20, report\r\n count for the rest, and recommend running the full check in ATC directly.\r\n",
240
+ "sha256": "b4f7b0eb9365021a6b4c714763afe32d7acceff0f4d0fde236eed54e8189021b",
234
241
  "signature": "",
235
- "signedAt": "2026-09-17T20:19:05.819Z"
242
+ "signedAt": "2026-09-28T14:37:05.154Z"
236
243
  },
237
244
  "abap-upgrade-fix": {
238
245
  "name": "abap-upgrade-fix",
239
246
  "body": "---\r\nname: abap-upgrade-fix\r\ndescription: >\r\n Interactive AI-guided upgrade remediation. Fixes ATC findings one object at a\r\n time with developer approval, successor API lookup via ARS_W_API_SCCSSR,\r\n syntax verification before write, snapshot rollback on failure, and persistent\r\n progress tracking. Chains from /abap-upgrade-scan output. Never auto-fixes\r\n without explicit developer approval per object.\r\nphase: BUILD\r\nrequires_mcp: required\r\nforge_rules: [7, 10]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-upgrade-fix\r\n\r\n## Purpose\r\n\r\nProcess an S/4HANA upgrade remediation work list object by object. For each\r\nobject the skill reads the source, shows findings from the scan baseline, looks\r\nup successors in SAP's ARS_W_API_SCCSSR table (falling back to Claude ABAP\r\nknowledge), proposes a combined diff, waits for developer approval, syntax-checks\r\nthe new source before writing, snapshots for rollback, writes, and verifies\r\nactivation. Progress is saved to the state file so interrupted sessions resume.\r\n\r\nForge Rule 7: snapshot before every write. Forge Rule 10: verify activation\r\nafter every write. Both rules are hard stops — if either tool call fails, the\r\nwrite does not proceed.\r\n\r\nSkill chain: `/abap-upgrade-scan` → **`/abap-upgrade-fix`** → `/abap-upgrade-verify`\r\n\r\n> Note: the review-decisions honoring (Step 1b), the up-front severity policy\r\n> (Step 1c), and pseudo-comment suppression (Step 3g) ship LIVE only after this\r\n> SKILL.md is reseeded to the cspeach-proxy backend — disk edits do not reach the\r\n> running CLI until the seed runs.\r\n>\r\n> **Decision precedence (always):** an explicit per-finding decision from the\r\n> source envelope (Step 1b: auto-fix / suppress / manual / keep) OVERRIDES the\r\n> up-front default severity policy (Step 1c). The default policy applies only to\r\n> findings still `open`, and it NEVER suppresses — it fixes the auto-fixable and\r\n> leaves the rest untouched.\r\n\r\n## When to Use\r\n\r\n- After `/abap-upgrade-scan` has produced a baseline file in `.cspeach/upgrades/`\r\n- When picking up an interrupted fix session (skips already-fixed objects)\r\n- For targeted single-object fixing when a developer provides an explicit list\r\n\r\nDo NOT use for functional redesign findings (output management, credit\r\nmanagement, business partner conversion) — the skill identifies and marks these\r\nas manual review only.\r\n\r\n## Inputs Expected\r\n\r\nRequired (one of):\r\n- Scan baseline path: `.cspeach/upgrades/{package}_{variant}_{date}.json`\r\n produced by `/abap-upgrade-scan`. This is the canonical chain.\r\n- A cca-assessment envelope (v0.7) via `--from @<file>`. /abap-cca already\r\n ran ATC as part of its ANALYZE phase; the CLI adapter reads the assessment's\r\n `detailPath` and extracts the same shape `/abap-upgrade-scan` would have\r\n produced. No re-scan needed.\r\n- An explicit list of object names/types.\r\n\r\nPlus:\r\n- ATC variant used during scan (e.g. `S4HANA_READINESS_2023`)\r\n\r\nWill be asked if not provided:\r\n- Transport request number — mandatory for any package other than `$TMP`\r\n- Whether to process all objects or a specific subset\r\n\r\nOptional: `--batch N` to pause after N objects; object name to start from\r\nwhen resuming.\r\n\r\n### When the input is a cca-assessment (v0.7 chain)\r\n\r\nCCA's ANALYZE phase already produced ATC findings + Clean Core analysis. The\r\nadapter pulls the ATC slice — every object classified `fix` whose findings\r\nare ATC syntax issues (not Clean Core API violations, which are the\r\n`/abap-modernize` workstream). The fix loop runs identically; only the\r\ninput format differs.\r\n\r\nIf the cca-assessment contains objects classified `redesign` or `fix` whose\r\nfindings are Clean Core API violations (direct BSEG/BKPF reads, unreleased\r\nBAPIs), the adapter SKIPS them and prints a hint suggesting `/abap-modernize`\r\nfor those. The skill does not attempt structural rewrites — that's\r\nmodernize's job.\r\n\r\nRead the cca-assessment's detail file at `content.detailPath` (a\r\nproject-relative path like `.cspeach/cca/projects/<slug>/project.json`; for\r\nolder projects this may point at the legacy `.abapforge/cca/projects/<slug>/`\r\nlocation — read there as a fallback if the `.cspeach/` path is absent).\r\nThat file's `objects.<name>.atcFindings[]` is the per-object findings list\r\nready to consume.\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Load Work List\r\n\r\n**When invoked via `--from @<envelope>`:** the CLI injects a `Source detail file`\r\nblock giving the EXACT path of the baseline detail JSON (it lives under\r\n`.cspeach/<domain>/`, e.g. `.cspeach/upgrades/{package}_{variant}_{date}.json`).\r\n**`file_read` that exact path directly.** Do NOT `glob` for it (glob skips\r\n`.cspeach/`), and do NOT try to read the `.cspeach.json` envelope (its path is\r\nprotected and will fail). The injected detail file is the authoritative work list.\r\n\r\nOtherwise (no `--from`), read the baseline JSON from `.cspeach/upgrades/`; if\r\nabsent, fall back to the legacy `.abapforge/upgrades/` location (older projects).\r\nEither way, extract `scope.package`, `variant`, `findings` (per-object finding\r\nlist), and `fixSession.fixed` (already done — skip these). If the file is missing,\r\nask for the path or an explicit object list. Do NOT invent findings.\r\n\r\nNote: one underlying SELECT feeding multiple `WRITE`-in-`LOOP` statements yields\r\nONE finding per WRITE (the ATC `LOOP_WRITE` check) — adding `ORDER BY` to that one\r\nSELECT resolves them all. Do not be confused by N findings over a single SELECT.\r\n\r\nReport: \"Resuming fix session. {N} fixed. {M} remaining.\" if prior progress exists.\r\n\r\n### Step 1b — Honor Review Decisions From the Source (if present)\r\n\r\nWhen the work list arrives via `--from @<envelope>` (an `upgrade-scan` or\r\n`cca-assessment` source), the CLI injects a block into the prompt that carries\r\nthe per-finding review decisions the user made while triaging in the viewer:\r\n\r\n```\r\nReview decisions from the source envelope (honor these):\r\n Applying your review: 2 auto-fix, 1 suppress, 1 manual, 1 still open.\r\n\r\n finding-001 ZFI_DUNNING_SELECT line 52 SELECT_WO_ORDER_BY -> auto-fix\r\n finding-007 ZLEGACY_SD_PROCESS line 38 DB_OP_KONV_2220005 -> suppress\r\n finding-012 ZFIN_DOC_JOURNAL line 133 DB_OP_BSEG_2431747 -> manual\r\n finding-020 ZFI_REPORT line 9 NO_TEXT_1700 -> open\r\n```\r\n\r\nFor a `cca-assessment` source the decision token is the object classification\r\n(`fix` / `redesign` / `retire` / `keep`) instead of the upgrade triage status.\r\n\r\nWhen this block is present, these decisions DRIVE the fix loop — do NOT re-ask\r\nobject-by-object for findings that already carry a decision. Map each decision:\r\n\r\n- `auto-fix` (or cca `fix`) -> propose + apply the fix. Still under the full\r\n Step 3h write-safety: snapshot -> syntax-check -> write -> activate -> verify.\r\n Do not skip any safety step just because the user pre-approved the intent.\r\n- `suppress` -> suppress that finding using the correct mechanism in Step 3g\r\n (pragma OR pseudo-comment). Do not re-prompt.\r\n- `manual` (or cca `redesign` / `retire`) -> manual review: explain the finding\r\n and why it is not auto-fixed; make NO code change. (cca `retire` means the\r\n object is slated for retirement — flag it, do not edit.)\r\n- `keep` (cca only) -> leave the object as-is; no change, no suppression.\r\n- `open` -> no decision yet; fall back to the existing interactive ask in\r\n Step 3f for these findings ONLY.\r\n\r\nPrint a one-line summary before processing:\r\n\r\n```\r\nApplying your review: N auto-fix, M suppress, K manual, J still open.\r\n```\r\n\r\nAll write-safety rules (Forge Rule 7 snapshot, Forge Rule 10 verify) and the\r\nmanifest-emission rules in the Manifest section stay fully intact — the review\r\ndecisions change WHICH action each finding gets, never the safety around a write.\r\n\r\nIf no review-decisions block is present (a plain baseline path, or a fully\r\nuntriaged source where every finding is still `open`), proceed exactly as before\r\nwith the interactive per-object loop.\r\n\r\n### Step 1c — Up-Front Severity Policy (run after loading findings + decisions)\r\n\r\nDo this immediately after Step 1 (work list) and Step 1b (decisions block) and\r\nBEFORE the per-object loop. The goal: do NOT prompt object-by-object for a\r\ntriaged run. Present ONE up-front policy, get ONE confirm, then run straight\r\nthrough.\r\n\r\n**Precedence is fixed: explicit viewer decision (Step 1b) > up-front default\r\npolicy (this step).** Never let the default policy override an explicit\r\ndecision.\r\n\r\n**1. Apply the explicit decisions (Step 1b) verbatim:**\r\n\r\n- `auto-fix` (cca `fix`) -> FIX (full Step 3h write-safety).\r\n- `suppress` -> SUPPRESS via the correct mechanism in Step 3g.\r\n- `manual` (cca `redesign` / `retire`) -> MANUAL REVIEW: explain, no code change.\r\n- `keep` (cca only) -> leave as-is; no change, no suppression.\r\n\r\n**2. For findings still `open` (no explicit decision), apply the DEFAULT POLICY\r\nby severity:**\r\n\r\n| Severity | Default action |\r\n|---|---|\r\n| Error (E) | FIX. If no safe fix exists -> MANUAL REVIEW (or formal ATC exemption). |\r\n| Warning (W) | Propose a fix if one is safe; otherwise LEAVE untouched (no change). |\r\n| Info (I) | FIX the auto-fixable ones; LEAVE the rest untouched (no change). |\r\n\r\n**The default policy NEVER suppresses.** Suppression happens ONLY on an explicit\r\n`suppress` decision from Step 1b. \"Leave\" means make no code change AND insert no\r\nsuppression token — it is distinct from \"suppress\". A left finding still shows up\r\nin the next ATC run; a suppressed finding does not. The default must never hide a\r\nfinding behind a pragma or pseudo-comment.\r\n\r\n**3. Print a transparent summary BEFORE doing anything, then ONE confirm:**\r\n\r\n```\r\nUp-front policy for this run (precedence: explicit decision > default policy)\r\n\r\nExplicit (from envelope): N suppress · M manual · K fix\r\nOpen -> default policy: fix X auto-fixable Info · Y Errors to fix/manual (nothing suppressed by default)\r\n\r\nProceed? [y / adjust]\r\n```\r\n\r\n- `y` -> run straight through using the policy above. Do NOT re-prompt per object\r\n for findings that already have an action (explicit or defaulted). Keep stopping\r\n ONLY where a fix genuinely needs human judgment (e.g. an ambiguous successor, a\r\n field that does not verify, a diff the model is not confident in).\r\n- `adjust` -> let the developer change the per-severity defaults ONCE (e.g. \"leave\r\n all Warnings\", \"fix Info too\"), re-print the summary, and confirm again. Explicit\r\n decisions are NOT adjustable here — they always win.\r\n\r\nAfter the confirm, run without per-object nagging, but keep ALL Step 3h\r\nwrite-safety on every write: snapshot -> syntax-check -> write -> activate ->\r\nverify -> rollback-on-failure. The up-front policy changes only WHICH action each\r\nfinding gets and removes the per-object prompt; it never weakens the safety\r\naround a write.\r\n\r\n**Fallback:** if there is no `--from` source and no decisions block (a plain\r\nbaseline path, or a fully untriaged scan), skip this up-front policy and use the\r\nexisting per-object interactive loop (Step 3f) as before. The per-object path\r\nstays fully available.\r\n\r\n### Step 2 — Transport Gate\r\n\r\nIf `scope.package` is not `$TMP`, ask for transport number before processing\r\nany object. Record it in the state file. Do NOT write without it.\r\n\r\n### Step 3 — Per-Object Fix Loop\r\n\r\nRepeat for each unfixed object:\r\n\r\n**3a. Progress header:**\r\n```\r\nObject {N}/{TOTAL}: {OBJECT_NAME} ({OBJECT_TYPE})\r\nProgress: {done} fixed, {skipped} skipped, {remaining} remaining\r\n```\r\n\r\n**3b. Existence / lock check:**\r\nCall `sap_get_source`. If 404: mark `status: deleted`, skip. If locked: mark\r\n`status: locked` with user name, skip. Do not prompt for approval on either.\r\n\r\n**3c. Show findings table:**\r\n\r\n| # | Line | SAP Note | Message | Category |\r\n|---|------|----------|---------|----------|\r\n| 1 | 47 | 2431747 | Direct access to BSEG not permitted | Table Replacement |\r\n| 2 | 203 | 2228611 | CALL FUNCTION 'NAST_BELEG_PRINT' | Manual Review |\r\n\r\n**3d. Successor lookup — per finding (non-manual-review only):**\r\n\r\nPRIMARY — query SAP system table:\r\n```sql\r\nSELECT * FROM ars_w_api_sccssr WHERE subobjectname = '<deprecated_name>'\r\n```\r\nCall `sap_sql_query`. If a row is returned: confidence = **HIGH (SAP successor table)**.\r\n\r\nFALLBACK — if query returns zero rows or fails (ECC system, auth gap): use\r\nClaude ABAP knowledge + `sap_search_object` to find the replacement. Label\r\n**MEDIUM (AI-suggested — verify manually)** or **LOW (no successor — review SAP\r\nNote)** as appropriate. Log \"ARS table unavailable — lower confidence.\"\r\n\r\n**Manual review findings (do not fix, explain and move on):**\r\nOutput management (NAST/NACE/RV_MESSAGES_*), credit management (RVKRED*),\r\nbusiness partner conversion (KNA1/LFA1 direct writes, BUPA*), material ledger\r\n(CKMLCR, CO-PC-ACT tables), SD status schema (VBUK/VBUP field direct writes).\r\nShow: finding, reason, SAP Note link, \"This finding will NOT be auto-fixed.\"\r\n\r\n**3e. Field verification — BEFORE proposing any code:**\r\n\r\nWhen replacing deprecated tables (BSEG→ACDOCA, VBUK→VBAK, VBUP→VBAP,\r\nKONV→PRCD_ELEMENTS), you MUST verify that every field name you plan to use\r\nactually exists on the target table in THIS system. Do NOT assume field names\r\nfrom documentation — S/4 versions differ.\r\n\r\n```sql\r\nSELECT fieldname FROM dd03l\r\n WHERE tabname = '<TARGET_TABLE>'\r\n AND as4local = 'A'\r\n AND fieldname LIKE '<PATTERN>'\r\n ORDER BY fieldname\r\n```\r\n\r\nCall `sap_sql_query` for each target table you plan to reference. If a field\r\ndoes not exist: find the correct name from the query results, or mark the\r\nfinding as manual review. Never write code referencing a field you have not\r\nconfirmed exists.\r\n\r\nThis is especially critical for:\r\n- VBAK status fields (replacing VBUK) — check which `%STK` fields exist\r\n- VBAP status fields (replacing VBUP) — check which `%STA` fields exist\r\n- ACDOCA field names (RBUKRS not BUKRS, DOCLN not BUZEI, RLDNR for ledger)\r\n- PRCD_ELEMENTS field names (may differ from KONV)\r\n\r\n**3f. Propose code changes:**\r\nShow a diff block per auto-fixable finding — finding number, line, SAP Note,\r\nconfidence level. Include the field verification results:\r\n\r\n```\r\nField check: VBAK — confirmed fields: GBSTK, ABSTK, BESTK, DCSTK, LFSTK, WBSTK\r\n```\r\n\r\nAfter all findings:\r\n\r\n```\r\nFindings with proposed fixes: {N_AUTO} | Manual review: {N_MANUAL} | Suppressible: {N_SUPPRESS}\r\n\r\nOptions: yes / select (e.g. 1,3) / no / skip / suppress (e.g. suppress 4,5,6)\r\n```\r\n\r\n**3g. Suppress findings (ONLY on an explicit `suppress` decision):**\r\n\r\nSuppression happens ONLY when a finding carries an explicit `suppress` decision —\r\nfrom the Step 1b review-decisions block, or from the developer choosing `suppress`\r\nin the interactive prompt. The default severity policy (Step 1c) NEVER suppresses;\r\n\"leave\" is not \"suppress\". Do not insert a suppression token on a finding that was\r\nmerely left untouched by the default.\r\n\r\nWhen suppressing, pick the CORRECT suppression mechanism per finding. ABAP has TWO\r\ndistinct in-source suppression forms — they are not interchangeable, and some\r\nchecks accept only one:\r\n\r\n- **Pragma** `##NAME` — a token placed INSIDE the statement, immediately before\r\n the terminating `.` or `,`. This is the form SLIN/extended-program-check\r\n findings use.\r\n- **Pseudo-comment** `\"#EC <CHECK_ID>` — a TRAILING inline comment at the END of\r\n the statement/line the finding is on. This is the classic ATC /\r\n Code-Inspector form (the \"suppressed keyword in a comment\").\r\n\r\nDecide which one applies, in this order:\r\n\r\n**1. Pragma path — SLIN_DESC lookup.** The ATC `messageId` maps directly to\r\n`SLIN_DESC.CODE_NR`:\r\n```sql\r\nSELECT DISTINCT pragma FROM slin_desc WHERE code_nr = '<messageId>'\r\n```\r\nCall `sap_sql_query`. If a pragma is returned (e.g. `NO_TEXT`, `FM_SUBRC_OK`,\r\n`NEEDED`), use the pragma form.\r\n\r\n**Pragma insertion rule:** Add `##<PRAGMA>` inside the statement, immediately\r\nbefore the terminating `.` or `,` on the line identified by the finding:\r\n\r\n```abap\r\n\" Before:\r\n WRITE: / 'Financial Document Report'.\r\n\r\n\" After:\r\n WRITE: / 'Financial Document Report' ##NO_TEXT.\r\n```\r\n\r\nFor multi-line statements, place the pragma on the last line before the period.\r\n\r\n**Common pragmas for upgrade findings:**\r\n\r\n| messageId pattern | Pragma | Meaning |\r\n|---|---|---|\r\n| 1700, 1711, 1713 | `##NO_TEXT` | String not stored as text element |\r\n| 1006, 1007 | `##FM_SUBRC_OK` | SY-SUBRC not checked after FM call |\r\n| 0207, 0208, 0212 | `##NO_HANDLER` | Empty CATCH block |\r\n| 0503, 0801-0845 | `##NEEDED` | Variable declared but not used |\r\n| — | `##CATCH_ALL` | Catching CX_ROOT |\r\n| — | `##FM_OLDED` | Deliberately using deprecated FM |\r\n\r\n**2. Pseudo-comment path — ATC/Code-Inspector checks.** Many ATC and\r\nCode-Inspector checks are suppressed not by a pragma but by a pseudo-comment\r\n`\"#EC <CHECK_ID>` appended to the end of the offending line:\r\n\r\n```abap\r\n\" Before:\r\n SELECT * FROM mara INTO TABLE @lt_mara.\r\n\r\n\" After (CI check suppressed with its own pseudo-comment token):\r\n SELECT * FROM mara INTO TABLE @lt_mara. \"#EC CI_NOORDER\r\n```\r\n\r\nThe `<CHECK_ID>` MUST come from the finding's OWN ATC metadata — the check's\r\npseudo-comment, which is carried on the ATC worklist finding (the detailed\r\nworklist returned with Accept `application/atc.worklist.v1+xml` exposes the\r\ncheck's pseudo-comment / suppression token). NEVER invent or guess a CHECK_ID.\r\nIf you do not have the real pseudo-comment token for the finding from its ATC\r\nmetadata, do not fabricate one.\r\n\r\n**Difference, at a glance:**\r\n\r\n| Form | Where it goes | Example | Used by |\r\n|---|---|---|---|\r\n| Pragma `##NAME` | INSIDE the statement, before the `.`/`,` | `... 'Report' ##NO_TEXT.` | SLIN / extended program check |\r\n| Pseudo-comment `\"#EC ID` | TRAILING `\"#EC` comment at end of line | `SELECT ... . \"#EC CI_NOORDER` | ATC / Code-Inspector checks |\r\n\r\nSome checks accept only one of the two forms — use the form indicated by the\r\nlookup (pragma from SLIN_DESC) or by the finding's ATC metadata (pseudo-comment).\r\nDo not substitute one form for the other.\r\n\r\n**If neither a real pragma NOR a real pseudo-comment can be determined** for a\r\nfinding (no SLIN_DESC pragma AND no pseudo-comment token on the ATC finding —\r\ne.g. some custom checks): say so explicitly and treat the finding as\r\nfix-or-ATC-exempt. It must be either fixed or formally exempted via the ATC\r\nworklist. Do NOT fabricate a suppression token to silence it.\r\n\r\n**Not suppressible — S/4HANA incompatibility findings.** Genuine S/4HANA\r\n*breaks* — removed/changed tables (BSEG, VBUK, VBUP, KONV, MKPF/MSEG, …),\r\nremoved APIs, changed function-module signatures — are NOT lint-style findings\r\nand are generally NOT suppressible. Suppression (pragma or pseudo-comment) is\r\nfor lint-style findings only (no-ORDER-BY, NO_TEXT, unused var, SUBRC-not-\r\nchecked, …). An incompatibility finding must be FIXED (Step 3h) or formally\r\nATC-exempted — never suppressed with a token to hide a real break.\r\n\r\nBatch all suppression insertions (pragmas AND pseudo-comments) together with any\r\ncode fixes for the object into the SAME single `sap_set_source` write.\r\n\r\n**3h. Apply approved fixes and suppressions (if yes, select, or suppress):**\r\n\r\n1. Combine ALL approved changes into one new source version in memory. Never\r\n write per-finding — one `sap_set_source` call per object.\r\n2. Syntax-check BEFORE writing: call `sap_syntax_check` on the proposed source.\r\n If errors: show them, offer one self-correction attempt. If still broken:\r\n mark as \"needs manual fix\", skip. **Do NOT call `sap_set_source` until syntax\r\n is clean** — it auto-activates, and broken code goes live immediately.\r\n3. Snapshot (Forge Rule 7): call `sap_snapshot` (take). If it fails: abort the\r\n write, log reason, move to next object. No exceptions.\r\n4. Write: call `sap_set_source` with fixed source and transport number.\r\n5. Explicit activation: call `sap_activate`. Do NOT trust `sap_set_source`\r\n auto-activation — it reports `activated:true` but objects stay inactive.\r\n6. Verify activation (Forge Rule 10): call `sap_inactive_objects` to confirm\r\n the object no longer appears on the inactive list. If it does: the activation\r\n failed silently. Show errors, call `sap_snapshot` (restore), mark\r\n `status: rollback_restored`, move on.\r\n7. On success: report \"Object {NAME}: {N} findings fixed, activation verified.\r\n Object {N}/{TOTAL} done. {R} remaining.\"\r\n8. Update state file: `status: fixed`, resolved finding IDs, transport, timestamp.\r\n\r\n**3i. Line number safety:**\r\nAfter any successful write, re-read source via `sap_get_source` before processing\r\nthe next object. Line numbers shift when lines are inserted or removed.\r\n\r\n### Step 4 — Session Summary\r\n\r\n```\r\nFix Session Complete\r\nObjects: {TOTAL} | Fixed: {F} | Skipped by dev: {S}\r\nLocked: {L} | Activation failed + restored: {E} | Manual review: {M}\r\n\r\nFindings auto-fixed: {N} | Suppressed (pragma): {SP} | Manual review: {P} | Not addressed: {Q}\r\nTransport: {TRANSPORT}\r\n\r\nOutputs:\r\n - Progress (state file) -> .cspeach/upgrades/{basename}.json\r\n - Next step -> /abap-upgrade-verify\r\n```\r\n\r\n## Batch Mode — Grouped Actions with Multi-Agent Support\r\n\r\nWhen a scan produces a large number of findings (50+), switch from per-object\r\ninteractive mode to **grouped batch mode**. This is the primary workflow for\r\nreal customer engagements.\r\n\r\n### Step 5 — Package-Level Summary (before per-object loop)\r\n\r\nPresent all findings grouped by action type:\r\n\r\n```\r\nPackage ZCUSTOM_FI — 312 objects, 2,847 findings\r\n\r\n Category Objects Findings Action\r\n──────────────────────────────────────────────────────────────\r\n [1] Table replacement (BSEG) 23 89 FIX\r\n [2] Table replacement (KONV) 12 34 FIX\r\n [3] Table replacement (VBUK/VBUP) 8 22 FIX\r\n [4] String not translated 187 1,841 SUPPRESS ##NO_TEXT\r\n [5] SY-SUBRC not checked 45 203 SUPPRESS ##FM_SUBRC_OK\r\n [6] Unused variables 67 312 SUPPRESS ##NEEDED\r\n [7] SELECT * without field list 34 98 FIX\r\n [8] Output management 4 18 MANUAL REVIEW\r\n [9] Credit management 2 8 MANUAL REVIEW\r\n [10] Other SLIN checks 31 222 REVIEW INDIVIDUALLY\r\n──────────────────────────────────────────────────────────────\r\n\r\nProcess: all [a] / select groups [1,2,4,5] / batch size [b20] / skip [n]\r\n```\r\n\r\nDeveloper picks groups. Suppressions (groups 4,5,6) are mechanical — no\r\njudgment needed. Code fixes (groups 1,2,3,7) require per-object review.\r\n\r\n### Step 6 — Batch Suppression Execution\r\n\r\nWhen developer selects suppress groups, process objects sequentially:\r\n\r\n**Per object in the suppress list:**\r\n1. `sap_get_source` — read current source\r\n2. For every finding line, insert the correct suppression token (Step 3g):\r\n a pragma `##{PRAGMA}` before the statement terminator (`.` or `,`) when\r\n SLIN_DESC returns a pragma, OR a trailing pseudo-comment `\"#EC {CHECK_ID}`\r\n at the end of the line for ATC/Code-Inspector checks (CHECK_ID from the\r\n finding's own ATC metadata — never invented)\r\n3. `sap_syntax_check` — MUST pass. If error: skip object, report failure.\r\n4. `sap_snapshot` (take) — MUST succeed. If error: skip object.\r\n5. `sap_set_source` — write with transport\r\n6. `sap_activate` — explicit activation\r\n7. `sap_inactive_objects` — confirm NOT on inactive list.\r\n If still inactive: `sap_snapshot` (restore), report failure.\r\n8. Report progress: `\"Object {N}/{TOTAL}: {NAME} — {count} pragmas inserted.\"`\r\n\r\nUpdate state file after each object (resumable if session drops).\r\n\r\n### Step 7 — Per-Object Code Fixes (after batch suppress)\r\n\r\nAfter batch suppression completes, remaining findings require per-object\r\ninteractive review (Steps 3a-3i). The finding count is now much smaller:\r\n\r\n```\r\nBatch suppress complete: 2,356 findings suppressed across 231 objects.\r\nRemaining: 491 findings across 81 objects (require code changes).\r\n\r\nContinue with per-object fix mode? [y/n]\r\n```\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\nObject {N}/{TOTAL}: {OBJECT_NAME} ({OBJECT_TYPE})\r\nProgress: {done} fixed, {skipped} skipped, {remaining} remaining\r\n\r\nFindings for {OBJECT_NAME}:\r\n| # | Line | SAP Note | Message | Category |\r\n|---|------|----------|----------------------------|-------------------|\r\n| 1 | {ln} | {note} | {message_title} | {category} |\r\n\r\nSuccessor lookup — Finding 1 ({DEPRECATED}):\r\n sap_sql_query → successor = '{SUCCESSOR}', note = '{NOTE}'\r\n Confidence: HIGH (SAP successor table)\r\n\r\nFinding 1 — Proposed Fix (Line {LN}) — {OLD} → {NEW} [HIGH — ARS_W_API_SCCSSR]\r\n- {old_code}\r\n+ {new_code}\r\n\r\nFinding 2 — MANUAL REVIEW REQUIRED\r\nReason: {reason}. SAP Note: {note}. This finding will NOT be auto-fixed.\r\n\r\nFindings with proposed fixes: {N_AUTO} | Manual review: {N_MANUAL} | Suppressible: {N_SUPPRESS}\r\nOptions: yes / select / no / skip / suppress (e.g. suppress 4,5)\r\n\r\nPragma lookup (Finding 4, messageId 1700):\r\n sap_sql_query: SELECT DISTINCT pragma FROM slin_desc WHERE code_nr = '1700'\r\n Result: ##NO_TEXT\r\n\r\nField check: {TARGET_TABLE} — confirmed fields: {field1}, {field2}, ...\r\nSyntax check: PASSED (0 errors)\r\nsap_snapshot (take) → {snapshot_id}\r\nsap_set_source (transport: {TR}) → statusCode: 200\r\nsap_activate → success: true, hasErrors: false\r\nsap_inactive_objects → {OBJECT_NAME} NOT on inactive list ✓\r\nObject {NAME}: {N} findings fixed, activation VERIFIED.\r\nObject {N}/{TOTAL} done. {R} remaining.\r\n\r\n---\r\n\r\nFix Session Complete\r\nObjects: {TOTAL} Fixed: {F} Suppressed: {SP} Skipped: {S} Locked: {L} Failed: {E}\r\nTransport: {TR} | Next step: /abap-upgrade-verify\r\n```\r\n\r\n## Slice / Scope Filter (--scope)\r\n\r\nMulti-developer upgrade projects need to run /abap-upgrade-fix on\r\ndifferent package slices in parallel without colliding. The `--scope`\r\nflag filters the loaded baseline to a subset of objects before the\r\nfix loop starts. Three accepted syntaxes (priority order):\r\n\r\n1. `--scope @<file>` — file containing one object name per line (or a\r\n comma-separated list). Most flexible for assignment workflows where\r\n the lead curates a list.\r\n Example: `/abap-upgrade-fix --from @scan-baseline.cspeach.json --scope @assignments-dev-A.txt`\r\n\r\n2. `--scope ZFI*,ZSD*` — comma-separated patterns, glob `*` wildcard.\r\n Example: `/abap-upgrade-fix --from @scan-baseline.cspeach.json --scope ZFI*,ZSD_REPORT*`\r\n\r\n3. `--scope ZFI*` — single pattern.\r\n Example: `/abap-upgrade-fix --from @scan-baseline.cspeach.json --scope ZFI*`\r\n\r\nBehaviour:\r\n- Filter is applied AFTER the baseline loads but BEFORE the per-object\r\n fix loop. Objects not matching the scope are NOT touched, NOT counted\r\n in this run's progress; they remain `pending` in the next merge step.\r\n- Each scope produces a separate `upgrade-progress` manifest. A later\r\n `/abap-upgrade-merge` reconciles N progress files into one consolidated\r\n view before `/abap-upgrade-verify`.\r\n- If a developer runs without `--scope`, full baseline is processed (the\r\n single-developer case).\r\n- Patterns are case-insensitive and match against object name. The `*`\r\n wildcard is the only supported glob.\r\n\r\nWhen dispatching to /abap-upgrade-verify after a scoped fix run, the\r\nverify step sees only the scoped slice — the consolidated cross-slice\r\nverification happens after merge.\r\n\r\n## Manifest Emission (MANDATORY — last block of every successful output)\r\n\r\nAfter the human-facing fix log + summary, emit ONE manifest block so the\r\nsave hook can persist the `upgrade-progress` envelope. Same pattern as\r\nupgrade-scan, parsed by `extractUpgradeProgress`.\r\n\r\n```\r\n<!-- cspeach:upgrade-manifest\r\nartefact: upgrade-progress\r\ntitle: <baseline title — e.g. ZTEST_LAS — S/4HANA 2023 readiness — Dev A slice>\r\ndetail_path: <project-relative path to progress JSON, e.g. .cspeach/upgrades/ZTEST_LAS_progress_2026-05-09_devA.json>\r\nbaseline_path: <project-relative path to the originating scan baseline>\r\nbaseline_sha256: <sha256 of the baseline JSON file body, hex>\r\ntransport: <TR number, or \"-\" if no transport assigned>\r\nfixed: <count fixed and activated successfully>\r\nskipped: <count skipped by user via review prompt>\r\nfailed: <count where fix attempt errored out>\r\npending: <count in-scope but not yet attempted (--scope partial run)>\r\nfixes:\r\n finding-001 | <objectName> | <atcRule> | fixed\r\n finding-002 | <objectName> | <atcRule> | skipped\r\n finding-003 | <objectName> | <atcRule> | failed\r\n ...\r\n-->\r\n```\r\n\r\nRules:\r\n- IDs MUST match those of the originating baseline (finding-001 here =\r\n finding-001 there). Stable IDs are how `/abap-upgrade-merge` reconciles\r\n multiple progress files.\r\n- `baseline_path` is project-relative; same portability rule as scan.\r\n- `baseline_sha256` is the sha256 of the baseline JSON file body (hex,\r\n lowercase). The verify step uses this to detect \"you fixed against\r\n a baseline that's been re-run since\" and warn.\r\n- Status values come from the upgrade-progress status enum:\r\n `fixed` / `skipped` / `failed` / `pending`. Use `pending` for\r\n in-scope objects the run never reached (e.g. session interrupted).\r\n\r\n## Post-Save Chain Prompt (auto-route to /abap-upgrade-verify)\r\n\r\nAfter the manifest + save fires, ask once:\r\n\r\n> \"Fix run saved. {fixed_count} fixed, {failed_count} failed,\r\n> {pending_count} pending. Run /abap-upgrade-verify next?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-verify (formal sign-off-ready report)\r\n> - Run /abap-upgrade-merge (consolidate with other devs' slices first)\r\n> - End turn\r\n\r\nOn \"Run /abap-upgrade-verify next\" — `dispatch_skill` with the verify\r\ncommand, copying the `Source file:` token from the prompt header\r\nverbatim. Same caveats apply when the saved file is the just-emitted\r\nprogress (post-save hook hasn't run yet) — fall back to the hint:\r\n\r\n> `→ Run \\`/files\\`, pick the just-saved progress, then choose /abap-upgrade-verify.`\r\n\r\nOn \"Run /abap-upgrade-merge\" — print the hint, end the turn (merge\r\ntakes multiple progress files via N `--inputs @<file>` flags; user\r\ncollects them out-of-band first).\r\n\r\n## Guardrails\r\n\r\n### Write Safety\r\n- NEVER use `sap_set_source` to rewrite a full class when only modifying method\r\n logic — use `sap_update_method` per method instead (Rule 7a). Only use\r\n `sap_set_source` when the class definition itself must change.\r\n- NEVER call `sap_set_source` without a prior successful `sap_snapshot` for that\r\n object (Forge Rule 7). Snapshot failure = hard stop, not a warning.\r\n- NEVER call `sap_set_source` before `sap_syntax_check` passes. `sap_set_source`\r\n auto-activates — broken code becomes live for all users immediately.\r\n- NEVER write per-finding. Batch all approved changes for one object into a\r\n single combined source and write once.\r\n\r\n### Activation Safety\r\n- ALWAYS call `sap_activate` explicitly after `sap_set_source`. The auto-activation\r\n in `sap_set_source` is unreliable — it reports `activated:true` while the object\r\n remains inactive. This was verified on live SAP BASIS 758 SP02 (2026-04-01).\r\n- ALWAYS call `sap_inactive_objects` after `sap_activate` to confirm the object\r\n is no longer on the inactive list. This is the only reliable check.\r\n- If the object is still inactive after explicit activation: treat as activation\r\n failure. Show the errors, restore from snapshot, move on.\r\n\r\n### Field Verification\r\n- NEVER reference a field on a replacement table without verifying it exists via\r\n `sap_sql_query` on DD03L. S/4 versions differ — field names from documentation\r\n may not match the target system.\r\n- For table replacements (BSEG→ACDOCA, VBUK→VBAK, VBUP→VBAP, KONV→PRCD_ELEMENTS):\r\n query DD03L FIRST, then propose code. Not the other way around.\r\n- Show the field verification results to the developer before they approve.\r\n\r\n### Developer Control\r\n- NEVER auto-fix without developer approval. The `yes/select/no/skip` prompt is\r\n mandatory for every object, no exceptions.\r\n- NEVER attempt to fix functional redesign findings. Identify, explain, mark as\r\n manual review, and move on.\r\n- Always show successor confidence level (HIGH/MEDIUM/LOW). Developer must be\r\n able to assess fix quality before approving.\r\n\r\n### Error Handling\r\n- If `sap_sql_query` fails: fall back gracefully, log it, label confidence MEDIUM.\r\n- Do NOT claim \"fixed\" unless `sap_activate` succeeded AND `sap_inactive_objects`\r\n confirms the object is active. The response from `sap_set_source` is not reliable.\r\n- Multiple findings on the same line: re-read source after each successful write;\r\n re-derive line references from the updated source before the next diff.\r\n\r\n**Known table replacements — fallback when ARS_W_API_SCCSSR returns nothing:**\r\n\r\n| Deprecated | Successor | Area | SAP Note |\r\n|-----------|----------|------|----------|\r\n| BSEG | ACDOCA | FI line items | 2431747 |\r\n| BSIS / BSAS / BSIK / BSID | ACDOCA + I_JournalEntry CDS | FI secondary indexes | 2431747 |\r\n| MKPF / MSEG | MATDOC | Material documents | — |\r\n| VBUK | Status fields on VBAK | SD header status | 2198647 |\r\n| VBUP | Status fields on VBAP | SD item status | 2198647 |\r\n| KONV | PRCD_ELEMENTS | Pricing conditions | 2220005 |\r\n| COEP | ACDOCA | CO line items | — |\r\n| FAGLFLEXA | ACDOCA | General ledger | — |\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-upgrade-fix\r\n\r\nBaseline: .cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json\r\nTransport: DEVK912399\r\n```\r\n\r\n## Example Output — Small Scope (< 50 findings)\r\n\r\n```\r\nLoading baseline: 23 objects, 67 findings. 0 already fixed.\r\nTransport: DEVK912399\r\n\r\nObject 1/23: ZFI_BSEG_READER (Report)\r\n\r\n Category Count Action Detail\r\n──────────────────────────────────────────────────────────────\r\n [1] Table replacement 2 FIX BSEG/BSAS → ACDOCA\r\n [2] SELECT * 1 FIX Add field list\r\n [3] String not translated 8 SUPPRESS ##NO_TEXT\r\n [4] SY-SUBRC not checked 1 SUPPRESS ##FM_SUBRC_OK\r\n──────────────────────────────────────────────────────────────\r\n Total: 12 Fix: 3 Suppress: 9 Manual: 0\r\n\r\nApply: all [a] / fix-only [f] / suppress-only [s] / select [1,2] / skip [n]\r\n> a\r\n\r\n[Shows diffs for findings 1-2, pragma insertions for 3-4]\r\nSyntax check: PASSED\r\nsap_snapshot → taken\r\nsap_set_source → 200\r\nsap_activate → success\r\nsap_inactive_objects → NOT on list ✓\r\n\r\nObject ZFI_BSEG_READER: 3 fixed, 9 suppressed. 22 remaining.\r\n```\r\n\r\n## Example Output — Large Scope (500+ findings, batch mode)\r\n\r\n```\r\nLoading baseline: 312 objects, 2,847 findings. 0 already fixed.\r\nTransport: DEVK912399\r\n\r\n Category Objects Findings Action\r\n──────────────────────────────────────────────────────────────\r\n [1] Table replacement (BSEG) 23 89 FIX\r\n [2] Table replacement (KONV) 12 34 FIX\r\n [3] Table replacement (VBUK/VBUP) 8 22 FIX\r\n [4] String not translated 187 1,841 SUPPRESS ##NO_TEXT\r\n [5] SY-SUBRC not checked 45 203 SUPPRESS ##FM_SUBRC_OK\r\n [6] Unused variables 67 312 SUPPRESS ##NEEDED\r\n [7] SELECT * without field list 34 98 FIX\r\n [8] Output management 4 18 MANUAL REVIEW\r\n [9] Credit management 2 8 MANUAL REVIEW\r\n [10] Other SLIN checks 31 222 REVIEW INDIVIDUALLY\r\n──────────────────────────────────────────────────────────────\r\n\r\nProcess: all [a] / select groups [4,5,6] / skip [n]\r\n> 4,5,6\r\n\r\nBatch suppress: 2,356 findings across 231 objects.\r\nProcessing sequentially...\r\n\r\nObject 1/231: ZFI_AGING_REPORT — 14 ##NO_TEXT inserted ✓\r\nObject 2/231: ZFI_BALANCE_CHECK — 8 ##NO_TEXT, 2 ##FM_SUBRC_OK inserted ✓\r\nObject 3/231: ZFI_CASHFLOW — 11 ##NO_TEXT inserted ✓\r\n...\r\nObject 150/231: ZFI_OLD_REPORT — SKIPPED (locked by JSMITH)\r\n...\r\nObject 231/231: ZSD_UTIL_CONVERT — 3 ##NEEDED inserted ✓\r\n\r\nBatch suppress complete: 2,341 suppressed across 230 objects. 1 skipped (locked).\r\nRemaining: 491 findings across 82 objects (code changes needed).\r\n\r\nContinue with per-object fix mode? [y]\r\n\r\nObject 1/82: ZFI_BSEG_READER (Report)\r\n[... per-object interactive fix flow as above ...]\r\n\r\nFix Session Complete\r\nObjects: 312 Fixed: 72 Suppressed: 230 Skipped: 5 Locked: 2 Failed: 3\r\nFindings: 2,847 total → 2,341 suppressed, 344 fixed, 136 manual review, 26 skipped\r\nTransport: DEVK912399 | Next step: /abap-upgrade-verify\r\n```\r\n",
240
247
  "sha256": "372c0130b96404fce3c32515bd49f0e8d7f33ed6f6addb4bcc55129211e6e329",
241
248
  "signature": "",
242
- "signedAt": "2026-09-17T20:19:05.863Z"
249
+ "signedAt": "2026-09-28T14:37:05.184Z"
243
250
  },
244
251
  "abap-upgrade-merge": {
245
252
  "name": "abap-upgrade-merge",
246
253
  "body": "---\r\nname: abap-upgrade-merge\r\ndescription: >\r\n Reconcile multiple parallel /abap-upgrade-fix progress files into one\r\n consolidated upgrade-progress. For multi-developer upgrade teams who\r\n ran scoped slices in parallel — merges N progress envelopes back into\r\n a single picture before /abap-upgrade-verify.\r\nphase: BUILD\r\nrequires_mcp: optional\r\nforge_rules: [1, 6]\r\nversion: \"2.0\"\r\nmin_cli_version: \"0.9.0\"\r\n---\r\n\r\n# abap-upgrade-merge\r\n\r\n## Purpose\r\n\r\nBig-team upgrade projects split the baseline scan into per-developer\r\nslices via `/abap-upgrade-fix --scope ZFI*`. Each developer runs their\r\nslice independently, producing their own `upgrade-progress` envelope.\r\nBefore `/abap-upgrade-verify` can produce a consolidated sign-off\r\nreport, those N progress files need to be merged back into ONE\r\nauthoritative progress view.\r\n\r\n`/abap-upgrade-merge` does that reconciliation — same baseline IDs\r\nacross all inputs let it union per-finding statuses without losing\r\ninformation.\r\n\r\nThis skill produces a NEW `upgrade-progress` envelope that supersedes\r\neach input. The originals are preserved unchanged on disk so the team\r\naudit trail remains intact.\r\n\r\n## When to Use\r\n\r\n- Multi-developer team where each ran `/abap-upgrade-fix --scope` on a\r\n different package slice and now needs one verifier-ready progress\r\n file.\r\n- Cross-shift handoffs (onshore-AM, onshore-PM, offshore-night) — each\r\n shift produces a progress slice; merge each morning consolidates.\r\n- After splitting a single dev's run across multiple sessions (laptop\r\n battery died, switched to desktop) — merge consolidates the partial\r\n progress files into one.\r\n\r\nDo NOT use for:\r\n- Combining progress files from DIFFERENT baselines. The skill rejects\r\n inputs whose `baseline_sha256` doesn't match.\r\n- Full system audit (\"show me all upgrade work ever done\") — that's a\r\n reporting concern, not a merge.\r\n\r\n## Inputs Expected\r\n\r\n```\r\n/abap-upgrade-merge --inputs @<progress-A> @<progress-B> @<progress-C>\r\n```\r\n\r\nTwo or more `--inputs @<file>` references. Each must be an\r\n`upgrade-progress` envelope. The skill loads each, validates that\r\n`baseline_sha256` matches across all inputs, and reconciles per\r\nfinding.\r\n\r\nOptional flags:\r\n- `--baseline @<scan-baseline>` — explicit baseline reference for the\r\n merge. If omitted, baseline is derived from the first input's\r\n `baseline_path`. The explicit form is recommended when the inputs\r\n came from different machines (path differences are normalised\r\n against the explicit baseline).\r\n\r\n## Required Behavior\r\n\r\n**The merge itself is DETERMINISTIC CODE, not your reasoning.** Battery\r\ntesting proved that hand-reconciling envelopes corrupts counts. You\r\norchestrate and present; the `upgrade_merge_progress` tool does ALL\r\narithmetic.\r\n\r\n### 1. Call the `upgrade_merge_progress` tool\r\n\r\nOne call, passing the user's input references through verbatim:\r\n\r\n```\r\nupgrade_merge_progress({\r\n inputs: [\"@ZTEST_progress_devA.cspeach.json\", \"@ZTEST_progress_devB.cspeach.json\"],\r\n baseline: \"@ZTEST_scan.cspeach.json\" // the originating upgrade-scan envelope — pass it whenever the user gave --baseline or the scan file is known\r\n})\r\n```\r\n\r\nThe tool validates every envelope (artefact type), HARD-ERRORS on any\r\n`baseline_sha256` mismatch, reconciles per-finding statuses with the\r\nprecedence `failed > fixed > skipped > pending`, detects double-fixes,\r\nrecomputes every count from the merged rows, and returns:\r\n\r\n- `merged_summary`, `per_input` — consolidated + per-slice numbers\r\n- `warnings` — double-fixes, unknown statuses, transport mismatches\r\n- `merged_fixes` — the full merged per-finding rows\r\n- `manifest_block` — the EXACT `csforge:upgrade-manifest` block to emit\r\n\r\nWith an explicit `baseline`, the tool also fills baseline findings no\r\nslice touched as `pending`, so the merged view covers the whole scan.\r\nWithout it, consistency is checked against the first input's\r\nbaselineRef and the tool warns that untouched findings are not filled.\r\n\r\nIf the tool returns an error (baseline mismatch, unreadable file),\r\nsurface the message and stop — do NOT merge by hand as a fallback. If\r\nonly one input is supplied, the merge is a no-op — print a hint that\r\nno merge is needed and end the turn.\r\n\r\n### 2. Present the consolidated result\r\n\r\nRender the merged summary from the tool output — numbers copied, never\r\nrecomputed:\r\n\r\n```\r\n## /abap-upgrade-merge — Consolidated Progress\r\n\r\n**Baseline:** {baseline title} (sha256 {short})\r\n**Inputs merged:** {N} progress files\r\n**Fixed:** {merged_summary.fixed}\r\n**Skipped:** {merged_summary.skipped}\r\n**Failed:** {merged_summary.failed}\r\n**Pending:** {merged_summary.pending} (out-of-scope across all slices, or never reached)\r\n\r\n### Per-input breakdown\r\n| Input file | Fixed | Skipped | Failed | Pending |\r\n|---|---|---|---|---|\r\n{one row per per_input entry}\r\n\r\n### Warnings\r\n{each tool warning, one bullet each}\r\n```\r\n\r\nYOUR value-add is the judgment commentary: which double-fixes need a\r\ndev conversation, what the failed findings block, whether pending\r\ncounts mean a slice was never run. Recommend concrete follow-ups.\r\n\r\n### 3. Emit the manifest VERBATIM\r\n\r\nPaste `manifest_block` from the tool result — unchanged, byte for byte\r\n— as the LAST block of the response. Do not re-sort rows or adjust\r\ncounts. The CLI save hook parses this block; the merged `detail_path`\r\nis a NEW `_progress_merged_<date>` file so no input is overwritten.\r\n\r\n### 4. Post-Save Chain Prompt\r\n\r\nAfter the merge envelope saves, ask once:\r\n\r\n> \"Merged {N} slices: {fixed_count} fixed, {failed_count} failed,\r\n> {pending_count} pending. Run /abap-upgrade-verify on the merged\r\n> result?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-verify on the merged progress (recommended)\r\n> - End turn\r\n\r\nUse the hint pattern (the merged file is the just-saved one):\r\n\r\n> `→ Run \\`/files\\`, pick the just-saved merged progress, then choose /abap-upgrade-verify.`\r\n\r\n## Output Structure\r\n\r\n(See step 2 above — the rendered summary IS the output.)\r\n\r\n## Guardrails\r\n\r\n- READ-ONLY against SAP. The skill only reads the input progress files\r\n and the baseline; it does NOT touch the system, write transports,\r\n or modify code. Forge Rule 6 (read-only by default).\r\n- Reject baseline-sha256 mismatches FIRST. A merge across different\r\n baselines is meaningless and would silently produce a corrupt\r\n consolidated view.\r\n- Preserve originals — input progress files are never modified.\r\n- No estimates. The merge is a fact-collection step; do not invent\r\n effort numbers or ATC re-checks here. Verify is the next step and\r\n it does the actual re-check.\r\n- Do not chain into /abap-upgrade-fix from this skill. The merge is\r\n followed by verify; remediation of regressions is verify's hand-off.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-upgrade-merge --inputs @ZTEST_LAS_progress_devA.cspeach.json @ZTEST_LAS_progress_devB.cspeach.json @ZTEST_LAS_progress_devC.cspeach.json\r\n```\r\n\r\n## Example Output (abridged)\r\n\r\n```\r\n## /abap-upgrade-merge — Consolidated Progress\r\n\r\n**Baseline:** ZTEST_LAS — S/4HANA 2023 readiness (sha256 a7Qj8…)\r\n**Inputs merged:** 3 progress files\r\n**Total findings:** 42\r\n**Fixed:** 25 (from 3 devs)\r\n**Skipped:** 1\r\n**Failed:** 1\r\n**Pending:** 15\r\n\r\n### Per-input breakdown\r\n| Input file | Slice | Fixed | Skipped | Failed | Pending |\r\n|----------------------------------|-------|-------|---------|--------|---------|\r\n| ZTEST_LAS_progress_devA.cspeach…| ZFI* | 12 | 1 | 0 | 0 |\r\n| ZTEST_LAS_progress_devB.cspeach…| ZSD* | 8 | 0 | 1 | 0 |\r\n| ZTEST_LAS_progress_devC.cspeach…| ZMM* | 5 | 0 | 0 | 0 |\r\n\r\n### Warnings\r\n⚠ DOUBLE-FIX: finding-018 — ZGEN_HELPER (ZFI* and ZSD* slices both fixed it).\r\n Verify which fix is active via /abap-upgrade-verify.\r\n\r\n<!-- csforge:upgrade-manifest\r\nartefact: upgrade-progress\r\ntitle: ZTEST_LAS — S/4HANA 2023 readiness — Merged (3 slices)\r\ndetail_path: .cspeach/upgrades/ZTEST_LAS_progress_merged_2026-05-09.json\r\nbaseline_path: .cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json\r\nbaseline_sha256: a7Qj8ibfLTj49kQRTn1DOE_UAExuoBgjhaXxf33+JPY=\r\ntransport: -\r\nfixed: 25\r\nskipped: 1\r\nfailed: 1\r\npending: 15\r\nfixes:\r\n finding-001 | ZFI_REPORT | S4HANA_BUSINESS_PARTNER | fixed\r\n finding-018 | ZGEN_HELPER | API_DEPRECATED | fixed\r\n finding-019 | ZSD_ORDER_PROC | DB_CDS_REQUIRED | failed\r\n finding-020 | ZSD_ORDER_VALIDATE | NULL_CHECK_REQUIRED | skipped\r\n ...\r\n-->\r\n```\r\n\r\n## Forge Rules Compliance\r\n\r\n- Rule 1 (no blind generation): merge is mechanical reconciliation,\r\n no LLM creativity. Conflict resolution rules are deterministic and\r\n run in CODE — the `upgrade_merge_progress` tool — never in model\r\n arithmetic.\r\n- Rule 6 (read-only by default): no SAP writes, no transport mutation.\r\n- All inputs are auditable on disk; merge produces a NEW file.\r\n",
247
254
  "sha256": "33380034dc9d149000e5bae21c3c4d5fc054c185d80c70f7ce04c07d7d7b94a0",
248
255
  "signature": "",
249
- "signedAt": "2026-09-17T20:19:05.916Z"
256
+ "signedAt": "2026-09-28T14:37:05.220Z"
250
257
  },
251
258
  "abap-upgrade-scan": {
252
259
  "name": "abap-upgrade-scan",
253
- "body": "---\nname: abap-upgrade-scan\ndescription: \"Scan custom ABAP code for S/4HANA upgrade findings. Runs ATC with readiness variant, categorizes findings, produces remediation plan with effort estimate. Use before starting upgrade remediation.\"\nphase: ANALYZE\nrequires_mcp: required\nforge_rules: [1, 3, 6]\nversion: \"1.0\"\n---\n\n# abap-upgrade-scan\n\n## Purpose\n\nAnalyze a custom code package against a target S/4HANA release by running ATC\nwith the corresponding readiness variant. Parse all findings, categorize them into\nfive actionable buckets, look up successor APIs from SAP's own ARS_W_API_SCCSSR\ntable, calculate a realistic effort estimate, and produce a consulting-grade\nUpgrade Readiness Report.\n\nThis skill is read-only and produces analysis only. It never modifies source code.\nThe output is the entry point for `/abap-upgrade-fix` (automated remediation) and\n`/abap-upgrade-verify` (sign-off report). State is persisted to\n`.cspeach/upgrades/` so the downstream skills can read the baseline.\n\nForge Rule 1: No assumption about findings — all categorization is driven by\nactual ATC output, not guesses. Forge Rule 3: No cloud readiness claim without\nevidence from the ARS table or system query. Forge Rule 6: This skill is\nread-only — it records findings, never applies changes.\n\n## When to Use\n\n- Before starting an S/4HANA upgrade project to scope custom code remediation\n- When a SAP Readiness Check has returned a finding count and you need details\n- To produce a project proposal with a defensible effort estimate for a customer\n- As the first step in the scan → fix → verify workflow\n- When moving between S/4HANA releases (e.g., 2022 → 2023 delta scan)\n\nThis skill requires an active MCP connection to SAP. The ATC variant must be\ninstalled on the target system. Without MCP, use `/abap-migrate` for an\noffline paste-mode assessment.\n\nWhen NOT to use: fixing the findings — `/abap-upgrade-fix` (step 2 of 3); per-object migration strategy (retire/replace/refactor) — `/abap-migrate`.\n\n## Inputs Expected\n\nRequired:\n- Package scope — the ABAP package name to scan (e.g., ZTEST_LAS, ZFI_CUSTOM).\n Sub-packages are included automatically via DEVC scope in ATC.\n- Target release — the S/4HANA version you are upgrading TO.\n Accepted values: 2020, 2021, 2022, 2023, 2025, HANA (DB migration only),\n or CLOUD (clean core / cloudification).\n\nOptional:\n- Include usage analysis — whether to query /SDF/MON_RECS for program usage\n statistics. Enables identification of retire candidates. Default: ask the user.\n- Baseline comparison — if a prior scan JSON exists in `.cspeach/upgrades/`\n (or, for older projects, the legacy `.abapforge/upgrades/`), offer to compare\n against it (delta scan).\n\nIf any input is missing, ask before running. Do not assume defaults for package\nscope or target release — these are the two variables that determine everything.\n\n## ATC Variant Mapping\n\nMap the target release to the ATC variant name before calling `sap_atc_run`:\n\n| Target Release | ATC Variant Name | SAP Note |\n|----------------------|---------------------------|----------|\n| S/4HANA 2020 | S4HANA_READINESS_2020 | — |\n| S/4HANA 2021 | S4HANA_READINESS_2021 | — |\n| S/4HANA 2022 | S4HANA_READINESS_2022 | 3231748 |\n| S/4HANA 2023 | S4HANA_READINESS_2023 | 3365357 |\n| S/4HANA 2025 | S4HANA_READINESS_2025 | — |\n| HANA DB migration | FUNCTIONAL_DB | 1935918 |\n| Cloud / Clean Core | Cloudification Repository | 3284711 |\n\nIf the user specifies only the year (e.g., \"2023\"), expand to the full variant\nname. If the variant does not exist on the system, see the Guardrails section.\n\n## Required Behavior\n\n### Step 1 — Confirm Inputs\n\nCollect package scope and target release. If not provided in the prompt, ask:\n \"Which package should I scan? (e.g., ZTEST_LAS)\"\n \"Which S/4HANA release are you targeting? (2020 / 2021 / 2022 / 2023 / 2025 / HANA / CLOUD)\"\n \"Should I include usage data analysis to identify retire candidates? (yes / no)\"\n\nDo not proceed until both required inputs are confirmed.\n\n### Step 2 — Map Variant and Run ATC\n\nLook up the variant name from the mapping table above.\nCall `sap_atc_run` with:\n - objectType: DEVC\n - objectName: {package}\n - variant: {mapped_variant}\n\nIf the call succeeds, report: \"ATC run complete. {n} findings returned.\"\nIf the call fails, follow the error handling in Guardrails.\n\n### Step 3 — Parse All Findings\n\nFor each finding returned by `sap_atc_run`, extract:\n- `checkTitle` — the ATC check name (e.g., \"Use of Database Table BSEG\")\n- `messageTitle` — the full finding description text\n- `priority` — 1 (error/blocker), 2 (warning), 3 (info)\n- `location` — source URI (format: `/sap/bc/adt/.../source/main#start=LINE,COL`)\n- `line` — line number extracted from the `#start=` portion of location\n- `objectName` — the ABAP object (program, class, function group, etc.)\n- `sapNote` — 7-digit SAP Note number embedded in checkTitle or messageTitle\n (regex: `\\b\\d{7}\\b`). If multiple notes match, take the first.\n\nBuild a structured internal list of all findings with these fields before\nproceeding to categorization.\n\n### Step 4 — Optional Usage Data Query\n\nIf the user requested usage analysis, run:\n\n```sql\nSELECT program, dynpro, count FROM /sdf/mon_recs\nWHERE program LIKE 'Z%'\nGROUP BY program\nORDER BY count DESCENDING\n```\n\nvia `sap_sql_query`. Match results against the object names in the findings list.\nObjects with count = 0 or no entry in the usage data are retire candidates.\n\nIf the query fails (authorization or table not available), note it in the report\nunder \"Data Gaps\" and continue without usage data. Do not abort the scan.\n\n### Step 5 — Look Up Successor APIs\n\nFor each finding that mentions a deprecated function module, class method, or\nBAPI in `messageTitle`, extract the deprecated object name and query:\n\n```sql\nSELECT * FROM ars_w_api_sccssr WHERE subobjectname = '<deprecated_object_name>'\n```\n\nvia `sap_sql_query`. The result gives the SAP-provided successor API.\n\nIf the query returns a result: mark the finding with `successor_source: ARS_TABLE`\nand record the successor name. Confidence: High.\n\nIf the query returns no result (object not in table): mark as `successor_source: AI_KNOWLEDGE`\nand use your ABAP knowledge to propose the successor. Confidence: Medium.\nIf unsure: mark as `successor_source: UNKNOWN` and recommend manual investigation.\n\nIf the system has no ARS_W_API_SCCSSR table (ECC system), note \"lower confidence —\nno successor table available\" for all API findings. Fall back to AI knowledge\nand `sap_search_object` as needed.\n\n### Step 6 — Categorize All Findings\n\nAssign each finding to exactly one category:\n\n**Quick Wins** — auto-fixable syntactic changes with no behavior risk:\n- checkTitle or messageTitle contains: \"ORDER BY\", \"SELECT *\", \"INTO CORRESPONDING FIELDS\",\n \"MOVE statement\", \"COMPUTE statement\", \"CONCATENATE\", \"pseudo-comment\", \"pragma\",\n \"obsolete statement\", \"SLIN\"\n- These are deterministic fixes — no business logic knowledge required.\n\n**Table Replacements** — direct table access to tables removed or redirected in S/4HANA:\n- messageTitle mentions any of: BSEG, BSIS, BSAS, BSIK, BSID, MKPF, MSEG, VBUK, VBUP,\n KONV, COEP, FAGLFLEXA\n- Reference table for successor mapping:\n\n| Deprecated Table | Successor | Area | SAP Note |\n|-----------------|------------------------|-----------------|----------|\n| BSEG | ACDOCA | FI Line Items | 2431747 |\n| BSIS / BSAS | ACDOCA + CDS view | FI Sec. Indexes | 2431747 |\n| BSIK / BSID | ACDOCA + CDS view | FI Sec. Indexes | 2431747 |\n| MKPF / MSEG | MATDOC | Mat. Documents | — |\n| VBUK | VBAK fields (status) | SD Hdr Status | 2198647 |\n| VBUP | VBAP fields (status) | SD Item Status | 2198647 |\n| KONV | PRCD_ELEMENTS | Pricing Cond. | 2220005 |\n| COEP | ACDOCA | CO Line Items | — |\n\n**API Replacements** — deprecated function modules, BAPIs, or class methods:\n- messageTitle mentions a deprecated FM, BAPI, or class and a successor exists\n (either from ARS_W_API_SCCSSR or AI knowledge)\n- Lookup result from Step 5 determines confidence level\n\n**Functional Redesign** — changes requiring business process decisions:\n- messageTitle mentions: output management, credit management, material ledger,\n business partner (BP migration), customer/vendor integration (CVI), tax engine,\n profit center accounting, new asset accounting\n- These findings cannot be addressed with code-only fixes. Business sign-off\n and process redesign are required. Mark with SAP Note and do not flag as auto-fixable.\n\n**Retire Candidates** — objects with confirmed zero usage:\n- Object has count = 0 in usage data query result, OR\n- Object is a wrapper for a deprecated transaction that no longer exists in S/4HANA\n- Retirement requires business sign-off — note it in the report but do not suggest\n deletion.\n\n### Step 7 — Calculate Effort Estimate\n\nApply the following per-finding estimates:\n\n| Category | Estimate Per Finding | Basis |\n|---------------------|---------------------|---------------------------------------------|\n| Quick win | 5 minutes | Deterministic fix, no business logic |\n| Table replacement | 30 minutes | Field mapping + ledger filter + unit test |\n| API replacement | 45 minutes | Interface mapping + parameter changes |\n| Functional redesign | 1.5 days (avg) | Use 1 day min, 3 days max per item |\n| Retire | 10 minutes | Verify no usage + delete (excl. sign-off) |\n\nFormula:\n Raw hours = (quick_wins × 5/60) + (table_reps × 0.5) + (api_reps × 0.75)\n + (redesigns × 12) + (retires × 10/60)\n Buffered hours = raw_hours × 1.20 (20% buffer for testing and coordination)\n Person-days = buffered_hours / 8\n\nShow the breakdown per category and the final total. Never skip the effort estimate.\n\nIf there are 0 findings: the estimate is 0. Do not invent effort.\n\n### Step 8 — Produce the Upgrade Readiness Report\n\nWrite the full report in the Output Structure format defined below.\n\n### Step 9 — Save Baseline to State File\n\nSave the scan results to `.cspeach/upgrades/{package}_{variant}_{date}.json`\nusing the Write tool. Date format: YYYYMMDD. Example:\n `.cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json`\n\nJSON structure:\n```json\n{\n \"scope\": {\n \"package\": \"ZTEST_LAS\",\n \"objectType\": \"DEVC\"\n },\n \"variant\": \"S4HANA_READINESS_2023\",\n \"targetRelease\": \"S/4HANA 2023\",\n \"scanDate\": \"2026-03-29\",\n \"baseline\": {\n \"totalFindings\": 23,\n \"quickWins\": 8,\n \"tableReplacements\": 7,\n \"apiReplacements\": 5,\n \"functionalRedesigns\": 2,\n \"retireCandidates\": 1\n },\n \"effortEstimate\": {\n \"rawHours\": 19.3,\n \"bufferedHours\": 23.1,\n \"personDays\": 2.9\n },\n \"findings\": [\n {\n \"id\": 1,\n \"objectName\": \"ZFI_POSTING_REPORT\",\n \"objectType\": \"PROG\",\n \"category\": \"tableReplacement\",\n \"checkTitle\": \"Use of Database Table BSEG\",\n \"messageTitle\": \"Direct SELECT on BSEG not recommended. Use ACDOCA or CDS view I_JournalEntryItem. SAP Note 2431747.\",\n \"priority\": 2,\n \"line\": 147,\n \"sapNote\": \"2431747\",\n \"successor\": \"ACDOCA\",\n \"successorSource\": \"ARS_TABLE\",\n \"autoFixable\": false\n }\n ]\n}\n```\n\nIf `.cspeach/upgrades/` directory does not exist, create it. The downstream\nskills (`/abap-upgrade-fix`, `/abap-upgrade-verify`) read this file.\n\n### Step 10 — Offer Quick Win Batch Fix\n\nAfter producing the report, count auto-fixable quick-win findings. Then ask:\n\n \"{n} quick wins can be fixed immediately (estimated {t} minutes).\n Proceed with automated fix session? [yes / no]\"\n\nIf the user answers yes: hand off to `/abap-upgrade-fix` with the quick-win\nfindings list pre-loaded. If no: close the scan session. The state file is saved\nand the user can invoke `/abap-upgrade-fix` at any time.\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\n```\n## Upgrade Readiness Report: {Package}\n### Target: S/4HANA {Release}\n### ATC Variant: {variant_name}\n### Date: {date}\n### Prepared by: {developer} / {company} — prepared with CSPeach\n\n---\n\n### Executive Summary\n\n| Category | Findings | Auto-Fixable | Person-Days |\n|-----------------------|----------|--------------|-------------|\n| Quick Wins | {n} | Yes | {d} |\n| Table Replacements | {n} | Partial | {d} |\n| API Replacements | {n} | Partial | {d} |\n| Functional Redesign | {n} | No | {d} |\n| Retire Candidates | {n} | No | {d} |\n| **TOTAL** | **{n}** | | **{d}** |\n\n**Effort Estimate:** {raw_hours}h raw + 20% buffer = **{buffered_hours}h\n({person_days} person-days)**\n\n---\n\n### Quick Wins (Auto-Fixable)\n\n| # | Object | Line | Finding | Fix Description | SAP Note |\n|---|--------|------|---------|-----------------|----------|\n| 1 | {object} | {line} | {messageTitle} | {fix} | {note} |\n\n---\n\n### Table Replacements\n\n| # | Object | Line | Deprecated Table | Successor | SAP Note |\n|---|--------|------|------------------|-----------|----------|\n| 1 | {object} | {line} | {table} | {successor} | {note} |\n\n---\n\n### API Replacements\n\n| # | Object | Line | Deprecated API | Successor | Source | SAP Note |\n|---|--------|------|----------------|-----------|--------|----------|\n| 1 | {object} | {line} | {fm_or_class} | {successor} | ARS Table / AI Knowledge | {note} |\n\n---\n\n### Functional Redesign Required\n\n| # | Object | Line | Finding | SAP Note | Effort Range |\n|---|--------|------|---------|----------|--------------|\n| 1 | {object} | {line} | {messageTitle} | {note} | {1-3 days} |\n\n*These items require business process decisions and cannot be addressed by\nautomated code fixes. Engage functional consultants before starting development.*\n\n---\n\n### Retire Candidates\n\n| # | Object | Type | Usage Count | Last Used | Action |\n|---|--------|------|-------------|-----------|--------|\n| 1 | {object} | {type} | 0 | {date_or_unknown} | Decommission after business sign-off |\n\n---\n\n### Recommended Fix Order\n\n1. **Quick Wins** — start immediately, minimal risk, free up developer bandwidth\n2. **Table Replacements** — group by table name; BSEG→ACDOCA has the most objects\n3. **API Replacements** — verify successor via ARS table before coding; test each\n4. **Functional Redesigns** — schedule after functional workshop with business team\n5. **Retires** — process separately with business owner approval\n\n---\n\n### Effort Estimate\n\n| Category | Count | Per Finding | Subtotal (h) |\n|--------------------|-------|-------------|--------------|\n| Quick wins | {n} | 5 min | {h} |\n| Table replacements | {n} | 30 min | {h} |\n| API replacements | {n} | 45 min | {h} |\n| Functional redesign | {n} | 12h avg | {h} |\n| Retire | {n} | 10 min | {h} |\n| **Subtotal** | | | **{h}** |\n| **+20% buffer** | | | **{h}** |\n| **Total** | | | **{h} ({d} person-days)** |\n\n---\n\n### Data Gaps\n\n{List any data that could not be retrieved: usage query failed, ARS table\nunavailable, variant not found. If none: omit this section.}\n\n---\n\n### State File\n\nOutputs:\n - Baseline (state file) -> `.cspeach/upgrades/{package}_{variant}_{date}.json`\n - Feeds -> `/abap-upgrade-fix` and `/abap-upgrade-verify`\n\nUse this file with `/abap-upgrade-fix` and `/abap-upgrade-verify`.\n```\n\n## Manifest Emission (MANDATORY — last block of every successful output)\n\nAfter the human-facing report, emit ONE HTML-comment manifest block so the\nCLI's save hook can persist a `.cspeach.json` envelope and the `/files`\npicker can route the artefact through `--from` chains. The block is hidden\nin human renderers but parsed by `extract-upgrade.ts` (`extractUpgradeScan`).\n\nFormat — exact key spelling matters. Indented rows under `findings:` are\nparsed as a `|`-delimited table.\n\n```\n<!-- cspeach:upgrade-manifest\nartefact: upgrade-scan\ntitle: <package or scope, e.g. ZTEST_LAS — S/4HANA 2023 readiness>\ndetail_path: <project-relative path to baseline JSON, e.g. .cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json>\natc_variant: <variant name, e.g. S4HANA_READINESS_2023>\nscope: <free-text scope summary>\ntotal: <total finding count>\nauto_fixable: <count categorised Quick Wins>\nmanual_review: <count categorised Manual / Functional / API / Table replace>\nsuppressed: <count suppressed via /abap-upgrade-suppress or pre-filter>\nfindings:\n finding-001 | <objectName> | <objectType> | <atcRule> | <severity E/W/I> | <line> | <message>\n finding-002 | <objectName> | <objectType> | <atcRule> | <severity E/W/I> | <line> | <message>\n ...\n-->\n```\n\nEach findings row is **7 columns**:\n`finding-NNN | OBJECT | TYPE | RULE | SEVERITY | LINE | MESSAGE`\n\n- **LINE** — the source line number of the finding as an integer (e.g. `88`).\n Use `-` when the finding is object-level and not tied to a specific line.\n- **MESSAGE** — a concise one-line description of the finding AND its fix\n (e.g. `Replace BAPI_BUPA_CREATE with I_BusinessPartner CDS`). It MUST contain\n **NO `|` characters** — the `|` is the column delimiter; a `|` inside the\n message would shift the columns. Keep it to one line, no newlines.\n\nExample rows:\n```\nfindings:\n finding-001 | ZFI_BP_SYNC | PROG | S4HANA_BUSINESS_PARTNER | E | 88 | Replace BAPI_BUPA_CREATE with I_BusinessPartner CDS\n finding-002 | ZCL_FI_DOC | CLAS | PERF_DB_SEARCH | W | - | Add ORDER BY PRIMARY KEY to the SELECT\n```\n\nRules:\n- `detail_path` is **project-relative** (no leading `/`). Paths are resolved\n from `process.cwd()` when read; portability across machines depends on\n this. Emit `detail_path` only AFTER the baseline JSON is confirmed\n written to disk — a manifest pointing at a never-saved file corrupts\n the fix/merge/verify chain.\n- **Counts must equal rows.** The CLI extractor cross-checks `total`\n against the number of finding rows and REJECTS the manifest on\n mismatch — the save fails loudly.\n- Keep the findings table to ≤ 50 rows even when the scan returned more —\n the manifest is a summary index, the heavy data lives at `detail_path`.\n When you cap the table, you MUST mark the truncation — directly above\n `findings:`:\n ```\n findings_truncated: true\n findings_shown: <rows actually in the table>\n ```\n (`total` stays the FULL finding count.) An unmarked cap is rejected by\n the extractor.\n- IDs MUST be `finding-001`, `finding-002`, … zero-padded to 3 digits, in\n the same order as the scan's \"Recommended Fix Order\" — `--from` chains\n rely on stable IDs.\n- When 0 findings, still emit the block with `total: 0` and an empty\n `findings:` table — the picker should see the artefact even if there's\n nothing to remediate.\n\n## Post-Save Chain Prompt (auto-route to /abap-upgrade-fix)\n\nAfter the manifest emits and the save hook fires, ask once via\n`ask_question` (kind: 'choice'):\n\n> \"Scan saved. {N} auto-fixable findings ready for remediation. Run\n> /abap-upgrade-fix next?\"\n>\n> Options:\n> - Run /abap-upgrade-fix next (start automated remediation)\n> - End turn — I'll review first\n\nOn \"Run /abap-upgrade-fix next\" — call `dispatch_skill`:\n\n```\ndispatch_skill({ command: \"/abap-upgrade-fix --from <Source-file token from prompt header>\" })\n```\n\nUse the `Source file:` filename verbatim if one was injected by `--from`\non a prior chained skill (rare for scan, which is the entry point). For\nfresh scan runs, the saved manifest filename comes from the post-save\nhook AFTER step 11 ends — same caveat as `/abap-design` step 11. In\nthat case, fall back to the hint pattern:\n\n> `→ Run \\`/files\\`, pick the just-saved scan, then choose /abap-upgrade-fix.`\n\nEnd the turn after either branch.\n\n## Guardrails\n\n- NEVER modify source code in scan mode — this skill is strictly read-only\n- NEVER mark a finding as auto-fixable if it requires business logic knowledge\n or could change runtime behavior. When in doubt, mark Manual Review.\n- NEVER skip the effort estimate section — even if it is 0 findings (produce\n a clean report, not an empty response)\n- NEVER claim a successor API without stating the source (ARS_TABLE vs AI_KNOWLEDGE).\n AI-suggested successors must be labeled \"AI-suggested — verify manually.\"\n- If ATC variant not found on system: report the error with the SAP Note from\n the mapping table. Example: \"Variant S4HANA_READINESS_2023 not found. Install\n via SAP Note 3365357 or check with Basis team. Available variants can be viewed\n in transaction ATC > Manage Check Variants.\"\n- If 0 findings: produce a clean report with congratulatory summary. Do not\n invent findings or suggest proactive fixes that ATC did not raise.\n- If ATC returns 5000+ findings: cap the displayed table at 500. Group the\n remaining by checkTitle and show a count summary. Proceed with full categorization\n and effort estimate (do not truncate the estimate).\n- If usage data query (Step 4) fails: note under Data Gaps and continue without\n usage data. Do not abort. Never claim an object is unused without query evidence.\n- If `sap_sql_query` for ARS_W_API_SCCSSR fails: fall back gracefully. Report\n \"Successor table unavailable — using AI knowledge. Confidence: Medium.\" Continue.\n- Do not chain into `/abap-upgrade-fix` without explicit user confirmation (Step 10).\n The transition is always user-initiated.\n- Always save the state file (Step 9) before presenting results — the file is the\n contract between scan, fix, and verify.\n\n## Example Prompt\n\n```\n/abap-upgrade-scan\n\nRun an upgrade scan on package ZTEST_LAS for target S/4HANA 2023.\nInclude usage data analysis.\n```\n\n## Example Output Outline\n\n```\n## Upgrade Readiness Report: ZTEST_LAS\n### Target: S/4HANA 2023\n### ATC Variant: S4HANA_READINESS_2023\n### Date: 2026-03-29\n### Prepared by: {developer} / {company} — prepared with CSPeach\n\n---\n\n### Executive Summary\n\n| Category | Findings | Auto-Fixable | Person-Days |\n|-----------------------|----------|--------------|-------------|\n| Quick Wins | 8 | Yes | 0.1 |\n| Table Replacements | 4 | Partial | 0.3 |\n| API Replacements | 3 | Partial | 0.3 |\n| Functional Redesign | 2 | No | 3.0 |\n| Retire Candidates | 1 | No | — |\n| **TOTAL** | **18** | | **~3.7** |\n\n**Effort Estimate:** 25.3h raw + 20% buffer = **30.4h (3.8 person-days)**\n\nATC run completed: 18 findings across 9 objects in package ZTEST_LAS.\n8 findings are auto-fixable quick wins (estimated 40 minutes total).\n\n---\n\n### Quick Wins (Auto-Fixable)\n\n| # | Object | Line | Finding | Fix Description | SAP Note |\n|---|--------------------|------|-------------------------------------------------------------|------------------------------------------|----------|\n| 1 | ZTEST_FI_REPORT | 43 | SELECT * without field list — select specific fields | Replace SELECT * with explicit field list | — |\n| 2 | ZTEST_FI_REPORT | 87 | Missing ORDER BY on SELECT — non-deterministic result set | Add ORDER BY PRIMARY KEY | — |\n| 3 | ZCL_ORDER_PROC | 212 | MOVE statement is obsolete | Replace with direct assignment operator | — |\n| 4 | ZCL_ORDER_PROC | 219 | COMPUTE statement is obsolete | Replace with direct assignment operator | — |\n| 5 | ZCL_ORDER_PROC | 334 | CONCATENATE is obsolete — use string template | Replace with \\|{ a }{ b }\\| template | — |\n| 6 | ZTEST_VENDOR_SYNC | 17 | INTO CORRESPONDING FIELDS used — reduces performance | Use explicit field mapping | — |\n| 7 | ZTEST_VENDOR_SYNC | 58 | Missing ORDER BY on SELECT with UP TO 1 ROWS | Add ORDER BY PRIMARY KEY | — |\n| 8 | ZFM_LEGACY_HELPER | 102 | Obsolete pseudo-comment \"#EC *\" — use ABAP pragma instead | Replace with \"#pragma suppress_class | — |\n\n---\n\n### Table Replacements\n\n| # | Object | Line | Deprecated Table | Successor | SAP Note |\n|---|------------------|------|------------------|------------------------|----------|\n| 1 | ZTEST_FI_REPORT | 56 | BSEG | ACDOCA / I_JournalEntryItem | 2431747 |\n| 2 | ZTEST_FI_REPORT | 91 | BSIS | ACDOCA + CDS view | 2431747 |\n| 3 | ZTEST_MM_GOODS | 34 | MKPF | MATDOC | — |\n| 4 | ZTEST_MM_GOODS | 35 | MSEG | MATDOC | — |\n\n*BSEG replacement requires ACDOCA field mapping and ledger filter (RLDNR = '0L').\nMKPF/MSEG replacement: use MATDOC which combines header and item. Verify material\ndocument numbering with logistics team before changing.*\n\n---\n\n### API Replacements\n\n| # | Object | Line | Deprecated API | Successor | Source | SAP Note |\n|---|-------------------|------|---------------------------------|---------------------------------|------------|----------|\n| 1 | ZTEST_FI_REPORT | 203 | FM FI_DOCUMENT_CHANGE | BAPI_ACC_DOCUMENT_POST | ARS Table | — |\n| 2 | ZTEST_VENDOR_SYNC | 78 | FM VENDOR_READ | CDS I_Supplier | ARS Table | — |\n| 3 | ZCL_ORDER_PROC | 445 | FM SD_SALESDOCUMENT_CHANGE | CL_SALESDOCUMENT=>CHANGE | AI-suggested — verify manually | 2198647 |\n\n*Finding 3 successor is AI-suggested (not in ARS_W_API_SCCSSR). Run\n`SELECT * FROM ars_w_api_sccssr WHERE subobjectname = 'SD_SALESDOCUMENT_CHANGE'`\nto confirm or refute before coding the replacement.*\n\n---\n\n### Functional Redesign Required\n\n| # | Object | Line | Finding | SAP Note | Effort Range |\n|---|------------------|------|---------------------------------------------------------------------|----------|--------------|\n| 1 | ZTEST_FI_REPORT | 312 | Output management: NAST-based output replaced by BRF+ output mgmt | 3061060 | 1-3 days |\n| 2 | ZTEST_CUST_CRED | 89 | Credit management: FD32 / KNKK direct access replaced by FSCM | 2448598 | 2-3 days |\n\n*These findings require engagement with functional consultants and business\nprocess owners before development can begin. Do not attempt code-only fixes.*\n\n---\n\n### Retire Candidates\n\n| # | Object | Type | Usage Count | Last Used | Action |\n|---|-----------------|------|-------------|-----------|-------------------------------------------|\n| 1 | ZECC_OLD_BATCH | PROG | 0 | Unknown | Decommission after business sign-off |\n\n*ZECC_OLD_BATCH has no recorded executions in /SDF/MON_RECS. Confirm with\nbusiness owner that this program is no longer needed before deleting.*\n\n---\n\n### Recommended Fix Order\n\n1. **Quick Wins** (8 findings, ~40 min) — run `/abap-upgrade-fix` for\n auto-fixable items immediately. No business input required.\n2. **Table Replacements** — BSEG→ACDOCA (findings 1-2) should be done together\n as they share the same CDS view. MKPF/MSEG→MATDOC is a separate workstream.\n3. **API Replacements** — verify ARS result for SD_SALESDOCUMENT_CHANGE first\n (finding 3) before coding. FI_DOCUMENT_CHANGE and VENDOR_READ can proceed\n immediately (ARS-confirmed successors).\n4. **Functional Redesign** — schedule a workshop on NAST→BRF+ output management\n before sprint planning. Credit management (FSCM) requires separate project.\n5. **Retire** — initiate business sign-off for ZECC_OLD_BATCH in parallel.\n\n---\n\n### Effort Estimate\n\n| Category | Count | Per Finding | Subtotal (h) |\n|---------------------|-------|-------------|--------------|\n| Quick wins | 8 | 5 min | 0.7 |\n| Table replacements | 4 | 30 min | 2.0 |\n| API replacements | 3 | 45 min | 2.3 |\n| Functional redesign | 2 | 12h avg | 24.0 |\n| Retire | 1 | 10 min | 0.2 |\n| **Subtotal** | | | **29.2h** |\n| **+20% buffer** | | | **5.8h** |\n| **Total** | | | **35.0h (4.4 person-days)** |\n\nNote: The 2 functional redesign items dominate the estimate. If these are\ndescoped (handled by a separate project), the remaining effort drops to\n11.0h (1.4 person-days).\n\n---\n\n### State File\n\nBaseline saved to: `.cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json`\n\n8 quick wins can be fixed immediately (estimated 40 minutes).\nProceed with automated fix session? [yes / no]\n```\n",
254
- "sha256": "d7be01d1bdc51be098a64d53e661b30463f6a60b4b06f86a74c102dff3c515c9",
260
+ "body": "---\r\nname: abap-upgrade-scan\r\ndescription: \"Scan custom ABAP code for S/4HANA upgrade findings. Runs ATC with readiness variant, categorizes findings, produces remediation plan with effort estimate. Use before starting upgrade remediation.\"\r\nphase: ANALYZE\r\nrequires_mcp: required\r\nforge_rules: [1, 3, 6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-upgrade-scan\r\n\r\n## Purpose\r\n\r\nAnalyze a custom code package against a target S/4HANA release by running ATC\r\nwith the corresponding readiness variant. Parse all findings, categorize them into\r\nfive actionable buckets, look up successor APIs from SAP's own ARS_W_API_SCCSSR\r\ntable, calculate a realistic effort estimate, and produce a consulting-grade\r\nUpgrade Readiness Report.\r\n\r\nThis skill is read-only and produces analysis only. It never modifies source code.\r\nThe output is the entry point for `/abap-upgrade-fix` (automated remediation) and\r\n`/abap-upgrade-verify` (sign-off report). State is persisted to\r\n`.cspeach/upgrades/` so the downstream skills can read the baseline.\r\n\r\nForge Rule 1: No assumption about findings — all categorization is driven by\r\nactual ATC output, not guesses. Forge Rule 3: No cloud readiness claim without\r\nevidence from the ARS table or system query. Forge Rule 6: This skill is\r\nread-only — it records findings, never applies changes.\r\n\r\n## When to Use\r\n\r\n- Before starting an S/4HANA upgrade project to scope custom code remediation\r\n- When a SAP Readiness Check has returned a finding count and you need details\r\n- To produce a project proposal with a defensible effort estimate for a customer\r\n- As the first step in the scan → fix → verify workflow\r\n- When moving between S/4HANA releases (e.g., 2022 → 2023 delta scan)\r\n\r\nThis skill requires an active MCP connection to SAP. The ATC variant must be\r\ninstalled on the target system. Without MCP, use `/abap-migrate` for an\r\noffline paste-mode assessment.\r\n\r\nWhen NOT to use: fixing the findings — `/abap-upgrade-fix` (step 2 of 3); per-object migration strategy (retire/replace/refactor) — `/abap-migrate`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Package scope — the ABAP package name to scan (e.g., ZTEST_LAS, ZFI_CUSTOM).\r\n Sub-packages are included automatically via DEVC scope in ATC.\r\n- Target release — the S/4HANA version you are upgrading TO.\r\n Accepted values: 2020, 2021, 2022, 2023, 2025, HANA (DB migration only),\r\n or CLOUD (clean core / cloudification).\r\n\r\nOptional:\r\n- Include usage analysis — whether to query /SDF/MON_RECS for program usage\r\n statistics. Enables identification of retire candidates. Default: ask the user.\r\n- Baseline comparison — if a prior scan JSON exists in `.cspeach/upgrades/`\r\n (or, for older projects, the legacy `.abapforge/upgrades/`), offer to compare\r\n against it (delta scan).\r\n\r\nIf any input is missing, ask before running. Do not assume defaults for package\r\nscope or target release — these are the two variables that determine everything.\r\n\r\n## ATC Variant Mapping\r\n\r\nMap the target release to the ATC variant name before calling `sap_atc_run`:\r\n\r\n| Target Release | ATC Variant Name | SAP Note |\r\n|----------------------|---------------------------|----------|\r\n| S/4HANA 2020 | S4HANA_READINESS_2020 | — |\r\n| S/4HANA 2021 | S4HANA_READINESS_2021 | — |\r\n| S/4HANA 2022 | S4HANA_READINESS_2022 | 3231748 |\r\n| S/4HANA 2023 | S4HANA_READINESS_2023 | 3365357 |\r\n| S/4HANA 2025 | S4HANA_READINESS_2025 | — |\r\n| HANA DB migration | FUNCTIONAL_DB | 1935918 |\r\n| Cloud / Clean Core | Cloudification Repository | 3284711 |\r\n\r\nIf the user specifies only the year (e.g., \"2023\"), expand to the full variant\r\nname. If the variant does not exist on the system, see the Guardrails section.\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Confirm Inputs\r\n\r\nCollect package scope and target release. If not provided in the prompt, ask:\r\n \"Which package should I scan? (e.g., ZTEST_LAS)\"\r\n \"Which S/4HANA release are you targeting? (2020 / 2021 / 2022 / 2023 / 2025 / HANA / CLOUD)\"\r\n \"Should I include usage data analysis to identify retire candidates? (yes / no)\"\r\n\r\nDo not proceed until both required inputs are confirmed.\r\n\r\n### Step 2 — Map Variant and Run ATC\r\n\r\nLook up the variant name from the mapping table above.\r\nCall `sap_atc_run` with:\r\n - objectType: DEVC\r\n - objectName: {package}\r\n - variant: {mapped_variant}\r\n\r\nIf the call succeeds, report: \"ATC run complete. {n} findings returned.\"\r\nIf the call fails, follow the error handling in Guardrails.\r\n\r\n### Step 3 — Parse All Findings\r\n\r\nFor each finding returned by `sap_atc_run`, extract:\r\n- `checkTitle` — the ATC check name (e.g., \"Use of Database Table BSEG\")\r\n- `messageTitle` — the full finding description text\r\n- `priority` — 1 (error/blocker), 2 (warning), 3 (info)\r\n- `location` — source URI (format: `/sap/bc/adt/.../source/main#start=LINE,COL`)\r\n- `line` — line number extracted from the `#start=` portion of location\r\n- `objectName` — the ABAP object (program, class, function group, etc.)\r\n- `sapNote` — 7-digit SAP Note number embedded in checkTitle or messageTitle\r\n (regex: `\\b\\d{7}\\b`). If multiple notes match, take the first.\r\n\r\nBuild a structured internal list of all findings with these fields before\r\nproceeding to categorization.\r\n\r\n### Step 4 — Optional Usage Data Query\r\n\r\nIf the user requested usage analysis, run:\r\n\r\n```sql\r\nSELECT program, dynpro, count FROM /sdf/mon_recs\r\nWHERE program LIKE 'Z%'\r\nGROUP BY program\r\nORDER BY count DESCENDING\r\n```\r\n\r\nvia `sap_sql_query`. Match results against the object names in the findings list.\r\nObjects with count = 0 or no entry in the usage data are retire candidates.\r\n\r\nIf the query fails (authorization or table not available), note it in the report\r\nunder \"Data Gaps\" and continue without usage data. Do not abort the scan.\r\n\r\n### Step 5 — Look Up Successor APIs\r\n\r\nFor each finding that mentions a deprecated function module, class method, or\r\nBAPI in `messageTitle`, extract the deprecated object name and query:\r\n\r\n```sql\r\nSELECT * FROM ars_w_api_sccssr WHERE subobjectname = '<deprecated_object_name>'\r\n```\r\n\r\nvia `sap_sql_query`. The result gives the SAP-provided successor API.\r\n\r\nIf the query returns a result: mark the finding with `successor_source: ARS_TABLE`\r\nand record the successor name. Confidence: High.\r\n\r\nIf the query returns no result (object not in table): mark as `successor_source: AI_KNOWLEDGE`\r\nand use your ABAP knowledge to propose the successor. Confidence: Medium.\r\nIf unsure: mark as `successor_source: UNKNOWN` and recommend manual investigation.\r\n\r\nIf the system has no ARS_W_API_SCCSSR table (ECC system), note \"lower confidence —\r\nno successor table available\" for all API findings. Fall back to AI knowledge\r\nand `sap_search_object` as needed.\r\n\r\n### Step 6 — Categorize All Findings\r\n\r\nAssign each finding to exactly one category:\r\n\r\n**Quick Wins** — auto-fixable syntactic changes with no behavior risk:\r\n- checkTitle or messageTitle contains: \"ORDER BY\", \"SELECT *\", \"INTO CORRESPONDING FIELDS\",\r\n \"MOVE statement\", \"COMPUTE statement\", \"CONCATENATE\", \"pseudo-comment\", \"pragma\",\r\n \"obsolete statement\", \"SLIN\"\r\n- These are deterministic fixes — no business logic knowledge required.\r\n\r\n**Table Replacements** — direct table access to tables removed or redirected in S/4HANA:\r\n- messageTitle mentions any of: BSEG, BSIS, BSAS, BSIK, BSID, MKPF, MSEG, VBUK, VBUP,\r\n KONV, COEP, FAGLFLEXA\r\n- Reference table for successor mapping:\r\n\r\n| Deprecated Table | Successor | Area | SAP Note |\r\n|-----------------|------------------------|-----------------|----------|\r\n| BSEG | ACDOCA | FI Line Items | 2431747 |\r\n| BSIS / BSAS | ACDOCA + CDS view | FI Sec. Indexes | 2431747 |\r\n| BSIK / BSID | ACDOCA + CDS view | FI Sec. Indexes | 2431747 |\r\n| MKPF / MSEG | MATDOC | Mat. Documents | — |\r\n| VBUK | VBAK fields (status) | SD Hdr Status | 2198647 |\r\n| VBUP | VBAP fields (status) | SD Item Status | 2198647 |\r\n| KONV | PRCD_ELEMENTS | Pricing Cond. | 2220005 |\r\n| COEP | ACDOCA | CO Line Items | — |\r\n\r\n**API Replacements** — deprecated function modules, BAPIs, or class methods:\r\n- messageTitle mentions a deprecated FM, BAPI, or class and a successor exists\r\n (either from ARS_W_API_SCCSSR or AI knowledge)\r\n- Lookup result from Step 5 determines confidence level\r\n\r\n**Functional Redesign** — changes requiring business process decisions:\r\n- messageTitle mentions: output management, credit management, material ledger,\r\n business partner (BP migration), customer/vendor integration (CVI), tax engine,\r\n profit center accounting, new asset accounting\r\n- These findings cannot be addressed with code-only fixes. Business sign-off\r\n and process redesign are required. Mark with SAP Note and do not flag as auto-fixable.\r\n\r\n**Retire Candidates** — objects with confirmed zero usage:\r\n- Object has count = 0 in usage data query result, OR\r\n- Object is a wrapper for a deprecated transaction that no longer exists in S/4HANA\r\n- Retirement requires business sign-off — note it in the report but do not suggest\r\n deletion.\r\n\r\n### Step 7 — Calculate Effort Estimate\r\n\r\nApply the following per-finding estimates:\r\n\r\n| Category | Estimate Per Finding | Basis |\r\n|---------------------|---------------------|---------------------------------------------|\r\n| Quick win | 5 minutes | Deterministic fix, no business logic |\r\n| Table replacement | 30 minutes | Field mapping + ledger filter + unit test |\r\n| API replacement | 45 minutes | Interface mapping + parameter changes |\r\n| Functional redesign | 1.5 days (avg) | Use 1 day min, 3 days max per item |\r\n| Retire | 10 minutes | Verify no usage + delete (excl. sign-off) |\r\n\r\nFormula:\r\n Raw hours = (quick_wins × 5/60) + (table_reps × 0.5) + (api_reps × 0.75)\r\n + (redesigns × 12) + (retires × 10/60)\r\n Buffered hours = raw_hours × 1.20 (20% buffer for testing and coordination)\r\n Person-days = buffered_hours / 8\r\n\r\nShow the breakdown per category and the final total. Never skip the effort estimate.\r\n\r\nIf there are 0 findings: the estimate is 0. Do not invent effort.\r\n\r\n### Step 8 — Produce the Upgrade Readiness Report\r\n\r\nWrite the full report in the Output Structure format defined below.\r\n\r\n### Step 9 — Save Baseline to State File\r\n\r\nSave the scan results to `.cspeach/upgrades/{package}_{variant}_{date}.json`\r\nusing the Write tool. Date format: YYYYMMDD. Example:\r\n `.cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json`\r\n\r\nJSON structure:\r\n```json\r\n{\r\n \"scope\": {\r\n \"package\": \"ZTEST_LAS\",\r\n \"objectType\": \"DEVC\"\r\n },\r\n \"variant\": \"S4HANA_READINESS_2023\",\r\n \"targetRelease\": \"S/4HANA 2023\",\r\n \"scanDate\": \"2026-03-29\",\r\n \"baseline\": {\r\n \"totalFindings\": 23,\r\n \"quickWins\": 8,\r\n \"tableReplacements\": 7,\r\n \"apiReplacements\": 5,\r\n \"functionalRedesigns\": 2,\r\n \"retireCandidates\": 1\r\n },\r\n \"effortEstimate\": {\r\n \"rawHours\": 19.3,\r\n \"bufferedHours\": 23.1,\r\n \"personDays\": 2.9\r\n },\r\n \"findings\": [\r\n {\r\n \"id\": 1,\r\n \"objectName\": \"ZFI_POSTING_REPORT\",\r\n \"objectType\": \"PROG\",\r\n \"category\": \"tableReplacement\",\r\n \"checkTitle\": \"Use of Database Table BSEG\",\r\n \"messageTitle\": \"Direct SELECT on BSEG not recommended. Use ACDOCA or CDS view I_JournalEntryItem. SAP Note 2431747.\",\r\n \"priority\": 2,\r\n \"line\": 147,\r\n \"sapNote\": \"2431747\",\r\n \"successor\": \"ACDOCA\",\r\n \"successorSource\": \"ARS_TABLE\",\r\n \"autoFixable\": false\r\n }\r\n ]\r\n}\r\n```\r\n\r\nIf `.cspeach/upgrades/` directory does not exist, create it. The downstream\r\nskills (`/abap-upgrade-fix`, `/abap-upgrade-verify`) read this file.\r\n\r\n### Step 10 — Offer Quick Win Batch Fix\r\n\r\nAfter producing the report, count auto-fixable quick-win findings. Then ask:\r\n\r\n \"{n} quick wins can be fixed immediately (estimated {t} minutes).\r\n Proceed with automated fix session? [yes / no]\"\r\n\r\nIf the user answers yes: hand off to `/abap-upgrade-fix` with the quick-win\r\nfindings list pre-loaded. If no: close the scan session. The state file is saved\r\nand the user can invoke `/abap-upgrade-fix` at any time.\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Upgrade Readiness Report: {Package}\r\n### Target: S/4HANA {Release}\r\n### ATC Variant: {variant_name}\r\n### Date: {date}\r\n### Prepared by: {developer} / {company} — prepared with CSPeach\r\n\r\n---\r\n\r\n### Executive Summary\r\n\r\n| Category | Findings | Auto-Fixable | Person-Days |\r\n|-----------------------|----------|--------------|-------------|\r\n| Quick Wins | {n} | Yes | {d} |\r\n| Table Replacements | {n} | Partial | {d} |\r\n| API Replacements | {n} | Partial | {d} |\r\n| Functional Redesign | {n} | No | {d} |\r\n| Retire Candidates | {n} | No | {d} |\r\n| **TOTAL** | **{n}** | | **{d}** |\r\n\r\n**Effort Estimate:** {raw_hours}h raw + 20% buffer = **{buffered_hours}h\r\n({person_days} person-days)**\r\n\r\n---\r\n\r\n### Quick Wins (Auto-Fixable)\r\n\r\n| # | Object | Line | Finding | Fix Description | SAP Note |\r\n|---|--------|------|---------|-----------------|----------|\r\n| 1 | {object} | {line} | {messageTitle} | {fix} | {note} |\r\n\r\n---\r\n\r\n### Table Replacements\r\n\r\n| # | Object | Line | Deprecated Table | Successor | SAP Note |\r\n|---|--------|------|------------------|-----------|----------|\r\n| 1 | {object} | {line} | {table} | {successor} | {note} |\r\n\r\n---\r\n\r\n### API Replacements\r\n\r\n| # | Object | Line | Deprecated API | Successor | Source | SAP Note |\r\n|---|--------|------|----------------|-----------|--------|----------|\r\n| 1 | {object} | {line} | {fm_or_class} | {successor} | ARS Table / AI Knowledge | {note} |\r\n\r\n---\r\n\r\n### Functional Redesign Required\r\n\r\n| # | Object | Line | Finding | SAP Note | Effort Range |\r\n|---|--------|------|---------|----------|--------------|\r\n| 1 | {object} | {line} | {messageTitle} | {note} | {1-3 days} |\r\n\r\n*These items require business process decisions and cannot be addressed by\r\nautomated code fixes. Engage functional consultants before starting development.*\r\n\r\n---\r\n\r\n### Retire Candidates\r\n\r\n| # | Object | Type | Usage Count | Last Used | Action |\r\n|---|--------|------|-------------|-----------|--------|\r\n| 1 | {object} | {type} | 0 | {date_or_unknown} | Decommission after business sign-off |\r\n\r\n---\r\n\r\n### Recommended Fix Order\r\n\r\n1. **Quick Wins** — start immediately, minimal risk, free up developer bandwidth\r\n2. **Table Replacements** — group by table name; BSEG→ACDOCA has the most objects\r\n3. **API Replacements** — verify successor via ARS table before coding; test each\r\n4. **Functional Redesigns** — schedule after functional workshop with business team\r\n5. **Retires** — process separately with business owner approval\r\n\r\n---\r\n\r\n### Effort Estimate\r\n\r\n| Category | Count | Per Finding | Subtotal (h) |\r\n|--------------------|-------|-------------|--------------|\r\n| Quick wins | {n} | 5 min | {h} |\r\n| Table replacements | {n} | 30 min | {h} |\r\n| API replacements | {n} | 45 min | {h} |\r\n| Functional redesign | {n} | 12h avg | {h} |\r\n| Retire | {n} | 10 min | {h} |\r\n| **Subtotal** | | | **{h}** |\r\n| **+20% buffer** | | | **{h}** |\r\n| **Total** | | | **{h} ({d} person-days)** |\r\n\r\n---\r\n\r\n### Data Gaps\r\n\r\n{List any data that could not be retrieved: usage query failed, ARS table\r\nunavailable, variant not found. If none: omit this section.}\r\n\r\n---\r\n\r\n### State File\r\n\r\nOutputs:\r\n - Baseline (state file) -> `.cspeach/upgrades/{package}_{variant}_{date}.json`\r\n - Feeds -> `/abap-upgrade-fix` and `/abap-upgrade-verify`\r\n\r\nUse this file with `/abap-upgrade-fix` and `/abap-upgrade-verify`.\r\n```\r\n\r\n## Manifest Emission (MANDATORY — last block of every successful output)\r\n\r\nAfter the human-facing report, emit ONE HTML-comment manifest block so the\r\nCLI's save hook can persist a `.cspeach.json` envelope and the `/files`\r\npicker can route the artefact through `--from` chains. The block is hidden\r\nin human renderers but parsed by `extract-upgrade.ts` (`extractUpgradeScan`).\r\n\r\nFormat — exact key spelling matters. Indented rows under `findings:` are\r\nparsed as a `|`-delimited table.\r\n\r\n```\r\n<!-- cspeach:upgrade-manifest\r\nartefact: upgrade-scan\r\ntitle: <package or scope, e.g. ZTEST_LAS — S/4HANA 2023 readiness>\r\ndetail_path: <project-relative path to baseline JSON, e.g. .cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json>\r\natc_variant: <variant name, e.g. S4HANA_READINESS_2023>\r\nscope: <free-text scope summary>\r\ntotal: <total finding count>\r\nauto_fixable: <count categorised Quick Wins>\r\nmanual_review: <count categorised Manual / Functional / API / Table replace>\r\nsuppressed: <count suppressed via /abap-upgrade-suppress or pre-filter>\r\nfindings:\r\n finding-001 | <objectName> | <objectType> | <atcRule> | <severity E/W/I> | <line> | <message>\r\n finding-002 | <objectName> | <objectType> | <atcRule> | <severity E/W/I> | <line> | <message>\r\n ...\r\n-->\r\n```\r\n\r\nEach findings row is **7 columns**:\r\n`finding-NNN | OBJECT | TYPE | RULE | SEVERITY | LINE | MESSAGE`\r\n\r\n- **LINE** — the source line number of the finding as an integer (e.g. `88`).\r\n Use `-` when the finding is object-level and not tied to a specific line.\r\n- **MESSAGE** — a concise one-line description of the finding AND its fix\r\n (e.g. `Replace BAPI_BUPA_CREATE with I_BusinessPartner CDS`). It MUST contain\r\n **NO `|` characters** — the `|` is the column delimiter; a `|` inside the\r\n message would shift the columns. Keep it to one line, no newlines.\r\n\r\nExample rows:\r\n```\r\nfindings:\r\n finding-001 | ZFI_BP_SYNC | PROG | S4HANA_BUSINESS_PARTNER | E | 88 | Replace BAPI_BUPA_CREATE with I_BusinessPartner CDS\r\n finding-002 | ZCL_FI_DOC | CLAS | PERF_DB_SEARCH | W | - | Add ORDER BY PRIMARY KEY to the SELECT\r\n```\r\n\r\nRules:\r\n- `detail_path` is **project-relative** (no leading `/`). Paths are resolved\r\n from `process.cwd()` when read; portability across machines depends on\r\n this. Emit `detail_path` only AFTER the baseline JSON is confirmed\r\n written to disk — a manifest pointing at a never-saved file corrupts\r\n the fix/merge/verify chain.\r\n- **Counts must equal rows.** The CLI extractor cross-checks `total`\r\n against the number of finding rows and REJECTS the manifest on\r\n mismatch — the save fails loudly.\r\n- Keep the findings table to ≤ 50 rows even when the scan returned more —\r\n the manifest is a summary index, the heavy data lives at `detail_path`.\r\n When you cap the table, you MUST mark the truncation — directly above\r\n `findings:`:\r\n ```\r\n findings_truncated: true\r\n findings_shown: <rows actually in the table>\r\n ```\r\n (`total` stays the FULL finding count.) An unmarked cap is rejected by\r\n the extractor.\r\n- IDs MUST be `finding-001`, `finding-002`, … zero-padded to 3 digits, in\r\n the same order as the scan's \"Recommended Fix Order\" — `--from` chains\r\n rely on stable IDs.\r\n- When 0 findings, still emit the block with `total: 0` and an empty\r\n `findings:` table — the picker should see the artefact even if there's\r\n nothing to remediate.\r\n\r\n## Post-Save Chain Prompt (auto-route to /abap-upgrade-fix)\r\n\r\nAfter the manifest emits and the save hook fires, ask once via\r\n`ask_question` (kind: 'choice'):\r\n\r\n> \"Scan saved. {N} auto-fixable findings ready for remediation. Run\r\n> /abap-upgrade-fix next?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-fix next (start automated remediation)\r\n> - End turn — I'll review first\r\n\r\nOn \"Run /abap-upgrade-fix next\" — call `dispatch_skill`:\r\n\r\n```\r\ndispatch_skill({ command: \"/abap-upgrade-fix --from <Source-file token from prompt header>\" })\r\n```\r\n\r\nUse the `Source file:` filename verbatim if one was injected by `--from`\r\non a prior chained skill (rare for scan, which is the entry point). For\r\nfresh scan runs, the saved manifest filename comes from the post-save\r\nhook AFTER step 11 ends — same caveat as `/abap-design` step 11. In\r\nthat case, fall back to the hint pattern:\r\n\r\n> `→ Run \\`/files\\`, pick the just-saved scan, then choose /abap-upgrade-fix.`\r\n\r\nEnd the turn after either branch.\r\n\r\n## Guardrails\r\n\r\n- NEVER modify source code in scan mode — this skill is strictly read-only\r\n- NEVER mark a finding as auto-fixable if it requires business logic knowledge\r\n or could change runtime behavior. When in doubt, mark Manual Review.\r\n- NEVER skip the effort estimate section — even if it is 0 findings (produce\r\n a clean report, not an empty response)\r\n- NEVER claim a successor API without stating the source (ARS_TABLE vs AI_KNOWLEDGE).\r\n AI-suggested successors must be labeled \"AI-suggested — verify manually.\"\r\n- If ATC variant not found on system: report the error with the SAP Note from\r\n the mapping table. Example: \"Variant S4HANA_READINESS_2023 not found. Install\r\n via SAP Note 3365357 or check with Basis team. Available variants can be viewed\r\n in transaction ATC > Manage Check Variants.\"\r\n- If 0 findings: produce a clean report with congratulatory summary. Do not\r\n invent findings or suggest proactive fixes that ATC did not raise.\r\n- If ATC returns 5000+ findings: cap the displayed table at 500. Group the\r\n remaining by checkTitle and show a count summary. Proceed with full categorization\r\n and effort estimate (do not truncate the estimate).\r\n- If usage data query (Step 4) fails: note under Data Gaps and continue without\r\n usage data. Do not abort. Never claim an object is unused without query evidence.\r\n- If `sap_sql_query` for ARS_W_API_SCCSSR fails: fall back gracefully. Report\r\n \"Successor table unavailable — using AI knowledge. Confidence: Medium.\" Continue.\r\n- Do not chain into `/abap-upgrade-fix` without explicit user confirmation (Step 10).\r\n The transition is always user-initiated.\r\n- Always save the state file (Step 9) before presenting results — the file is the\r\n contract between scan, fix, and verify.\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-upgrade-scan\r\n\r\nRun an upgrade scan on package ZTEST_LAS for target S/4HANA 2023.\r\nInclude usage data analysis.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Upgrade Readiness Report: ZTEST_LAS\r\n### Target: S/4HANA 2023\r\n### ATC Variant: S4HANA_READINESS_2023\r\n### Date: 2026-03-29\r\n### Prepared by: {developer} / {company} — prepared with CSPeach\r\n\r\n---\r\n\r\n### Executive Summary\r\n\r\n| Category | Findings | Auto-Fixable | Person-Days |\r\n|-----------------------|----------|--------------|-------------|\r\n| Quick Wins | 8 | Yes | 0.1 |\r\n| Table Replacements | 4 | Partial | 0.3 |\r\n| API Replacements | 3 | Partial | 0.3 |\r\n| Functional Redesign | 2 | No | 3.0 |\r\n| Retire Candidates | 1 | No | — |\r\n| **TOTAL** | **18** | | **~3.7** |\r\n\r\n**Effort Estimate:** 25.3h raw + 20% buffer = **30.4h (3.8 person-days)**\r\n\r\nATC run completed: 18 findings across 9 objects in package ZTEST_LAS.\r\n8 findings are auto-fixable quick wins (estimated 40 minutes total).\r\n\r\n---\r\n\r\n### Quick Wins (Auto-Fixable)\r\n\r\n| # | Object | Line | Finding | Fix Description | SAP Note |\r\n|---|--------------------|------|-------------------------------------------------------------|------------------------------------------|----------|\r\n| 1 | ZTEST_FI_REPORT | 43 | SELECT * without field list — select specific fields | Replace SELECT * with explicit field list | — |\r\n| 2 | ZTEST_FI_REPORT | 87 | Missing ORDER BY on SELECT — non-deterministic result set | Add ORDER BY PRIMARY KEY | — |\r\n| 3 | ZCL_ORDER_PROC | 212 | MOVE statement is obsolete | Replace with direct assignment operator | — |\r\n| 4 | ZCL_ORDER_PROC | 219 | COMPUTE statement is obsolete | Replace with direct assignment operator | — |\r\n| 5 | ZCL_ORDER_PROC | 334 | CONCATENATE is obsolete — use string template | Replace with \\|{ a }{ b }\\| template | — |\r\n| 6 | ZTEST_VENDOR_SYNC | 17 | INTO CORRESPONDING FIELDS used — reduces performance | Use explicit field mapping | — |\r\n| 7 | ZTEST_VENDOR_SYNC | 58 | Missing ORDER BY on SELECT with UP TO 1 ROWS | Add ORDER BY PRIMARY KEY | — |\r\n| 8 | ZFM_LEGACY_HELPER | 102 | Obsolete pseudo-comment \"#EC *\" — use ABAP pragma instead | Replace with \"#pragma suppress_class | — |\r\n\r\n---\r\n\r\n### Table Replacements\r\n\r\n| # | Object | Line | Deprecated Table | Successor | SAP Note |\r\n|---|------------------|------|------------------|------------------------|----------|\r\n| 1 | ZTEST_FI_REPORT | 56 | BSEG | ACDOCA / I_JournalEntryItem | 2431747 |\r\n| 2 | ZTEST_FI_REPORT | 91 | BSIS | ACDOCA + CDS view | 2431747 |\r\n| 3 | ZTEST_MM_GOODS | 34 | MKPF | MATDOC | — |\r\n| 4 | ZTEST_MM_GOODS | 35 | MSEG | MATDOC | — |\r\n\r\n*BSEG replacement requires ACDOCA field mapping and ledger filter (RLDNR = '0L').\r\nMKPF/MSEG replacement: use MATDOC which combines header and item. Verify material\r\ndocument numbering with logistics team before changing.*\r\n\r\n---\r\n\r\n### API Replacements\r\n\r\n| # | Object | Line | Deprecated API | Successor | Source | SAP Note |\r\n|---|-------------------|------|---------------------------------|---------------------------------|------------|----------|\r\n| 1 | ZTEST_FI_REPORT | 203 | FM FI_DOCUMENT_CHANGE | BAPI_ACC_DOCUMENT_POST | ARS Table | — |\r\n| 2 | ZTEST_VENDOR_SYNC | 78 | FM VENDOR_READ | CDS I_Supplier | ARS Table | — |\r\n| 3 | ZCL_ORDER_PROC | 445 | FM SD_SALESDOCUMENT_CHANGE | CL_SALESDOCUMENT=>CHANGE | AI-suggested — verify manually | 2198647 |\r\n\r\n*Finding 3 successor is AI-suggested (not in ARS_W_API_SCCSSR). Run\r\n`SELECT * FROM ars_w_api_sccssr WHERE subobjectname = 'SD_SALESDOCUMENT_CHANGE'`\r\nto confirm or refute before coding the replacement.*\r\n\r\n---\r\n\r\n### Functional Redesign Required\r\n\r\n| # | Object | Line | Finding | SAP Note | Effort Range |\r\n|---|------------------|------|---------------------------------------------------------------------|----------|--------------|\r\n| 1 | ZTEST_FI_REPORT | 312 | Output management: NAST-based output replaced by BRF+ output mgmt | 3061060 | 1-3 days |\r\n| 2 | ZTEST_CUST_CRED | 89 | Credit management: FD32 / KNKK direct access replaced by FSCM | 2448598 | 2-3 days |\r\n\r\n*These findings require engagement with functional consultants and business\r\nprocess owners before development can begin. Do not attempt code-only fixes.*\r\n\r\n---\r\n\r\n### Retire Candidates\r\n\r\n| # | Object | Type | Usage Count | Last Used | Action |\r\n|---|-----------------|------|-------------|-----------|-------------------------------------------|\r\n| 1 | ZECC_OLD_BATCH | PROG | 0 | Unknown | Decommission after business sign-off |\r\n\r\n*ZECC_OLD_BATCH has no recorded executions in /SDF/MON_RECS. Confirm with\r\nbusiness owner that this program is no longer needed before deleting.*\r\n\r\n---\r\n\r\n### Recommended Fix Order\r\n\r\n1. **Quick Wins** (8 findings, ~40 min) — run `/abap-upgrade-fix` for\r\n auto-fixable items immediately. No business input required.\r\n2. **Table Replacements** — BSEG→ACDOCA (findings 1-2) should be done together\r\n as they share the same CDS view. MKPF/MSEG→MATDOC is a separate workstream.\r\n3. **API Replacements** — verify ARS result for SD_SALESDOCUMENT_CHANGE first\r\n (finding 3) before coding. FI_DOCUMENT_CHANGE and VENDOR_READ can proceed\r\n immediately (ARS-confirmed successors).\r\n4. **Functional Redesign** — schedule a workshop on NAST→BRF+ output management\r\n before sprint planning. Credit management (FSCM) requires separate project.\r\n5. **Retire** — initiate business sign-off for ZECC_OLD_BATCH in parallel.\r\n\r\n---\r\n\r\n### Effort Estimate\r\n\r\n| Category | Count | Per Finding | Subtotal (h) |\r\n|---------------------|-------|-------------|--------------|\r\n| Quick wins | 8 | 5 min | 0.7 |\r\n| Table replacements | 4 | 30 min | 2.0 |\r\n| API replacements | 3 | 45 min | 2.3 |\r\n| Functional redesign | 2 | 12h avg | 24.0 |\r\n| Retire | 1 | 10 min | 0.2 |\r\n| **Subtotal** | | | **29.2h** |\r\n| **+20% buffer** | | | **5.8h** |\r\n| **Total** | | | **35.0h (4.4 person-days)** |\r\n\r\nNote: The 2 functional redesign items dominate the estimate. If these are\r\ndescoped (handled by a separate project), the remaining effort drops to\r\n11.0h (1.4 person-days).\r\n\r\n---\r\n\r\n### State File\r\n\r\nBaseline saved to: `.cspeach/upgrades/ZTEST_LAS_S4HANA_READINESS_2023_20260329.json`\r\n\r\n8 quick wins can be fixed immediately (estimated 40 minutes).\r\nProceed with automated fix session? [yes / no]\r\n```\r\n",
261
+ "sha256": "67b351ecbb2789ee3cc262cab652cea8396b798994574946b279be913c6c42c5",
255
262
  "signature": "",
256
- "signedAt": "2026-09-17T20:19:05.962Z"
263
+ "signedAt": "2026-09-28T14:37:05.254Z"
257
264
  },
258
265
  "abap-upgrade-verify": {
259
266
  "name": "abap-upgrade-verify",
260
- "body": "---\nname: abap-upgrade-verify\ndescription: >\n Verify upgrade remediation results. Re-runs ATC with the same variant and\n scope as the original scan, compares findings against the baseline, flags\n regressions prominently, and generates a formal Upgrade Remediation Report\n suitable for project sign-off and customer delivery.\nphase: VERIFY\nrequires_mcp: required\nforge_rules: [6]\nversion: \"1.0\"\n---\n\n# abap-upgrade-verify\n\n## Purpose\n\nGenerate a formal, consulting-grade Upgrade Remediation Report after upgrade\nremediation work is complete. The skill re-runs ATC against the same package\nscope and variant used in the original `/abap-upgrade-scan`, compares the new\nfindings against the scan baseline, and produces a structured document covering\nexecutive summary, per-object change history, remaining findings, transport\nchain, and a sign-off block.\n\nThis skill is read-only. It never modifies source code or system data.\nIts output is a deliverable — structured for inclusion in project documentation,\ncustomer handover packages, or SAP upgrade project sign-off gates.\n\nFor upgrade-related upgrades also see:\n- `/abap-upgrade-scan` — runs the initial ATC scan and produces the remediation plan\n- `/abap-upgrade-fix` — interactive object-by-object remediation with developer approval\n\n## When to Use\n\n- After `/abap-upgrade-fix` completes and the developer confirms all objects\n are fixed, skipped, or deferred\n- Before presenting results to a customer or project steering committee\n- As a formal closure gate for an upgrade remediation engagement\n- When a re-scan is needed to confirm a specific set of objects is clean\n- To generate a regression check after additional changes were made\n\nWhen NOT to use: fixes not finished yet — `/abap-upgrade-fix`; consolidating parallel team slices first — `/abap-upgrade-merge`.\n\n## Inputs Expected\n\nRequired:\n- Package scope (same as used in original scan, e.g. ZTEST_LAS or ZFICO_*)\n- ATC variant (same as original scan, e.g. S4HANA_READINESS_2023)\n\nAutomatically sourced (if the state file exists):\n- Baseline finding count and object list — from `.cspeach/upgrades/{package}_{variant}_{date}.json`\n- Fix session results (per-object change log) — from the same state file\n- Transport numbers — from the fix session state\n\nIf no state file is found, ask the user for:\n- Baseline finding count (before remediation)\n- List of objects that were modified (with transport numbers)\n- Whether any objects were retired\n\nOptional:\n- Customer name (for report header)\n- Prepared-by name (defaults to current SAP logon user)\n- Whether to include the full Changes Made section or a summary only\n\n## Required Behavior\n\n### Step 1 — Load Baseline\n\nRead `.cspeach/upgrades/{package}_{variant}_{date}.json` using the Read tool;\nif absent, fall back to the legacy `.abapforge/upgrades/` location (older\nprojects). If multiple files match (multiple scan dates), use the most recent.\n\nExtract from the state file:\n- `baseline.totalFindings` — total findings before remediation\n- `baseline.objectCount` — number of objects with findings\n- `baseline.findingsByObject` — map of objectName → finding list\n- `fixSession.objectsModified` — list of objects that were changed\n- `fixSession.changesPerObject` — per-object log of what was fixed\n- `fixSession.transports` — transport numbers used\n- `fixSession.objectsSkipped` — objects not remediated with reasons\n- `fixSession.objectsRetired` — objects decommissioned\n\nIf the state file is missing or incomplete, note which comparisons are\napproximate and proceed with the data available. Do not abort.\n\n### Step 2 — Re-Run ATC\n\nCall `sap_atc_run` with:\n- objectType: DEVC\n- objectName: {package scope}\n- variant: {same variant as original scan}\n\nParse the new findings. For each finding extract:\n- objectName, objectType, checkTitle, messageTitle, priority, location, line\n\nBuild a map: newFindingsByObject → finding list per object.\n\n### Step 3 — Compare Against Baseline\n\nCompute the three comparison sets:\n\n**Resolved findings:** Present in baseline, not present in new scan.\nMatch by: objectName + checkTitle + messageTitle (trim whitespace).\nIf location/line is available, also use it as a secondary match.\n\n**Remaining findings:** Present in both baseline and new scan.\n\n**Regression findings (NEW):** Present in new scan but NOT in baseline.\nThese are findings that did not exist before remediation started.\n\nReport the counts: resolved={n}, remaining={n}, regressions={n}.\n\nIf regressions > 0, flag them with a prominent \"REGRESSION\" label throughout\nthe report. Regressions indicate that a fix introduced a new ATC violation.\n\n### Step 4 — Query System Information\n\nQuery system info for the report header using `sap_sql_query`:\n\n```sql\nSELECT sysid, mandt, saprl, host FROM t000 WHERE mandt = sy-mandt\n```\n\nIf that query fails or returns no rows, fall back to:\n\n```sql\nSELECT * FROM t000 WHERE mandt = '100'\n```\n\nExtract: SID (SYSID), Client (MANDT), SAP Release (SAPRL), Host.\nUse these in the report header.\n\nIf the SQL query is unavailable, note \"System info unavailable — add manually\"\nin the header and continue.\n\n### Step 5 — Generate the Formal Upgrade Remediation Report\n\nProduce the report as structured Markdown following the Output Structure below.\nUse actual data from steps 1–4. Do not omit any section — include every section\neven if it has no entries (use \"None\" or \"N/A\" as appropriate).\n\nAfter generating the report, state:\n\n> This report can be exported and used as a project sign-off document.\n> Copy the Markdown to a Word or PDF template, or attach the raw `.md` file\n> to your project documentation system.\n\n### Step 6 — Regression Handling\n\nIf regressions were found:\n\n1. List each regression with: object, check, message, line, priority\n2. Label each with **[REGRESSION]** in bold\n3. In the Executive Summary, flag: \"X regression findings introduced — review required\"\n4. Do NOT mark the engagement as complete — state that regressions must be\n investigated before sign-off\n5. Offer: \"Run `/abap-upgrade-fix` to address the regressions, then re-run\n `/abap-upgrade-verify`.\"\n\n\n## TL;DR (MANDATORY — must be the first section of every output)\n\n<!-- csforge:tldr-required -->\n\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\n\n```markdown\n## [Skill Name] — [Target Object/Package/Context]\n\n## TL;DR\n\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\n\n**Top 3 (priority order — most important first):**\n1. <highest-priority finding, decision, or action>\n2. <second most important>\n3. <third most important>\n\n**Verdict:** <1-line overall conclusion or next-step recommendation>\n\n---\n\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\n```\n\n**Rules for the Top 3 list:**\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\n\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\n\n\n## Output Structure\n\n```\n## Upgrade Remediation Report\n\n**Customer:** {customer_name}\n**System:** {SID} / Client {client}\n**SAP Release:** {saprl}\n**Target:** S/4HANA {target_release}\n**Package Scope:** {package}\n**ATC Variant:** {variant}\n**Prepared by:** {developer} / {company}\n**Report Date:** {date}\n\n---\n\n## Executive Summary\n\n| Metric | Value |\n|--------|-------|\n| Findings Before Remediation | {baseline_count} across {object_count} objects |\n| Findings After Remediation | {new_count} across {new_object_count} objects |\n| Findings Resolved | {resolved_count} ({resolved_pct}%) |\n| Findings Remaining | {remaining_count} |\n| Regression Findings (NEW) | {regression_count} |\n\n{If regressions > 0:}\n> **[REGRESSION ALERT]** {regression_count} new finding(s) were introduced\n> during remediation. These must be resolved before sign-off. See Remaining\n> Findings section.\n\n**Remediation Status:** {Complete — all findings resolved | Partial — {n} findings remain | BLOCKED — regressions detected}\n\n---\n\n## Objects Modified\n\n| Object | Type | Findings Fixed | Transport | Status |\n|--------|------|---------------|-----------|--------|\n| {object} | {PROG/CLAS/FUGR/etc} | {n} | {transport_number} | Fixed |\n\n---\n\n## Changes Made\n\n### {Object_Name} ({Object_Type})\n\n**Transport:** {transport_number}\n**Findings Fixed:** {n}\n\n| Line | Finding | Change Made | SAP Note |\n|------|---------|-------------|----------|\n| {line} | {checkTitle}: {messageTitle} | {description_of_fix} | {note_number} |\n\n---\n\n## Remaining Findings\n\n| Object | Type | Priority | Finding | Reason Not Fixed |\n|--------|------|----------|---------|-----------------|\n| {object} | {type} | {1/2/3} | {checkTitle}: {messageTitle} | {reason} |\n\n{If any remaining finding was introduced by the fix session (regression):}\n| {object} | {type} | {priority} | **[REGRESSION]** {checkTitle}: {messageTitle} | New finding — not in original baseline |\n\n---\n\n## Retired Objects\n\n| Object | Type | Last Usage | Approved By | Action |\n|--------|------|-----------|-------------|--------|\n| {object} | {type} | {date or Never} | {approver} | Deleted / Transport: {transport} |\n\n---\n\n## Transport Summary\n\n| Transport | Description | Objects Included | Status |\n|-----------|-------------|-----------------|--------|\n| {transport_number} | {description} | {object_list} | Released / Open |\n\n---\n\n## Sign-Off\n\n**Remediation performed by:** _____________________________ Date: ___________\n\n**Reviewed and approved by:** _____________________________ Date: ___________\n\n**Customer representative:** _____________________________ Date: ___________\n\n*This report documents the results of ABAP upgrade remediation performed on\n{system} / Client {client} for target release S/4HANA {target_release}.\nPrepared by {company} with CSPeach.*\n\nOutputs:\n - Sign-off report (detail) -> .cspeach/upgrades/{package}_report_{date}.json\n - Human report (this text) -> shown above; saved with the envelope\n```\n\n## Manifest Emission (MANDATORY — last block of every successful output)\n\nAfter the formal report, emit ONE manifest block so the save hook can\npersist the `upgrade-report` envelope. Parsed by `extractUpgradeReport`.\n\n```\n<!-- cspeach:upgrade-manifest\nartefact: upgrade-report\ntitle: <e.g. ZTEST_LAS — S/4HANA 2023 sign-off>\ndetail_path: <project-relative path to report JSON, e.g. .cspeach/upgrades/ZTEST_LAS_report_2026-05-09.json>\nbaseline_path: <project-relative path to the originating scan baseline>\nbaseline_sha256: <hex>\nprogress_path: <project-relative path to the upgrade-progress used as input>\nprogress_sha256: <hex>\nclosed: <count of baseline findings now resolved>\nregressions: <count of NEW findings introduced during fixing>\npending: <count of baseline findings still open (deferred / blocked)>\nregressions:\n reg-001 | <objectName> | <atcRule> | <one-line reason>\n reg-002 | <objectName> | <atcRule> | <one-line reason>\n ...\n-->\n```\n\nRules:\n- The two `regressions:` lines are NOT redundant: the kv `regressions: <n>`\n is the count, the table `regressions:` (followed by indented rows) lists\n the actual regressions. The parser handles both because they're in\n different scopes (kv vs table-header).\n- Empty regressions:\n - When zero regressions, the kv reads `regressions: 0` and the table is\n `regressions:` followed by no indented rows. Emit both for parser\n clarity.\n- IDs `reg-001`, `reg-002`, … sequential, zero-padded to 3 digits.\n- A regression's `reason` should be ONE LINE (no newlines) — the rest\n of the analysis lives in the body of the human-facing report.\n\n## Post-Save Chain Prompt\n\nAfter the manifest + save fires, branch on the regression count:\n\nIf `regressions > 0`:\n\n> \"Verify saved. {regression_count} regression(s) found. Run\n> /abap-upgrade-fix to address them, then re-run /abap-upgrade-verify?\"\n>\n> Options:\n> - Run /abap-upgrade-fix on the regressions\n> - End turn — I'll triage manually first\n\nOn \"Run /abap-upgrade-fix\" — print the hint pattern (the regressions\nfile is the just-saved one — same post-save-timing caveat as scan):\n\n> `→ Run \\`/files\\`, pick the just-saved verify report, then choose /abap-upgrade-fix to remediate the listed regressions.`\n\nIf `regressions === 0`:\n\n> \"Verify saved — clean report. {closed_count} findings closed,\n> 0 regressions. Sign-off ready.\"\n>\n> Options:\n> - End turn (sign-off complete)\n\nEnd cleanly with a brief congratulatory note. No further chain.\n\n## Guardrails\n\n- NEVER modify any source code — this skill is read-only analysis and reporting\n- If regressions are found, flag them with \"REGRESSION\" in every section where\n they appear; never suppress or minimize them\n- If baseline data is incomplete, label each affected comparison as \"approximate\"\n and explain what data was missing\n- Always include the Sign-Off section, even if the rest of the report is partial\n- If the ATC re-run fails (tool error), report the error and do not fabricate\n a \"clean\" result\n- Do not mark the engagement complete if any priority-1 findings remain\n (resolved or not) without explicit developer confirmation\n- If no state file is found and the user provides manual inputs, note in the\n report header: \"Baseline data provided manually — not sourced from state file\"\n- Distinguish clearly between \"Remaining\" (existed before, not fixed) and\n \"Regression\" (new, introduced by the fix session) — these are different risk\n categories\n\n## Example Prompt\n\n```\n/abap-upgrade-verify\n\nPackage: ZFICO_CUSTOM\nVariant: S4HANA_READINESS_2023\nCustomer: Contoso AG\n\nRemediation is complete. Please generate the sign-off report.\n```\n\n## Example Output Outline\n\n```\n## Upgrade Remediation Report\n\n**Customer:** Contoso AG\n**System:** C11 / Client 100\n**SAP Release:** 758 SP02\n**Target:** S/4HANA 2023\n**Package Scope:** ZFICO_CUSTOM\n**ATC Variant:** S4HANA_READINESS_2023\n**Prepared by:** {developer} / {company}\n**Report Date:** 2026-03-29\n\n---\n\n## Executive Summary\n\n| Metric | Value |\n|--------|-------|\n| Findings Before Remediation | 200 across 31 objects |\n| Findings After Remediation | 12 across 7 objects |\n| Findings Resolved | 188 (94%) |\n| Findings Remaining | 10 |\n| Regression Findings (NEW) | 2 |\n\n> **[REGRESSION ALERT]** 2 new finding(s) were introduced during remediation.\n> These must be resolved before sign-off. See Remaining Findings section.\n\n**Remediation Status:** BLOCKED — regressions detected\n\n---\n\n## Objects Modified\n\n| Object | Type | Findings Fixed | Transport | Status |\n|--------------------------|------|---------------|-----------|--------|\n| ZFI_POSTING_REPORT | PROG | 47 | DEVK912401 | Fixed |\n| ZCL_FI_DOC_PROCESSOR | CLAS | 38 | DEVK912401 | Fixed |\n| ZFI_BSEG_READER | PROG | 29 | DEVK912402 | Fixed |\n| ZFI_KONV_PRICE_REPORT | PROG | 22 | DEVK912402 | Fixed |\n| ZCL_COEP_EXTRACTOR | CLAS | 19 | DEVK912403 | Fixed |\n| ZFI_VBUK_STATUS_CHECK | PROG | 17 | DEVK912403 | Fixed |\n| ZFI_MKPF_GOODS_READER | PROG | 16 | DEVK912404 | Fixed |\n\n---\n\n## Changes Made\n\n### ZFI_POSTING_REPORT (Program)\n\n**Transport:** DEVK912401\n**Findings Fixed:** 47\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------------|--------------------------------------------------|----------|\n| 112 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\n| 156 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\n| 203 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\n| 89 | STABI_OBSOLETE_STATEMENT: MOVE statement obsolete | Replaced MOVE with direct assignment = | — |\n| 134 | STABI_OBSOLETE_STATEMENT: COMPUTE statement obsolete | Replaced COMPUTE with direct arithmetic | — |\n| ... | (42 additional findings — SELECT * and obsolete statements) | Field lists added; MOVE/COMPUTE replaced | — |\n\n### ZCL_FI_DOC_PROCESSOR (Class)\n\n**Transport:** DEVK912401\n**Findings Fixed:** 38\n\n| Line | Finding | Change Made | SAP Note |\n|-------|---------------------------------------------------------------|---------------------------------------------------------|----------|\n| 78 | S4HANA_READINESS_2023: FM BAPI_FI_DOC_CREATE deprecated | Replaced with BAPI_ACC_DOCUMENT_POST (ARS_W_API_SCCSSR) | 2220005 |\n| 145 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\n| 201 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced with I_JournalEntry CDS view | 2431747 |\n| ... | (35 additional findings) | API replacements and obsolete statement fixes | — |\n\n### ZFI_BSEG_READER (Program)\n\n**Transport:** DEVK912402\n**Findings Fixed:** 29\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\n| 34 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced with I_JournalEntry + I_JournalEntryItem | 2431747 |\n| 67 | S4HANA_READINESS_2023: Direct read from BSIS | Replaced with ACDOCA filtered by rbstat | 2431747 |\n| 98 | S4HANA_READINESS_2023: Direct read from BSAS | Replaced with ACDOCA filtered by augdt | 2431747 |\n| ... | (26 additional findings) | All BSEG/BSIS/BSAS reads migrated to ACDOCA/CDS | 2431747 |\n\n### ZFI_KONV_PRICE_REPORT (Program)\n\n**Transport:** DEVK912402\n**Findings Fixed:** 22\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\n| 45 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\n| 89 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\n| ... | (20 additional KONV reads) | All replaced with PRCD_ELEMENTS | 2220005 |\n\n### ZCL_COEP_EXTRACTOR (Class)\n\n**Transport:** DEVK912403\n**Findings Fixed:** 19\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\n| 56 | S4HANA_READINESS_2023: Direct read from COEP | Replaced with ACDOCA (CO area + cost element filter) | — |\n| 102 | S4HANA_READINESS_2023: Direct read from COEP | Replaced with ACDOCA | — |\n| ... | (17 additional findings) | All COEP reads replaced with ACDOCA | — |\n\n### ZFI_VBUK_STATUS_CHECK (Program)\n\n**Transport:** DEVK912403\n**Findings Fixed:** 17\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\n| 23 | S4HANA_READINESS_2023: Direct read from VBUK | Replaced with VBAK fields (gbstk, abstk) | 2198647 |\n| 44 | S4HANA_READINESS_2023: Direct read from VBUP | Replaced with VBAP fields (gbsta, absta) | 2198647 |\n| ... | (15 additional findings) | VBUK/VBUP reads replaced with VBAK/VBAP fields | 2198647 |\n\n### ZFI_MKPF_GOODS_READER (Program)\n\n**Transport:** DEVK912404\n**Findings Fixed:** 16\n\n| Line | Finding | Change Made | SAP Note |\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\n| 34 | S4HANA_READINESS_2023: Direct read from MKPF | Replaced with MATDOC (material document header) | — |\n| 67 | S4HANA_READINESS_2023: Direct read from MSEG | Replaced with MATDOC (material document item) | — |\n| ... | (14 additional findings) | All MKPF/MSEG reads replaced with MATDOC | — |\n\n---\n\n## Remaining Findings\n\n| Object | Type | Priority | Finding | Reason Not Fixed |\n|---------------------------|------|----------|-----------------------------------------------------------------|-----------------|\n| ZFI_CREDIT_MGMT_REPORT | PROG | 1 | S4HANA_READINESS_2023: Credit management redesign required | Functional redesign — requires business decision on FCV vs SAP Credit Management |\n| ZFI_CREDIT_MGMT_REPORT | PROG | 2 | S4HANA_READINESS_2023: KNKK table obsolete in S/4HANA | Requires credit management migration — deferred to Phase 2 |\n| ZCL_OUTPUT_MGMT_WRAPPER | CLAS | 1 | S4HANA_READINESS_2023: NAST-based output deprecated | Output management redesign — BRF+ migration deferred, SAP Note 3284711 |\n| ZCL_OUTPUT_MGMT_WRAPPER | CLAS | 2 | S4HANA_READINESS_2023: SAPscript form reference obsolete | Output form migration out of scope for this engagement |\n| ZFI_BP_SYNC_PROGRAM | PROG | 1 | S4HANA_READINESS_2023: Vendor/Customer → Business Partner | Business partner migration must run first — organizational prerequisite |\n| ZFI_BP_SYNC_PROGRAM | PROG | 2 | S4HANA_READINESS_2023: LFB1/KNB1 direct read obsolete | Blocked by BP migration prerequisite above |\n| ZCL_MATERIAL_LEDGER_CALC | CLAS | 1 | S4HANA_READINESS_2023: Actual costing redesign required | Material ledger redesign — Cremencing Phase 3 engagement |\n| ZCL_MATERIAL_LEDGER_CALC | CLAS | 2 | S4HANA_READINESS_2023: CKMLPP table structure changed | Blocked by material ledger redesign above |\n| **[REGRESSION]** ZFI_POSTING_REPORT | PROG | 2 | PERF_DB_SEARCH: SELECT from I_JournalEntry without index field | **New finding introduced during BSEG→I_JournalEntry migration.** CDS view requires RLDNR filter for indexed access. Fix: add WHERE rldnr = '0L'. |\n| **[REGRESSION]** ZCL_FI_DOC_PROCESSOR | CLAS | 2 | STABI_MISSING_FIELD_SYMBOL: Unassigned field symbol in line 203 | **New finding introduced during refactoring of KONV→PRCD_ELEMENTS loop.** Field symbol not assigned before ASSIGN. Fix: add CHECK sy-subrc = 0 after ASSIGN. |\n\n---\n\n## Retired Objects\n\n| Object | Type | Last Usage | Approved By | Action |\n|------------------------|------|--------------------|-------------|-------------------------------|\n| ZFI_BSEG_OLD_UTILITY | PROG | Never (dead code) | JSMITH | Deleted — Transport DEVK912405 |\n| ZFI_KONV_COMPAT_SHIM | PROG | 2024-06-15 (UPL) | JSMITH | Deleted — Transport DEVK912405 |\n| ZFI_MKPF_LEGACY_READER | PROG | Never (dead code) | JSMITH | Deleted — Transport DEVK912405 |\n\n---\n\n## Transport Summary\n\n| Transport | Description | Objects Included | Status |\n|-------------|------------------------------------------------|--------------------------------------------------------|----------|\n| DEVK912401 | ZFICO S/4 Upgrade — FI Doc Processor + Posting | ZFI_POSTING_REPORT, ZCL_FI_DOC_PROCESSOR | Released |\n| DEVK912402 | ZFICO S/4 Upgrade — BSEG/KONV readers | ZFI_BSEG_READER, ZFI_KONV_PRICE_REPORT | Released |\n| DEVK912403 | ZFICO S/4 Upgrade — COEP Extractor + VBUK | ZCL_COEP_EXTRACTOR, ZFI_VBUK_STATUS_CHECK | Released |\n| DEVK912404 | ZFICO S/4 Upgrade — MKPF/MSEG Goods Reader | ZFI_MKPF_GOODS_READER | Released |\n| DEVK912405 | ZFICO S/4 Upgrade — Retire dead code | ZFI_BSEG_OLD_UTILITY, ZFI_KONV_COMPAT_SHIM, ZFI_MKPF_LEGACY_READER | Released |\n\n---\n\n## Sign-Off\n\n**Remediation performed by:** _____________________________ Date: ___________\n\n**Reviewed and approved by:** _____________________________ Date: ___________\n\n**Customer representative:** _____________________________ Date: ___________\n\n*This report documents the results of ABAP upgrade remediation performed on\nC11 / Client 100 for target release S/4HANA 2023.\nPrepared by {company} with CSPeach.*\n\n---\n\n> This report can be exported and used as a project sign-off document.\n> Copy the Markdown to a Word or PDF template, or attach the raw `.md` file\n> to your project documentation system.\n>\n> NOTE: 2 regression findings were detected (see Remaining Findings).\n> Run `/abap-upgrade-fix` to address regressions, then re-run\n> `/abap-upgrade-verify` to produce a final clean report.\n```\n",
261
- "sha256": "76ab9b0f1661793586a71ff6a24587cc532f7a4f3a6766b84b75b5d4c263bf5c",
267
+ "body": "---\r\nname: abap-upgrade-verify\r\ndescription: >\r\n Verify upgrade remediation results. Re-runs ATC with the same variant and\r\n scope as the original scan, compares findings against the baseline, flags\r\n regressions prominently, and generates a formal Upgrade Remediation Report\r\n suitable for project sign-off and customer delivery.\r\nphase: VERIFY\r\nrequires_mcp: required\r\nforge_rules: [6]\r\nversion: \"1.0\"\r\n---\r\n\r\n# abap-upgrade-verify\r\n\r\n## Purpose\r\n\r\nGenerate a formal, consulting-grade Upgrade Remediation Report after upgrade\r\nremediation work is complete. The skill re-runs ATC against the same package\r\nscope and variant used in the original `/abap-upgrade-scan`, compares the new\r\nfindings against the scan baseline, and produces a structured document covering\r\nexecutive summary, per-object change history, remaining findings, transport\r\nchain, and a sign-off block.\r\n\r\nThis skill is read-only. It never modifies source code or system data.\r\nIts output is a deliverable — structured for inclusion in project documentation,\r\ncustomer handover packages, or SAP upgrade project sign-off gates.\r\n\r\nFor upgrade-related upgrades also see:\r\n- `/abap-upgrade-scan` — runs the initial ATC scan and produces the remediation plan\r\n- `/abap-upgrade-fix` — interactive object-by-object remediation with developer approval\r\n\r\n## When to Use\r\n\r\n- After `/abap-upgrade-fix` completes and the developer confirms all objects\r\n are fixed, skipped, or deferred\r\n- Before presenting results to a customer or project steering committee\r\n- As a formal closure gate for an upgrade remediation engagement\r\n- When a re-scan is needed to confirm a specific set of objects is clean\r\n- To generate a regression check after additional changes were made\r\n\r\nWhen NOT to use: fixes not finished yet — `/abap-upgrade-fix`; consolidating parallel team slices first — `/abap-upgrade-merge`.\r\n\r\n## Inputs Expected\r\n\r\nRequired:\r\n- Package scope (same as used in original scan, e.g. ZTEST_LAS or ZFICO_*)\r\n- ATC variant (same as original scan, e.g. S4HANA_READINESS_2023)\r\n\r\nAutomatically sourced (if the state file exists):\r\n- Baseline finding count and object list — from `.cspeach/upgrades/{package}_{variant}_{date}.json`\r\n- Fix session results (per-object change log) — from the same state file\r\n- Transport numbers — from the fix session state\r\n\r\nIf no state file is found, ask the user for:\r\n- Baseline finding count (before remediation)\r\n- List of objects that were modified (with transport numbers)\r\n- Whether any objects were retired\r\n\r\nOptional:\r\n- Customer name (for report header)\r\n- Prepared-by name (defaults to current SAP logon user)\r\n- Whether to include the full Changes Made section or a summary only\r\n\r\n## Required Behavior\r\n\r\n### Step 1 — Load Baseline\r\n\r\nRead `.cspeach/upgrades/{package}_{variant}_{date}.json` using the Read tool;\r\nif absent, fall back to the legacy `.abapforge/upgrades/` location (older\r\nprojects). If multiple files match (multiple scan dates), use the most recent.\r\n\r\nExtract from the state file:\r\n- `baseline.totalFindings` — total findings before remediation\r\n- `baseline.objectCount` — number of objects with findings\r\n- `baseline.findingsByObject` — map of objectName → finding list\r\n- `fixSession.objectsModified` — list of objects that were changed\r\n- `fixSession.changesPerObject` — per-object log of what was fixed\r\n- `fixSession.transports` — transport numbers used\r\n- `fixSession.objectsSkipped` — objects not remediated with reasons\r\n- `fixSession.objectsRetired` — objects decommissioned\r\n\r\nIf the state file is missing or incomplete, note which comparisons are\r\napproximate and proceed with the data available. Do not abort.\r\n\r\n### Step 2 — Re-Run ATC\r\n\r\nCall `sap_atc_run` with:\r\n- objectType: DEVC\r\n- objectName: {package scope}\r\n- variant: {same variant as original scan}\r\n\r\nParse the new findings. For each finding extract:\r\n- objectName, objectType, checkTitle, messageTitle, priority, location, line\r\n\r\nBuild a map: newFindingsByObject → finding list per object.\r\n\r\n### Step 3 — Compare Against Baseline\r\n\r\nCompute the three comparison sets:\r\n\r\n**Resolved findings:** Present in baseline, not present in new scan.\r\nMatch by: objectName + checkTitle + messageTitle (trim whitespace).\r\nIf location/line is available, also use it as a secondary match.\r\n\r\n**Remaining findings:** Present in both baseline and new scan.\r\n\r\n**Regression findings (NEW):** Present in new scan but NOT in baseline.\r\nThese are findings that did not exist before remediation started.\r\n\r\nReport the counts: resolved={n}, remaining={n}, regressions={n}.\r\n\r\nIf regressions > 0, flag them with a prominent \"REGRESSION\" label throughout\r\nthe report. Regressions indicate that a fix introduced a new ATC violation.\r\n\r\n### Step 4 — Query System Information\r\n\r\nQuery system info for the report header using `sap_sql_query`:\r\n\r\n```sql\r\nSELECT sysid, mandt, saprl, host FROM t000 WHERE mandt = sy-mandt\r\n```\r\n\r\nIf that query fails or returns no rows, fall back to:\r\n\r\n```sql\r\nSELECT * FROM t000 WHERE mandt = '100'\r\n```\r\n\r\nExtract: SID (SYSID), Client (MANDT), SAP Release (SAPRL), Host.\r\nUse these in the report header.\r\n\r\nIf the SQL query is unavailable, note \"System info unavailable — add manually\"\r\nin the header and continue.\r\n\r\n### Step 5 — Generate the Formal Upgrade Remediation Report\r\n\r\nProduce the report as structured Markdown following the Output Structure below.\r\nUse actual data from steps 1–4. Do not omit any section — include every section\r\neven if it has no entries (use \"None\" or \"N/A\" as appropriate).\r\n\r\nAfter generating the report, state:\r\n\r\n> This report can be exported and used as a project sign-off document.\r\n> Copy the Markdown to a Word or PDF template, or attach the raw `.md` file\r\n> to your project documentation system.\r\n\r\n### Step 6 — Regression Handling\r\n\r\nIf regressions were found:\r\n\r\n1. List each regression with: object, check, message, line, priority\r\n2. Label each with **[REGRESSION]** in bold\r\n3. In the Executive Summary, flag: \"X regression findings introduced — review required\"\r\n4. Do NOT mark the engagement as complete — state that regressions must be\r\n investigated before sign-off\r\n5. Offer: \"Run `/abap-upgrade-fix` to address the regressions, then re-run\r\n `/abap-upgrade-verify`.\"\r\n\r\n\r\n## TL;DR (MANDATORY — must be the first section of every output)\r\n\r\n<!-- csforge:tldr-required -->\r\n\r\nEvery response from this skill MUST open with a `## TL;DR` block in exactly this shape, and nothing before it except the skill's main heading:\r\n\r\n```markdown\r\n## [Skill Name] — [Target Object/Package/Context]\r\n\r\n## TL;DR\r\n\r\n**Headline:** <1-sentence summary of what was analysed, produced, or decided>\r\n\r\n**Top 3 (priority order — most important first):**\r\n1. <highest-priority finding, decision, or action>\r\n2. <second most important>\r\n3. <third most important>\r\n\r\n**Verdict:** <1-line overall conclusion or next-step recommendation>\r\n\r\n---\r\n\r\n<then the full structured report below, exactly as defined in this skill's Output Structure section>\r\n```\r\n\r\n**Rules for the Top 3 list:**\r\n- **Prioritise by impact**, not by positional order or severity class alone. A HIGH with limited blast radius may rank below a MEDIUM that blocks the requested change.\r\n- **Be specific.** \"Race condition on `SELECT MAX( game_id ) + 1` in `save_result`\" beats \"Global state issues\".\r\n- **Each bullet must be actionable** — naming the concrete pattern, location, or decision, not a generic category.\r\n- If the skill doesn't have \"findings\" in the risk sense (e.g. code generation, documentation), substitute: top 3 **design decisions**, **artefacts produced**, or **things the user must review next**.\r\n- If there are fewer than 3 meaningful items, list only what's real and say so in the Verdict — do not pad.\r\n\r\n**Why this matters:** the TL;DR is what the developer sees in chat (short channel); the full structured report below is saved to a file for deeper review. The TL;DR must be self-contained — scan it and know the answer.\r\n\r\n\r\n## Output Structure\r\n\r\n```\r\n## Upgrade Remediation Report\r\n\r\n**Customer:** {customer_name}\r\n**System:** {SID} / Client {client}\r\n**SAP Release:** {saprl}\r\n**Target:** S/4HANA {target_release}\r\n**Package Scope:** {package}\r\n**ATC Variant:** {variant}\r\n**Prepared by:** {developer} / {company}\r\n**Report Date:** {date}\r\n\r\n---\r\n\r\n## Executive Summary\r\n\r\n| Metric | Value |\r\n|--------|-------|\r\n| Findings Before Remediation | {baseline_count} across {object_count} objects |\r\n| Findings After Remediation | {new_count} across {new_object_count} objects |\r\n| Findings Resolved | {resolved_count} ({resolved_pct}%) |\r\n| Findings Remaining | {remaining_count} |\r\n| Regression Findings (NEW) | {regression_count} |\r\n\r\n{If regressions > 0:}\r\n> **[REGRESSION ALERT]** {regression_count} new finding(s) were introduced\r\n> during remediation. These must be resolved before sign-off. See Remaining\r\n> Findings section.\r\n\r\n**Remediation Status:** {Complete — all findings resolved | Partial — {n} findings remain | BLOCKED — regressions detected}\r\n\r\n---\r\n\r\n## Objects Modified\r\n\r\n| Object | Type | Findings Fixed | Transport | Status |\r\n|--------|------|---------------|-----------|--------|\r\n| {object} | {PROG/CLAS/FUGR/etc} | {n} | {transport_number} | Fixed |\r\n\r\n---\r\n\r\n## Changes Made\r\n\r\n### {Object_Name} ({Object_Type})\r\n\r\n**Transport:** {transport_number}\r\n**Findings Fixed:** {n}\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|------|---------|-------------|----------|\r\n| {line} | {checkTitle}: {messageTitle} | {description_of_fix} | {note_number} |\r\n\r\n---\r\n\r\n## Remaining Findings\r\n\r\n| Object | Type | Priority | Finding | Reason Not Fixed |\r\n|--------|------|----------|---------|-----------------|\r\n| {object} | {type} | {1/2/3} | {checkTitle}: {messageTitle} | {reason} |\r\n\r\n{If any remaining finding was introduced by the fix session (regression):}\r\n| {object} | {type} | {priority} | **[REGRESSION]** {checkTitle}: {messageTitle} | New finding — not in original baseline |\r\n\r\n---\r\n\r\n## Retired Objects\r\n\r\n| Object | Type | Last Usage | Approved By | Action |\r\n|--------|------|-----------|-------------|--------|\r\n| {object} | {type} | {date or Never} | {approver} | Deleted / Transport: {transport} |\r\n\r\n---\r\n\r\n## Transport Summary\r\n\r\n| Transport | Description | Objects Included | Status |\r\n|-----------|-------------|-----------------|--------|\r\n| {transport_number} | {description} | {object_list} | Released / Open |\r\n\r\n---\r\n\r\n## Sign-Off\r\n\r\n**Remediation performed by:** _____________________________ Date: ___________\r\n\r\n**Reviewed and approved by:** _____________________________ Date: ___________\r\n\r\n**Customer representative:** _____________________________ Date: ___________\r\n\r\n*This report documents the results of ABAP upgrade remediation performed on\r\n{system} / Client {client} for target release S/4HANA {target_release}.\r\nPrepared by {company} with CSPeach.*\r\n\r\nOutputs:\r\n - Sign-off report (detail) -> .cspeach/upgrades/{package}_report_{date}.json\r\n - Human report (this text) -> shown above; saved with the envelope\r\n```\r\n\r\n## Manifest Emission (MANDATORY — last block of every successful output)\r\n\r\nAfter the formal report, emit ONE manifest block so the save hook can\r\npersist the `upgrade-report` envelope. Parsed by `extractUpgradeReport`.\r\n\r\n```\r\n<!-- cspeach:upgrade-manifest\r\nartefact: upgrade-report\r\ntitle: <e.g. ZTEST_LAS — S/4HANA 2023 sign-off>\r\ndetail_path: <project-relative path to report JSON, e.g. .cspeach/upgrades/ZTEST_LAS_report_2026-05-09.json>\r\nbaseline_path: <project-relative path to the originating scan baseline>\r\nbaseline_sha256: <hex>\r\nprogress_path: <project-relative path to the upgrade-progress used as input>\r\nprogress_sha256: <hex>\r\nclosed: <count of baseline findings now resolved>\r\nregressions: <count of NEW findings introduced during fixing>\r\npending: <count of baseline findings still open (deferred / blocked)>\r\nregressions:\r\n reg-001 | <objectName> | <atcRule> | <one-line reason>\r\n reg-002 | <objectName> | <atcRule> | <one-line reason>\r\n ...\r\n-->\r\n```\r\n\r\nRules:\r\n- The two `regressions:` lines are NOT redundant: the kv `regressions: <n>`\r\n is the count, the table `regressions:` (followed by indented rows) lists\r\n the actual regressions. The parser handles both because they're in\r\n different scopes (kv vs table-header).\r\n- Empty regressions:\r\n - When zero regressions, the kv reads `regressions: 0` and the table is\r\n `regressions:` followed by no indented rows. Emit both for parser\r\n clarity.\r\n- IDs `reg-001`, `reg-002`, … sequential, zero-padded to 3 digits.\r\n- A regression's `reason` should be ONE LINE (no newlines) — the rest\r\n of the analysis lives in the body of the human-facing report.\r\n\r\n## Post-Save Chain Prompt\r\n\r\nAfter the manifest + save fires, branch on the regression count:\r\n\r\nIf `regressions > 0`:\r\n\r\n> \"Verify saved. {regression_count} regression(s) found. Run\r\n> /abap-upgrade-fix to address them, then re-run /abap-upgrade-verify?\"\r\n>\r\n> Options:\r\n> - Run /abap-upgrade-fix on the regressions\r\n> - End turn — I'll triage manually first\r\n\r\nOn \"Run /abap-upgrade-fix\" — print the hint pattern (the regressions\r\nfile is the just-saved one — same post-save-timing caveat as scan):\r\n\r\n> `→ Run \\`/files\\`, pick the just-saved verify report, then choose /abap-upgrade-fix to remediate the listed regressions.`\r\n\r\nIf `regressions === 0`:\r\n\r\n> \"Verify saved — clean report. {closed_count} findings closed,\r\n> 0 regressions. Sign-off ready.\"\r\n>\r\n> Options:\r\n> - End turn (sign-off complete)\r\n\r\nEnd cleanly with a brief congratulatory note. No further chain.\r\n\r\n## Guardrails\r\n\r\n- NEVER modify any source code — this skill is read-only analysis and reporting\r\n- If regressions are found, flag them with \"REGRESSION\" in every section where\r\n they appear; never suppress or minimize them\r\n- If baseline data is incomplete, label each affected comparison as \"approximate\"\r\n and explain what data was missing\r\n- Always include the Sign-Off section, even if the rest of the report is partial\r\n- If the ATC re-run fails (tool error), report the error and do not fabricate\r\n a \"clean\" result\r\n- Do not mark the engagement complete if any priority-1 findings remain\r\n (resolved or not) without explicit developer confirmation\r\n- If no state file is found and the user provides manual inputs, note in the\r\n report header: \"Baseline data provided manually — not sourced from state file\"\r\n- Distinguish clearly between \"Remaining\" (existed before, not fixed) and\r\n \"Regression\" (new, introduced by the fix session) — these are different risk\r\n categories\r\n\r\n## Example Prompt\r\n\r\n```\r\n/abap-upgrade-verify\r\n\r\nPackage: ZFICO_CUSTOM\r\nVariant: S4HANA_READINESS_2023\r\nCustomer: Contoso AG\r\n\r\nRemediation is complete. Please generate the sign-off report.\r\n```\r\n\r\n## Example Output Outline\r\n\r\n```\r\n## Upgrade Remediation Report\r\n\r\n**Customer:** Contoso AG\r\n**System:** C11 / Client 100\r\n**SAP Release:** 758 SP02\r\n**Target:** S/4HANA 2023\r\n**Package Scope:** ZFICO_CUSTOM\r\n**ATC Variant:** S4HANA_READINESS_2023\r\n**Prepared by:** {developer} / {company}\r\n**Report Date:** 2026-03-29\r\n\r\n---\r\n\r\n## Executive Summary\r\n\r\n| Metric | Value |\r\n|--------|-------|\r\n| Findings Before Remediation | 200 across 31 objects |\r\n| Findings After Remediation | 12 across 7 objects |\r\n| Findings Resolved | 188 (94%) |\r\n| Findings Remaining | 10 |\r\n| Regression Findings (NEW) | 2 |\r\n\r\n> **[REGRESSION ALERT]** 2 new finding(s) were introduced during remediation.\r\n> These must be resolved before sign-off. See Remaining Findings section.\r\n\r\n**Remediation Status:** BLOCKED — regressions detected\r\n\r\n---\r\n\r\n## Objects Modified\r\n\r\n| Object | Type | Findings Fixed | Transport | Status |\r\n|--------------------------|------|---------------|-----------|--------|\r\n| ZFI_POSTING_REPORT | PROG | 47 | DEVK912401 | Fixed |\r\n| ZCL_FI_DOC_PROCESSOR | CLAS | 38 | DEVK912401 | Fixed |\r\n| ZFI_BSEG_READER | PROG | 29 | DEVK912402 | Fixed |\r\n| ZFI_KONV_PRICE_REPORT | PROG | 22 | DEVK912402 | Fixed |\r\n| ZCL_COEP_EXTRACTOR | CLAS | 19 | DEVK912403 | Fixed |\r\n| ZFI_VBUK_STATUS_CHECK | PROG | 17 | DEVK912403 | Fixed |\r\n| ZFI_MKPF_GOODS_READER | PROG | 16 | DEVK912404 | Fixed |\r\n\r\n---\r\n\r\n## Changes Made\r\n\r\n### ZFI_POSTING_REPORT (Program)\r\n\r\n**Transport:** DEVK912401\r\n**Findings Fixed:** 47\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------------|--------------------------------------------------|----------|\r\n| 112 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\r\n| 156 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\r\n| 203 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced SELECT FROM BSEG with I_JournalEntry CDS | 2431747 |\r\n| 89 | STABI_OBSOLETE_STATEMENT: MOVE statement obsolete | Replaced MOVE with direct assignment = | — |\r\n| 134 | STABI_OBSOLETE_STATEMENT: COMPUTE statement obsolete | Replaced COMPUTE with direct arithmetic | — |\r\n| ... | (42 additional findings — SELECT * and obsolete statements) | Field lists added; MOVE/COMPUTE replaced | — |\r\n\r\n### ZCL_FI_DOC_PROCESSOR (Class)\r\n\r\n**Transport:** DEVK912401\r\n**Findings Fixed:** 38\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|---------------------------------------------------------------|---------------------------------------------------------|----------|\r\n| 78 | S4HANA_READINESS_2023: FM BAPI_FI_DOC_CREATE deprecated | Replaced with BAPI_ACC_DOCUMENT_POST (ARS_W_API_SCCSSR) | 2220005 |\r\n| 145 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\r\n| 201 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced with I_JournalEntry CDS view | 2431747 |\r\n| ... | (35 additional findings) | API replacements and obsolete statement fixes | — |\r\n\r\n### ZFI_BSEG_READER (Program)\r\n\r\n**Transport:** DEVK912402\r\n**Findings Fixed:** 29\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\r\n| 34 | S4HANA_READINESS_2023: Direct read from BSEG | Replaced with I_JournalEntry + I_JournalEntryItem | 2431747 |\r\n| 67 | S4HANA_READINESS_2023: Direct read from BSIS | Replaced with ACDOCA filtered by rbstat | 2431747 |\r\n| 98 | S4HANA_READINESS_2023: Direct read from BSAS | Replaced with ACDOCA filtered by augdt | 2431747 |\r\n| ... | (26 additional findings) | All BSEG/BSIS/BSAS reads migrated to ACDOCA/CDS | 2431747 |\r\n\r\n### ZFI_KONV_PRICE_REPORT (Program)\r\n\r\n**Transport:** DEVK912402\r\n**Findings Fixed:** 22\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\r\n| 45 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\r\n| 89 | S4HANA_READINESS_2023: Direct read from KONV | Replaced with SELECT FROM PRCD_ELEMENTS | 2220005 |\r\n| ... | (20 additional KONV reads) | All replaced with PRCD_ELEMENTS | 2220005 |\r\n\r\n### ZCL_COEP_EXTRACTOR (Class)\r\n\r\n**Transport:** DEVK912403\r\n**Findings Fixed:** 19\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\r\n| 56 | S4HANA_READINESS_2023: Direct read from COEP | Replaced with ACDOCA (CO area + cost element filter) | — |\r\n| 102 | S4HANA_READINESS_2023: Direct read from COEP | Replaced with ACDOCA | — |\r\n| ... | (17 additional findings) | All COEP reads replaced with ACDOCA | — |\r\n\r\n### ZFI_VBUK_STATUS_CHECK (Program)\r\n\r\n**Transport:** DEVK912403\r\n**Findings Fixed:** 17\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\r\n| 23 | S4HANA_READINESS_2023: Direct read from VBUK | Replaced with VBAK fields (gbstk, abstk) | 2198647 |\r\n| 44 | S4HANA_READINESS_2023: Direct read from VBUP | Replaced with VBAP fields (gbsta, absta) | 2198647 |\r\n| ... | (15 additional findings) | VBUK/VBUP reads replaced with VBAK/VBAP fields | 2198647 |\r\n\r\n### ZFI_MKPF_GOODS_READER (Program)\r\n\r\n**Transport:** DEVK912404\r\n**Findings Fixed:** 16\r\n\r\n| Line | Finding | Change Made | SAP Note |\r\n|-------|------------------------------------------------------|-----------------------------------------------------|----------|\r\n| 34 | S4HANA_READINESS_2023: Direct read from MKPF | Replaced with MATDOC (material document header) | — |\r\n| 67 | S4HANA_READINESS_2023: Direct read from MSEG | Replaced with MATDOC (material document item) | — |\r\n| ... | (14 additional findings) | All MKPF/MSEG reads replaced with MATDOC | — |\r\n\r\n---\r\n\r\n## Remaining Findings\r\n\r\n| Object | Type | Priority | Finding | Reason Not Fixed |\r\n|---------------------------|------|----------|-----------------------------------------------------------------|-----------------|\r\n| ZFI_CREDIT_MGMT_REPORT | PROG | 1 | S4HANA_READINESS_2023: Credit management redesign required | Functional redesign — requires business decision on FCV vs SAP Credit Management |\r\n| ZFI_CREDIT_MGMT_REPORT | PROG | 2 | S4HANA_READINESS_2023: KNKK table obsolete in S/4HANA | Requires credit management migration — deferred to Phase 2 |\r\n| ZCL_OUTPUT_MGMT_WRAPPER | CLAS | 1 | S4HANA_READINESS_2023: NAST-based output deprecated | Output management redesign — BRF+ migration deferred, SAP Note 3284711 |\r\n| ZCL_OUTPUT_MGMT_WRAPPER | CLAS | 2 | S4HANA_READINESS_2023: SAPscript form reference obsolete | Output form migration out of scope for this engagement |\r\n| ZFI_BP_SYNC_PROGRAM | PROG | 1 | S4HANA_READINESS_2023: Vendor/Customer → Business Partner | Business partner migration must run first — organizational prerequisite |\r\n| ZFI_BP_SYNC_PROGRAM | PROG | 2 | S4HANA_READINESS_2023: LFB1/KNB1 direct read obsolete | Blocked by BP migration prerequisite above |\r\n| ZCL_MATERIAL_LEDGER_CALC | CLAS | 1 | S4HANA_READINESS_2023: Actual costing redesign required | Material ledger redesign — Cremencing Phase 3 engagement |\r\n| ZCL_MATERIAL_LEDGER_CALC | CLAS | 2 | S4HANA_READINESS_2023: CKMLPP table structure changed | Blocked by material ledger redesign above |\r\n| **[REGRESSION]** ZFI_POSTING_REPORT | PROG | 2 | PERF_DB_SEARCH: SELECT from I_JournalEntry without index field | **New finding introduced during BSEG→I_JournalEntry migration.** CDS view requires RLDNR filter for indexed access. Fix: add WHERE rldnr = '0L'. |\r\n| **[REGRESSION]** ZCL_FI_DOC_PROCESSOR | CLAS | 2 | STABI_MISSING_FIELD_SYMBOL: Unassigned field symbol in line 203 | **New finding introduced during refactoring of KONV→PRCD_ELEMENTS loop.** Field symbol not assigned before ASSIGN. Fix: add CHECK sy-subrc = 0 after ASSIGN. |\r\n\r\n---\r\n\r\n## Retired Objects\r\n\r\n| Object | Type | Last Usage | Approved By | Action |\r\n|------------------------|------|--------------------|-------------|-------------------------------|\r\n| ZFI_BSEG_OLD_UTILITY | PROG | Never (dead code) | JSMITH | Deleted — Transport DEVK912405 |\r\n| ZFI_KONV_COMPAT_SHIM | PROG | 2024-06-15 (UPL) | JSMITH | Deleted — Transport DEVK912405 |\r\n| ZFI_MKPF_LEGACY_READER | PROG | Never (dead code) | JSMITH | Deleted — Transport DEVK912405 |\r\n\r\n---\r\n\r\n## Transport Summary\r\n\r\n| Transport | Description | Objects Included | Status |\r\n|-------------|------------------------------------------------|--------------------------------------------------------|----------|\r\n| DEVK912401 | ZFICO S/4 Upgrade — FI Doc Processor + Posting | ZFI_POSTING_REPORT, ZCL_FI_DOC_PROCESSOR | Released |\r\n| DEVK912402 | ZFICO S/4 Upgrade — BSEG/KONV readers | ZFI_BSEG_READER, ZFI_KONV_PRICE_REPORT | Released |\r\n| DEVK912403 | ZFICO S/4 Upgrade — COEP Extractor + VBUK | ZCL_COEP_EXTRACTOR, ZFI_VBUK_STATUS_CHECK | Released |\r\n| DEVK912404 | ZFICO S/4 Upgrade — MKPF/MSEG Goods Reader | ZFI_MKPF_GOODS_READER | Released |\r\n| DEVK912405 | ZFICO S/4 Upgrade — Retire dead code | ZFI_BSEG_OLD_UTILITY, ZFI_KONV_COMPAT_SHIM, ZFI_MKPF_LEGACY_READER | Released |\r\n\r\n---\r\n\r\n## Sign-Off\r\n\r\n**Remediation performed by:** _____________________________ Date: ___________\r\n\r\n**Reviewed and approved by:** _____________________________ Date: ___________\r\n\r\n**Customer representative:** _____________________________ Date: ___________\r\n\r\n*This report documents the results of ABAP upgrade remediation performed on\r\nC11 / Client 100 for target release S/4HANA 2023.\r\nPrepared by {company} with CSPeach.*\r\n\r\n---\r\n\r\n> This report can be exported and used as a project sign-off document.\r\n> Copy the Markdown to a Word or PDF template, or attach the raw `.md` file\r\n> to your project documentation system.\r\n>\r\n> NOTE: 2 regression findings were detected (see Remaining Findings).\r\n> Run `/abap-upgrade-fix` to address regressions, then re-run\r\n> `/abap-upgrade-verify` to produce a final clean report.\r\n```\r\n",
268
+ "sha256": "5b0f9bbeb0a2518e41c92d4c75c40fbdf62ebeb363805aecc81ae655829062e8",
262
269
  "signature": "",
263
- "signedAt": "2026-09-17T20:19:06.011Z"
270
+ "signedAt": "2026-09-28T14:37:05.289Z"
264
271
  },
265
272
  "compact": {
266
273
  "name": "compact",
267
- "body": "---\nname: compact\ndescription: >\n Internal CSPeach utility — not user-invoked directly. /compact slash\n command calls this skill via the proxy to summarise older session turns\n into a compact context block, cutting subsequent-turn cache cost by\n 80-90% on long sessions. Runs on Haiku 4.5 (mechanical summarisation,\n no ABAP reasoning needed).\nphase: UTILITY\nrequires_mcp: never\nforge_rules: []\nversion: \"1.0\"\nmin_cli_version: \"0.6.5\"\n---\n\n# compact\n\nYou are compacting the older turns of a CSPeach (ABAP development CLI) conversation history.\n\nYour output replaces these turns in the session's context — the next model turn will read your summary and continue working from it. Preserve everything the next turn needs to be useful; drop everything that isn't load-bearing.\n\n## PRESERVE in your summary\n\n- **Decisions** made (architectural, naming, scope) with brief rationale\n- **SAP objects** created or modified, with their types and active/inactive state\n- **Build state** — which phases done, which still open, which deferred\n- **Open issues** — errors hit, retries pending, escalations\n- **Constraints** the user set (model boundaries, naming conventions, transport, package)\n- **Tool failures** that haven't been resolved (so the next turn doesn't re-attempt the same broken call)\n\n## DROP from your summary\n\n- Verbose tool-call args / results — say \"probed PACKAGE for existence\" not the full XML response\n- Repetitive content — the same class written 3 times collapses to \"the final shape\"\n- Intermediate failures already resolved\n- TL;DR / Next / Headline sections from prior turns (they describe THAT turn, not state)\n- ASCII art separators, spinner output, footer rows\n- Polite acknowledgements without information\n\n## OUTPUT FORMAT — single markdown block, sections in this order\n\n```markdown\n## Build state\nOne short paragraph: what's the project, what phase are we in, what was just completed.\n\n## Decisions\n- Bullet list of architectural / naming / scope decisions with one-line rationale.\n\n## Objects on SAP\nTable or bullet list. For each: name, type, active/inactive, brief role.\n\n## Open / Deferred\n- Items the developer chose to skip or postpone, with reason.\n- Errors hit that haven't been resolved yet.\n\n## Constraints set by user\n- Model boundaries (e.g. \"Opus stays on planning\")\n- Naming conventions\n- Transport / package\n- Anything else the user explicitly locked\n```\n\n## Length target\n\nAim for **1500-3000 tokens** of output. Better to drop a detail than to bloat past 3000 — the entire point of /compact is making the context small.\n\n## What you're being given\n\nThe user message will be a JSON serialisation of the messages array to summarise — role + flattened content per turn. Tool calls and tool results have been pre-condensed into one-line labels like `[tool_use: sap_search_object({...})]` and `[tool_result: <first 500 chars>]`. Use them as evidence; don't echo them.\n\nBegin your output directly with `## Build state` — no preamble, no \"Here is the summary\" lead-in.\n",
268
- "sha256": "aa0ad3109bb967c7cdd3ae902d79e755fb920fc33d43a4b7facf9c9ea7519cd5",
274
+ "body": "---\r\nname: compact\r\ndescription: >\r\n Internal CSPeach utility — not user-invoked directly. /compact slash\r\n command calls this skill via the proxy to summarise older session turns\r\n into a compact context block, cutting subsequent-turn cache cost by\r\n 80-90% on long sessions. Runs on Haiku 4.5 (mechanical summarisation,\r\n no ABAP reasoning needed).\r\nphase: UTILITY\r\nrequires_mcp: never\r\nforge_rules: []\r\nversion: \"1.0\"\r\nmin_cli_version: \"0.6.5\"\r\n---\r\n\r\n# compact\r\n\r\nYou are compacting the older turns of a CSPeach (ABAP development CLI) conversation history.\r\n\r\nYour output replaces these turns in the session's context — the next model turn will read your summary and continue working from it. Preserve everything the next turn needs to be useful; drop everything that isn't load-bearing.\r\n\r\n## PRESERVE in your summary\r\n\r\n- **Decisions** made (architectural, naming, scope) with brief rationale\r\n- **SAP objects** created or modified, with their types and active/inactive state\r\n- **Build state** — which phases done, which still open, which deferred\r\n- **Open issues** — errors hit, retries pending, escalations\r\n- **Constraints** the user set (model boundaries, naming conventions, transport, package)\r\n- **Tool failures** that haven't been resolved (so the next turn doesn't re-attempt the same broken call)\r\n\r\n## DROP from your summary\r\n\r\n- Verbose tool-call args / results — say \"probed PACKAGE for existence\" not the full XML response\r\n- Repetitive content — the same class written 3 times collapses to \"the final shape\"\r\n- Intermediate failures already resolved\r\n- TL;DR / Next / Headline sections from prior turns (they describe THAT turn, not state)\r\n- ASCII art separators, spinner output, footer rows\r\n- Polite acknowledgements without information\r\n\r\n## OUTPUT FORMAT — single markdown block, sections in this order\r\n\r\n```markdown\r\n## Build state\r\nOne short paragraph: what's the project, what phase are we in, what was just completed.\r\n\r\n## Decisions\r\n- Bullet list of architectural / naming / scope decisions with one-line rationale.\r\n\r\n## Objects on SAP\r\nTable or bullet list. For each: name, type, active/inactive, brief role.\r\n\r\n## Open / Deferred\r\n- Items the developer chose to skip or postpone, with reason.\r\n- Errors hit that haven't been resolved yet.\r\n\r\n## Constraints set by user\r\n- Model boundaries (e.g. \"Opus stays on planning\")\r\n- Naming conventions\r\n- Transport / package\r\n- Anything else the user explicitly locked\r\n```\r\n\r\n## Length target\r\n\r\nAim for **1500-3000 tokens** of output. Better to drop a detail than to bloat past 3000 — the entire point of /compact is making the context small.\r\n\r\n## What you're being given\r\n\r\nThe user message will be a JSON serialisation of the messages array to summarise — role + flattened content per turn. Tool calls and tool results have been pre-condensed into one-line labels like `[tool_use: sap_search_object({...})]` and `[tool_result: <first 500 chars>]`. Use them as evidence; don't echo them.\r\n\r\nBegin your output directly with `## Build state` — no preamble, no \"Here is the summary\" lead-in.\r\n",
275
+ "sha256": "5e92e7be845a6373d9bb7be2e1f30f004d44c20de1988fa3a43350ae200e4a0c",
269
276
  "signature": "",
270
- "signedAt": "2026-09-17T20:19:06.057Z"
277
+ "signedAt": "2026-09-28T14:37:05.319Z"
271
278
  }
272
279
  };