Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# The gate: npm test on Node 22 and 24 - schemas against the recorded
# traffic (shipped validator and ajv), docs links and anchors, the portable
# profile, the conformance apps' abapGit format, the CLI, and the backend
# suite against BOTH reference backends:
# node-runtime @abap2ui5/node-runtime + the ABAP apps, transpiled in the job
# (open-abap-core fetched with git, cached by its commit)
# cap2ui5 a CAP project + @cap2ui5/cds-plugin + the JS apps
# plus abaplint over the ABAP apps. Each backend run takes seconds; the
# install dominates.
# The frontend job runs the frontend suite against the UI5 SPA of abap2UI5
# (the commit the spec is derived from, app/webapp only) in Chromium - the
# test job skips that test, it has no browser and no checkout - and records
# the reports of the UI5 SPA and the agent client.
name: ci

on:
push:
branches: [ main ]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
node: [ '22', '24' ]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node }}
cache: npm
cache-dependency-path: |
package-lock.json
conformance/hosts/cap2ui5/package-lock.json
- run: npm ci
- name: open-abap-core commit of the runtime
id: core-sha
run: echo "sha=$(node -p "require('@abap2ui5/node-runtime/package.json').abap2ui5.openAbapCore")" >> "$GITHUB_OUTPUT"
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .cache/open-abap-core
key: open-abap-core-${{ steps.core-sha.outputs.sha }}
- name: cap2UI5 host
run: npm ci --prefix conformance/hosts/cap2ui5 --no-audit --no-fund
- name: abaplint (ABAP conformance apps)
run: npm run lint:abap
- name: npm test (both backends)
run: npm test
- name: backend suite report - node-runtime
if: ${{ !cancelled() }}
run: node scripts/run-conformance.mjs node-runtime --json report-node-runtime.json
- name: backend suite report - cap2ui5
if: ${{ !cancelled() }}
run: node scripts/run-conformance.mjs cap2ui5 --json report-cap2ui5.json
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && matrix.node == '22' }}
with:
name: conformance-reports
path: report-*.json
if-no-files-found: ignore

frontend:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: abap2UI5 app/webapp (the frontend under test)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: abap2UI5/abap2UI5
ref: 5d7e91f904bf37b282e8e1c0ecb2b3eeae4c5842 # main; app/webapp as at b812079
path: deps/abap2UI5
sparse-checkout: app/webapp
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22'
cache: npm
- run: npm ci
- name: Chromium for playwright-core
run: npx playwright-core install --with-deps chromium
- name: frontend suite - UI5 SPA and agent client
env:
PROTOCOL_REQUIRE_BROWSER: '1'
PROTOCOL_FRONTEND_REPORT: report-frontend-ui5.json
run: node --test test/frontend.test.mjs
- name: frontend suite report - agent client
if: ${{ !cancelled() }}
run: node conformance/backend/bin/abap2ui5-conformance.mjs frontend --adapter agent --json > report-frontend-agent.json || true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() }}
with:
name: frontend-reports
path: report-frontend-*.json
if-no-files-found: ignore
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
.build/
.cache/
deps/
conformance/apps/deps/
conformance/hosts/cap2ui5/node_modules/
conformance/hosts/cap2ui5/srv/apps/
*.log
96 changes: 96 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# AGENTS.md — abap2UI5 protocol

Guidance for agents and contributors. Read before making any change.

## What this is

The specification of the abap2UI5 roundtrip protocol (`spec/`), its view
profiles (`profiles/`), JSON Schemas (`schema/`) and conformance suites
(`conformance/`), plus recorded real traffic (`traffic/`). It is
**descriptive first**: the protocol was implemented before it was written
down, and the spec records what the implementations do.

## Rules

- **Every normative statement cites its source.** A MUST/SHOULD names the
file and method it comes from, with the abbreviations of
`spec/README.md#sources`. A statement you cannot trace to code is a
proposal - say so, or leave it out.
- **Accidental behaviour is an implementation note, not a rule.** When the
reference does something odd (draft ids that repeat after a leave, the URL
reflected into a 500 body), write it under *Implementation note* - another
implementation must not have to copy it.
- **A rule a backend can break gets a check.** New MUST/SHOULD for backends ->
a check in `conformance/backend/lib/checks/`, its id on the spec's
*Checked by* line, `node scripts/gen-check-list.mjs`. `npm test` fails when
a check cites a missing section or the check list is stale.
- **A rule a frontend can break gets a frontend check** - in
`conformance/frontend/lib/checks/`, its id on the spec's *Frontend
check(s)* line, the list regenerated the same way. A check scripts the
backend (`t.mock.reply(...)`, recorded responses from `traffic/` first,
synthetic ones for edge cases), drives the frontend only through the
adapter interface (`fill`, `press`, ... with targets every kind of
frontend can resolve) and reads the normalized `state()`. A check that
needs more than the core (a URL, timers, a DOM) declares it in `needs`.
- **A deviation of a real frontend is a finding, not a test to bend.** Decide
whether the spec or the frontend is wrong, write it into
`conformance/RESULTS.md` (and the spec, with its source, when the spec
was wrong), pin the result in `test/frontend.test.mjs`, file it upstream.
- **Provisional decisions go to `spec/open-questions.md`** with the current
decision and the alternatives, so the maintainer can decide.
- **Behaviour the suite needs lives in a conformance app, twice.** A new app
or event goes into `conformance/apps/README.md`, the ABAP class
(`conformance/apps/abap`, abapGit format, abaplint-clean:
`npm run lint:abap`; also clean in the abap2UI5 linter) AND the cap2UI5
twin (`conformance/apps/cap2ui5`). `test/traffic.test.mjs` requires both
backends to answer every check identically.
- **Traffic is recorded, never hand-edited.** `npm run record` rewrites
`traffic/`; record again after changing a check or an app.
- **Generated sections are generated.** `conformance/backend/README.md`
(check list), `profiles/portable.md` (between the `portable:*` markers,
from `profiles/portable-v1.json`) - run `npm run generate`.
- **`profiles/semantic.md` is the normative snapshot v1** (moved from
abap2UI5/mcp-server `docs/agent-snapshot.md`). Changes to the snapshot
shape are made here first; implementations follow.
- **The protocol number moves only with a breaking change**
(`spec/versioning.md`). Additive changes keep protocol 2.
- **Do not edit other repositories from here.** A difference found in a
backend is filed in `conformance/RESULTS.md` (and upstream), not patched.

## Layout

| Path | |
|---|---|
| `spec/` | the core protocol, one file per area |
| `profiles/` | UI5, portable (v1 + `portable-v1.json` + coverage) and semantic profiles |
| `schema/` | JSON Schemas 2020-12 |
| `conformance/backend/` | the backend suite - the published package's entry (`index.mjs`, `bin/`) |
| `conformance/frontend/` | the frontend suite - `lib/` (scripted backend `mock.mjs`, runner, response builders, `checks/`), `adapters/` (`ui5` Playwright + the boot page, `agent` + the vendored mcp-server client, `webcomponent`, `headless` stub) |
| `conformance/apps/` | the conformance apps (ABAP + cap2UI5) and their abaplint config |
| `conformance/hosts/` | `node-runtime` (build + serve) and `cap2ui5` (a CAP project) reference hosts |
| `traffic/` | recorded traffic per backend: `suite.json`, `ui5-frontend.json`, `agent-client.json` |
| `scripts/` | recorder, runner, generators, `vendor-agent-client.mjs`; `scripts/lib/ui5-frontend.mjs` runs the UI5 frontend's request code in Node |
| `tools/portable-census/` | the census scripts behind the portable profile, as run |
| `test/` | `node:test` - `npm test` |

## Gates

`npm test` is the gate: schemas (shipped validator vs ajv, strict mode),
recorded traffic, cross-backend equality, docs links and anchors, the
portable profile, the conformance apps' abapGit format, the CLI, the
full backend suite against both reference backends (`PROTOCOL_SKIP_BACKENDS=1`
skips those; the cap2UI5 run skips itself when its host is not installed),
and the frontend suite against the agent client (always) and the UI5 SPA
(when an abap2UI5 checkout and a Chromium are there;
`PROTOCOL_SKIP_BROWSER=1` skips it, `PROTOCOL_REQUIRE_BROWSER=1` fails
without them). CI runs the same on Node 22 and 24, and the UI5 SPA in a
job of its own with Chromium. Never `playwright install` in a sandbox that
has browsers already - point `CHROMIUM_BIN` at one.

## Style

ES modules, Node 22+, no runtime dependencies in `conformance/backend/`
and `conformance/frontend/` (the browser adapters import `playwright-core`
lazily). English, ASCII in source files - the vendored copies under
`conformance/frontend/adapters/vendor/` are byte-equal to upstream and
exempt. Markdown wrapped at ~78 columns.
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Changelog

## Unreleased

- **Frontend conformance suite** (`abap2ui5-conformance frontend --adapter
ui5|agent|webcomponent [--profile core|portable|ui5|semantic]`, library
`runFrontendSuite`, also at `@abap2ui5/protocol/frontend`): a scripted
backend answers the frontend under test from the recorded traffic plus
synthetic edge cases (PROTOCOL 3, no PROTOCOL, 500 with markup, CSRF token
layer, unknown follow-up action, NEST slots, a model push of another
app); 81 checks (66 MUST, 15 SHOULD) over the request side (envelope, ID
continuation, event arguments, model delta, CONFIG cadence, headers,
stateful session id, CSRF handshake, no retry of a 500, one roundtrip at
a time) and the response side (PROTOCOL, unknown keys, view slots, model
push, follow-up actions, messages, router, errors) per profile.
- **Adapters:** the UI5 SPA (abap2UI5 `app/webapp`) in Chromium via
Playwright, UI5 from the `@openui5` npm packages (no CDN); the agent
client of abap2UI5/mcp-server (vendored at `ea4e9fa`, `npm run
vendor:agent`); the UI5 Web Components frontend (`dist/abap2ui5-wc.js`);
a documented stub for the headless ABAP simulator (no HTTP seam yet).
- **Results** ([conformance/RESULTS.md](conformance/RESULTS.md#frontend-suite)):
the UI5 SPA passes every MUST but `portable.box-details` (message box
details empty on OpenUI5 >= 1.120); the agent client fails 5 MUSTs.
- **Specification revision 0.2** - the frontend side made explicit where the
suite found it implicit, each with its source: a frontend sends event
arguments as raw JSON values and never converts them itself; an app start
names its class; `MODEL` carries only edited attributes, a pushed value is
no edit; a table cell travels as `__delta` (SHOULD) or as the whole
table; `sap-contextid-accept: header` SHOULD; the session id is kept by
every HTTP frontend; "one roundtrip at a time" covers programs driving a
frontend; a PROTOCOL mismatch adopts nothing; an error body is shown
verbatim, its markup not interpreted; message box details are shown with
the box; the UI5 frontend is no longer called "a portable frontend by
construction" - it renders every portable app but fails a view on an
element it cannot load (`profiles/portable.md`). Every frontend rule names
its frontend checks.
- **`spec/open-questions.md`**: the provisional decisions for the
maintainer - the NEST rule, a new draft id per response, the URL in the
error body, boolean arguments for non-ABAP backends, the detail of an
app's error, and five from the frontend suite.
- CI: a `frontend` job (Chromium, abap2UI5 `app/webapp` at `5d7e91f` (main))
runs the UI5 SPA; `npm test` runs the agent client always.

## 0.1.0 - 2026-10-03

First version.

- **Specification** of protocol 2 (revision 0.1), derived from abap2UI5
1.146.0: core (`spec/core.md`), transport, request, response, actions,
sessions, navigation, errors, versioning - every normative statement
traced to its source.
- **View profiles:** UI5 (`profiles/ui5.md`); portable v1
(`profiles/portable.md`, `portable-v1.json`, `portable-coverage.md`) from a
census of 247 core apps and 642 samples-controls ports; semantic - agent
snapshot v1, moved here from abap2UI5/mcp-server as the normative version.
- **JSON Schemas** (2020-12) for the request, the response, the snapshot and
the portable profile.
- **Backend conformance suite** (`abap2ui5-conformance backend`, library
`runBackendSuite`): 75 checks, profiles `core` and `ui5`.
- **Conformance apps** `Z2UI5_CL_CONF_*` in ABAP (abapGit) and as cap2UI5
apps; reference hosts for `@abap2ui5/node-runtime` and cap2UI5.
- **Recorded traffic** of both reference backends (suite, UI5 frontend code,
agent client). Both pass every MUST; cap2UI5 warns on `error.details`.
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# CLAUDE.md

All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source of
truth for this repository (what is normative and how it is derived, the
spec/schema/conformance layout, the generated sections, the reference
backends and the test gates).

Read `AGENTS.md` before making any change.
Loading
Loading