14 KiB
PRD — arena-plm (standalone rebuild)
Vision & scope
I am rebuilding the Arena PLM → SAP S/4HANA integration as a proper SAP
application that lives in the tenant. The predecessor (Express.js,
~/projects/sap-apps/arena-plm) works as a demo but fails the test that
matters: a business user inside S/4HANA cannot find it — not in the Fiori
launchpad, not in the Custom CDS Views app — because everything was built
side-by-side behind standalone URLs with ad-hoc access control.
The acceptance test I hold this rebuild to: a business user finds the application in the S/4HANA Fiori launchpad, finds its data in the Custom CDS Views app, and access is governed by business roles — with the side-by-side footprint reduced to the one thing that cannot run in the tenant: Arena connectivity.
In scope: material, BOM, change-master, and document sync from Arena to S/4HANA, gated by a human approval workflow. Out of scope: every other package of the old estate — one package at a time.
Target architecture (ADR-002, 2026-09-04)
Arena PLM ──webhook + delta poll──▶ Connectivity worker (BTP CF, hardened TS)
│ no UI, no user URLs; technical user
reads approved / │ writes staged changes
executes sync ▼ ▼ via approval BO service binding
┌─ SAP S/4HANA Cloud Public (my429480) ─────────────────────────────────────┐
│ Z RAP BO "Arena Change Approval" ← staging + state machine + audit │
│ Fiori Elements List Report / Object Page ← in the tenant LAUNCHPAD │
│ IAM app → business catalog → business roles ← the only access control │
│ Custom CDS views over the BO (join I_Product, …) ← Custom CDS Views app │
│ Released OData APIs (Product/BOM/ChangeMaster/Attachment) ← sync targets │
└───────────────────────────────────────────────────────────────────────────┘
Build state (2026-09-14): the Fiori Elements UI layer is scaffolded as
com.minicircuits.arenaplm (List Report / Object Page, TS) in the structural app/ subfolder
(sibling to worker/), running on a mock OData V4 service — an interim, generator-iterable
stand-in. The real on-stack RAP BO + service binding (ARN-002, V-2) and
launchpad deployment (ARN-007) remain gated on developer extensibility (V-1).
The predecessor arena-plm on CF is verified live (/api/health 200) and is
the base of record for the domain/field shape until the RAP service exists.
Systems & boundaries
| System | Role | Boundary |
|---|---|---|
SAP S/4HANA Cloud Public (my429480) |
Application host (RAP BO, Fiori app, CDS views) and sync target | Developer extensibility (ABAP Cloud via ADT); released APIs only |
Arena PLM (api.arenasolutions.com/v1) |
Source of items, BOMs, changes, files | REST; webhook + delta poll, handled by the worker |
| Connectivity worker (BTP CF) | Arena connectivity + initial sync executor | Machine-to-machine only; technical user via communication arrangement |
Interface contracts
Carried from the predecessor, in live use (2026-09-04); re-verify on first POC touch. The worker keeps calling these on approval, so every KNOWLEDGE.md tombstone remains valid.
| API | OData | Path | Notes |
|---|---|---|---|
| Product | V4 | /sap/opu/odata4/sap/api_product/srvd_a2x/sap/product/0002 |
deep insert for plant extensions |
| Product (plant fix) | V2 | /sap/opu/odata/sap/API_PRODUCT_SRV |
AvailabilityCheckType is V2-only |
| BOM (write) | V2 | /sap/opu/odata/sap/API_BILL_OF_MATERIAL_SRV;v=0002 |
header first, then sequential items |
| BOM (read) | V2 | /sap/opu/odata/sap/API_BILL_OF_MATERIAL_SRV |
v1 only; v2 rejects guid keys |
| Change Master | V2 | /sap/opu/odata/sap/API_CHANGEMASTER;v=0002 |
object-type assignments, material linking |
| Attachment | V2 | /sap/opu/odata/sap/API_CV_ATTACHMENT_SRV |
function import for URL creation |
| Approval BO — UI binding (Fiori app) | V4 UI | /sap/opu/odata4/sap/zzui_arena_change_apprvl000_o4/srvd/sap/zzui_arena_change_apprvl000_o4/0001/ (binding ZZUI_ARENA_CHANGE_APPRVL000_O4, generated 2026-09-15, unpublished) |
expected namespace com.sap.gateway.srvd.zzui_arena_change_apprvl000_o4.v0001, alias SAP__self, entity ChangeApproval_Type/set ChangeApproval; confirm from live $metadata after publish (V-2); Fiori app still carries the pre-generation provisional name |
| Approval BO — Web API (A2X) binding (worker) | V4 Web API | binding ZAPI_ARENA_CHANGE_APPROVAL_A2X on definition ZAPI_ARENA_CHANGE_APPROVAL (pending: created after the A2X projection is active) |
custom communication scenario, technical user; namespace com.sap.gateway.srvd_a2x.zapi_arena_change_approval_a2x.v0001 expected; exposes create + read + markExecuting / markSynced(SapChangeNumber) / markFailed(ErrorMessage) (RAP-6) |
| Worker inbound surface | REST (OpenAPI 3.1) | docs/api/worker.openapi.json |
webhook intake (503 when buffer full), health (ok/degraded, session none/active/expired/refreshing), sync-run admin (strict request schema); contract v0.2.0 (2026-09-14 wave); auth scheme pending OQ-6 (ARN-009) |
Requirements
Capability level — field mappings deferred to the POC per my POC-before-spec rule.
| ID | Requirement | Status |
|---|---|---|
| ARN-001 | Sync chain PRODUCT → CHANGE_MASTER → BOM → DOCUMENTS in config-driven order, executed by the worker on approval | Planned |
| ARN-002 | Approval domain as an on-stack RAP BO: staging fields, state machine PENDING → APPROVED → EXECUTING → SYNCED | FAILED, REJECTED terminal, FAILED → retrySync → EXECUTING (amended 2026-09-14: a terminal success state is required so an approved record is never executed twice); approve/reject/retrySync as RAP actions with instance feature control; audit fields (RequestedBy/RequestedOn/DecidedBy/DecidedOn); execution failure is a distinct state, never overloaded onto rejection (predecessor defect, docs/predecessor-flow.md §2) |
Planned |
| ARN-003 | Durable persistence & audit — satisfied structurally by in-tenant RAP persistence (the predecessor lost its queue on every restart) | Planned (by design) |
| ARN-004 | Business mappings maintained in-tenant — evaluate Business Configuration maintenance objects (Custom Business Configurations app) before any file-based config | Planned |
| ARN-005 | Arena ingestion via webhook + delta poll in the connectivity worker | Planned |
| ARN-006 | Idempotent SAP writes, SAP-as-ledger (see KNOWLEDGE.md) — predecessor BOM/document syncs were non-idempotent; executors must check existence first |
Planned |
| ARN-007 | Access exclusively via the Fiori launchpad with IAM app / business catalog / business roles; no user-facing standalone URLs | Planned |
| ARN-008 | Approval data discoverable via custom CDS views, joinable to released views (the discoverability test); look up join targets with object_type=CDS_STOB in sap-released-objects (no DDLS type in the public_cloud dataset, KNOWLEDGE 2026-09-14) |
Planned |
| ARN-009 | API-first: every interface this package owns carries an OpenAPI 3.1 JSON contract under docs/api/, authored before or with the change; implementation conforms to the contract |
Adopted (2026-09-10) |
| ARN-010 | The approved payload is the executed payload: the executor writes the staged data the approver saw — never re-fetch-and-recompute at execution time (the predecessor discarded the preview, making approval decorative; docs/predecessor-flow.md §2) |
Adopted (2026-09-10) |
RAP-track requirements harvested from the UI (2026-09-14)
Derived while binding the Fiori app; each becomes a rap-developer brief
once V-1 opens the development tenant.
| ID | Requirement | Source |
|---|---|---|
| RAP-1 | Behavior definition declares approve, reject, retrySync with ( features : instance ) and implements get_instance_features so the UI service emits Core.OperationAvailable; approve/reject only for PENDING, retrySync only for FAILED. approve writes no SapChangeNumber (execution does). The CDS emits numeric StatusCriticality 2/2/5/3/1/1 for PENDING/APPROVED/EXECUTING/SYNCED/REJECTED/FAILED (5 = Information is V4-only) |
[source: abap-docs /abap-docs-standard/ABENBDL_ACTIONS_FC; sap-docs /abap-fiori-showcase/01_general_features#operations-and-actions, verified 2026-09-14] |
| RAP-2 | reject takes a mandatory RejectReason (char 255) parameter; approve/retrySync return $self; the entity is read-only from the UI (no create/update/delete) |
UI contract (annotation.xml, mock metadata) |
| RAP-3 | Status carries a text element (@ObjectModel.text.element, @UI.textArrangement: #TEXT_ONLY) so the UI shows text, not codes |
[source: fiori-mcp-server "Text and Text Arrangement", verified 2026-09-14] |
| RAP-4 | Semantic key ItemNumber (@ObjectModel.semanticKey); Status and RequestedOn sortable/filterable |
UI default variant |
| RAP-5 | Two service bindings on definition ZUI_ARENA_CHANGE_APPROVAL: OData V4 UI binding ZUI_ARENA_CHANGE_APPROVAL_O4 for the app and an OData V4 Web API binding for the worker; the binding name sets the URL first segment and the namespace; hand back the live $metadata of the UI binding to reconcile (V-2) |
[source: sap-docs sap-help loio 5717c0a1dafc4335add0207b81c39cef, 5f45f125dfe84c20beb18f5de1512f5a, verified 2026-09-14] |
| RAP-6 | Worker-callable execution-state actions on the A2X binding — markExecuting, markSynced (sets SapChangeNumber), markFailed (sets ErrorMessage) — with instance feature control so the UI never offers Approve on an executing record |
[source: abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC, verified 2026-09-14] |
| RAP-7 | Staged payload (SapPreview) is abap.string — CDS char elements are capped at 1333 characters and truncation would break ARN-010 |
[source: abap-docs /abap-docs-cloud/ABENCDS_SELECT_LIST_ENTRY_V2, verified 2026-09-14] |
| RAP-8 | Field DecidedBy (not ApprovedBy) holds approver or rejecter; semantic key ItemNumber, ChangeType, ArenaEcoId |
UI contract |
Architecture decisions
- ADR-001 — toolchain-must-engage rebuild; Express predecessor reference-only. Amended by ADR-002.
- ADR-002 — on-stack first: RAP + Fiori Elements in the tenant; side-by-side reduced to a connectivity worker; CAP exits the architecture.
Verification items & open questions
| ID | Item | Status |
|---|---|---|
| V-1 | Developer-extensibility access on the development tenant | Closed 2026-09-15 — my431353.s4hana.cloud.sap: business user AZAW holds ZBR_DEVELOPER (SAP_BR_DEVELOPER template), ZBR_ADMINISTRATOR_SW_DEV, ZBR_DEVELOPMENT_MANAGER_DIN and five more (live: sap-api users getRoles, verified 2026-09-15). V-1b done 2026-09-15 (destination 0M7DEJI, MCP lists it). Role correct (A4C catalogs). V-1b closed 2026-09-15: package ZARENA_PLM under ZLOCAL; probe class created and activated via ADT MCP |
| V-2 | Custom communication scenario can expose the approval BO's A2X service to the worker's technical user; and the Fiori app's annotation namespace, _Type entity-type suffix and UI-binding URL segment are reconciled from the live $metadata of the UI binding (decided 2026-09-14; the mock cannot verify this) |
Open |
| V-3 | On-stack Application Jobs vs worker-side polling for the Arena delta | Open — flow findings 2026-09-10: Arena's outbound-integration queue holds the cursor (reconciled flag) and is the clean delta mechanism; /changes-feed polling needs client-side date filtering (docs/predecessor-flow.md §1) |
| V-4 | Can a Claude Code agent outside the VS Code editor author ABAP source through the ADT MCP Server? | Answered 2026-09-15: no. create_object and the generators take structure, not source (live probe); SAP's editing MCP tool is planned for Public Edition 2702 [source: sap-docs loio 7f716223a88c40e0992777c8d9febccf, verified 2026-09-15]. Build plan: x-ui-service generator for the BO skeleton, source edits handed over as files for the VS Code editor until 2702 |
| OQ-5 | Long-term: move the sync executor on-stack (released ABAP APIs) vs keep it in the worker — worker initially | Open |
| OQ-6 | Webhook receiver hosting and auth for the worker on CF | Open |
Status
| Work item | State |
|---|---|
| Package home + governance | Done (2026-09-04) |
| Developer environment | Done (2026-09-04); MCP loadout revised to ABAP/RAP set (done 2026-09-04) |
| Ground-truth enforcement kit (ADR-003 hooks) | Done (2026-09-04); hardened 2026-09-14 — Bash/MultiEdit/filesystem writes gated, grounding-only marker refresh, portable paths, fail-closed, 37 pipe-tests green |
| Specialist agent roster (5 agents, docs/agent-roster.md) | Done (2026-09-04; all 5 files YAML-verified in .claude/agents/) |
| V-1…V-3 verification POC | Planned — V-1 (ADT access on my429480) is the user-side gate for everything on-stack |
Fiori Elements app (app/, mock-bound) |
Done for the mock stage (2026-09-14) — gateway service shape (binding-derived identity), six-state domain, polished List Report/Object Page, launchpad inbound ArenaChangeApproval-manage; SAP review findings closed. Deploy gated on V-1/V-2 (live $metadata reconciliation) |
| RAP BO (approval domain) | In progress — skeleton generated and active in ZARENA_PLM (2026-09-15, ATC clean); waiting on owner: paste docs/handover/rap/ sources in VS Code per its README, then I activate/ATC/create the A2X BDEF + binding; IAM app/catalog and publishing are the next brief |
| Connectivity worker | Review wave Done (2026-09-14) — TTL wiring, cursor-after-success, buffer-full = tick failure, health conformance, strict JSON-only validation, bounded buffer, graceful drain, HTTP timeout, fixed-phrase errors; 137 tests green incl. the schema-driven contract-conformance suite. Next: Arena client POC (live), executor leg gated on V-2. Do not deploy the scaffold: the ingest buffer has no consumer yet, so a long-running instance would answer 503 after WEBHOOK_QUEUE_MAX_SIZE events. Next: Arena client POC (live), executor leg gated on V-2 |
Worker API contract (docs/api/worker.openapi.json) |
v0.2.1 Done (2026-09-14) — health enums/degraded/poller, strict SyncRunRequest, webhook 503, 413/415 + default on every operation, reserved 502, DOCUMENTS token; x-sap-stateInfo: beta, provisional webhook schema pending Arena POC |