Skip to content

The last pages, and the reference gate that knew the old repository - #21

Merged
oblomov-dev merged 6 commits into
mainfrom
claude/happy-turing-qt6ljo
Sep 20, 2026
Merged

oblomov-dev merged 6 commits into
mainfrom
claude/happy-turing-qt6ljo

Conversation

@oblomov-dev

Copy link
Copy Markdown
Member

This finishes the plugin migration of the site. Two commits: the remaining pages, then verify-refs.

The pages

Most were prose edits. The User Exit page was not, because writing it truthfully meant first finding out that the thing it documents did not work — it described the port's own scan for a JS class implementing z2ui5_if_exit, and upstream's mechanism does not replace it (the class-repository lookup has no open-abap equivalent; measured). cap2UI5 grew defineExit in response — see cap2UI5/cap2UI5#76 — and the page now documents that, and says why the exit is registered rather than discovered.

Everything else on that page was measured against the running framework too, and most of it had been wrong:

claim reality
favicon field not a field of the config structure at all
title sets the tab title still a field, no longer read — the page carries a constant title
src is the locally served runtime, "which keeps the stack working offline" it is the OpenUI5 CDN. Exactly backwards: a server without outbound internet renders nothing
7 security headers 5. The caching ones come from the handler
context: method / session_id / tenant / body path / app_start / t_params
two roundtrip fields were missing entirely: check_trust_forwarded_host (matters behind a reverse proxy) and check_hide_error_details

The CDN correction propagates. Troubleshooting told readers to reinstall openui5-dist and check a /resources route, neither of which exists (the route 404s — measured), and its draft section named Z2UI5_DRAFT_TTL_HOURS, srv/server.js and engine.set_store, all the port's. What is cap2UI5 listed a mirrored app/z2ui5/webapp/ as a moving part. Why cap2UI5 drew a project tree with a service definition, a server.js and a frontend folder — the plugin's tree is package.json plus one app file. Roadmap's "rendered against 1.108 locally" is now about where UI5 comes from at all. Configuration and the API reference gained defineExit.

verify-refs

The gate that guarantees "what the docs claim about the code is still true" was itself claiming the old world — repo paths under core/ srv/ db/ app/, classes as files in the cap2UI5 checkout, imports through core/package.json's exports map. It reported ten problems that were all its own.

Disabling it would have reintroduced exactly the defect class it exists to catch, and its own header says why: the leniency of skipping "is right on a laptop and wrong in CI".

The ground truth split in two, so it reads two checkouts:

  • cap2UI5 — repository paths, the app ids defineApp registers, what require("cap2ui5") exports, the plugin's configuration keys, the pinned @abap2ui5/runtime release
  • abap2UI5 — the framework classes the docs name. They are not in cap2UI5; they are upstream's ABAP, transpiled into @abap2ui5/runtime, whose content is assembled and gitignored

That second checkout is the point: resolving classes against the assembled runtime would have passed on a laptop, where runtime/output exists, and checked nothing in CI, where it does not — the silent-pass failure mode --require-checkout was added to prevent, one level down. Same reason the path inventory skips runtime/output, runtime/setup and runtime/webapp.

New checks, each replacing something that stopped being checkable:

  • ?app_start= resolves against defineApp registrations, and accepts an id the page itself defines — a page teaching an app may name it
  • require("cap2ui5") is checked by its destructured names against the package's real exports, and the port's require("abap2UI5/…") is reported by name instead of silently passing
  • cds.cap2ui5.<option> must be an option the plugin defines. The regex is case-sensitive on purpose: cap2ui5.Drafts is the entity, which the docs name constantly
  • the release pin is read from runtime/package.json

srv/, db/ and app/ left the path roots deliberately: since the plugin, those are the reader's project, and checking them against this repository would flag correct prose.

Verified by planting one defect of each of the eight kinds in a page — all eight reported with file and line, page restored goes green. npm run check:ci and docs:build are green.

.github/workflows/check.yml gains the second checkout. AGENTS.md carries the new ground truth and the lesson the User Exit page cost: boot the runtime and measure before porting a claim from abap2UI5's documentation.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF


Generated by Claude Code

…s fired

§22 said these pages were correct today and would be wrong on merge day, and
that the trigger was step 4.3 rather than my having the knowledge in hand. That
day is today: cap2UI5/cap2UI5@main has carried the plugin since 13:17 UTC, so
the published site now documents a product that does not exist.

Starting where a reader starts. The landing page and the quickstart, rewritten
against the plugin as merged - not against my memory of it:

  index.md          npm i cap2ui5 rather than "clone the repo"; cap2ui5.Drafts
                    rather than z2ui5_t_01; upstream's runtime rather than an
                    ABAP->JS pipeline of ours. The "Try it in the browser"
                    button is gone: it pointed at web-cap2UI5-build, which is
                    archived as of today, and a dead button on a landing page
                    is worse than no button.
  getting-started   defineApp + c.bind/c.event/c.isDisplay rather than
                    `class x extends z2ui5_if_app` + client.view_display; one
                    file in srv/apps/ rather than a cloned CAP project; reading
                    your own entities with cds.ql, which is the point of
                    running inside CAP.

TWO THINGS I GOT WRONG AND MEASURED BEFORE PUBLISHING.

`npm add cap2ui5` answers 404 - neither cap2ui5 nor @abap2ui5/runtime is
published yet, both wait on a release. The page keeps the command as what you
WILL run and carries a danger box saying it is the one line that does not work
today, with the repository route that does.

And the start URL was wrong. I had written
/z2ui5/webapp/index.html?app_start=..., which answers 200 and 950 bytes - the
shell's own index. The entry point is the ROUNDTRIP ROUTE:
/rest/root/z2ui5?app_start=..., 346 KB, the composed page that boots UI5 and
starts the app. Measured against the running example, and it is what
browser.e2e.mjs opens - the test that actually renders in CI.

A third one the build caught: `Owner-scoped: a draft ...` in the landing page's
frontmatter is a colon in an unquoted YAML scalar, and vitepress refused the
whole file. Every details block is a >- scalar now, which retires the class.

`npm run docs:build` green. 34 of 36 pages still describe the port; the audit
splitting them is in HANDOVER.md and unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
Nine more pages, plus two removed and the navigation that pointed at them.

REMOVED, because they point at things that no longer exist:

  guide/playground.md     the playground ran out of web-cap2UI5-build, which
                          was archived today. A page whose whole subject is a
                          dead link is not worth a rewrite.
  guide/samples.md        generated by scripts/gen-samples.mjs, whose line 39
                          hardcodes core/srv/app/samples - deleted by the
                          plugin merge. The generator goes with it, and
                          check:ci no longer runs it.

The nav entries for both, the external Playground link and the
builder-cap2UI5-web link are out of config.mjs. package.json edited through
the JSON parser rather than by regex, because the regex left a trailing comma
and produced invalid JSON - caught by parsing it back before writing.

REWRITTEN:

  project-structure   there is no cap2UI5 layout. It is YOUR CAP project plus
                      srv/apps/; the framework lives in node_modules and
                      nothing is generated into the tree
  configuration       the real cds.cap2ui5 defaults, and what requires:null
                      actually costs (every anonymous caller shares one draft
                      owner, hence each other's sessions)
  database            cap2ui5.Drafts as it is now, including why owner is
                      `not null` and the comparison `!==` - the ownerless-row
                      defect as a lesson rather than a footnote
  persistence         stateful across roundtrips, stateless in memory; the
                      SIGKILL evidence including the nav-stack case
  architecture        host, not port: the three pieces, the two seams, what
                      defineApp does and why, the two ABI gates
  protocol            MEASURED against a running server, not described from
                      memory: PROTOCOL 2, S_ACTION.T_SYSTEM, no PARAMS. The
                      danger callout this page carried is gone - it said the
                      page described what cap2UI5 emitted and not what
                      abap2UI5 emitted, and that gap is what the merge closed
  deployment          no HTML5 module, no app-repo push: the shell is a
                      directory inside a dependency. Says plainly that the CF
                      topology is the standard CAP one and NOT exercised
  lifecycle           isFirstRun vs isDisplay, with the silent-wrong-screen
                      failure spelled out; the two retired members
  data-binding        c.bind takes a NAME; the type table; tables, structures
                      and nesting; the untypeable-field warning
  events              c.event with arguments, and why the wire tests could not
                      see that they were missing
  navigation          navTo/navBack/prevApp, and that the stack is in the
                      database - with the cold-restart measurement
  views               UI5 XML as a string; the two holes; and an injection
                      warning, because interpolating user input into markup is
                      not the same as binding it
  popups              messageBox/messageToast/popup/nest, incl. that without
                      `clear` every nest call adds another fragment

Build is not green yet: 9 dead links remain in 7 pages that still describe the
port and are next. Committing the block that is done rather than one commit for
thirty-odd pages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
  api/client         every member of `c`, grouped by what it answers:
                     lifecycle, binding, screen, navigation, the raw escape
                     hatch - and the two members that now throw
  api/app-interface  defineApp and t, including why numbers need t.packed and
                     an untypeable field is named in a warning rather than
                     dropped
  api/view-builder   REMOVED. It documented a fluent ABAP builder; a JS app
                     hands c.view() a template literal. The builder still
                     exists in the runtime for ABAP apps and is reachable
                     through c.raw, which guide/views says. The sidebar's
                     "View Builder" under Concepts is "Views" now.

Examples, and I have labelled each one by how far it is actually proven,
because "example in the docs" and "file the CI runs" are not the same claim:

  hello-world        VERBATIM from examples/bookshop/srv/apps/hello.js - wire
                     tests, cold-restart test and browser test all drive it
  list               VERBATIM from books.js, the app coexistence.test.mjs
                     drives; both directions through the database are measured
  selection-screen   assembled from tested parts, and says so. The event
                     branch runs BEFORE the render branch, which is the point
                     worth copying
  static-xml-view    a view read from a file. Ordinary Node, marked
                     illustrative; the trap named is a placeholder colliding
                     with a UI5 row binding
  external-odata     standard CAP remote services, marked NOT exercised by
                     this repository's suite - no test here calls one

what-is-cap2ui5 keeps its argument and loses its mechanics: the worked example
is defineApp now, the "vendored core package" is two node_modules entries, and
the claim that the frontend is "wire-format compatible, kept in sync by a
pipeline" is replaced by the stronger and simpler truth - it is the same
frontend, from the same commit as the backend.

where-it-comes-from is rewritten around the change of strategy: what the port
was, the 17 measured drifts and the frontend/backend split it found, and what
upstream had to open for hosting - including that the codepage fix was a bug
found here and fixed at the source for everybody.

Five dead links left, in the four pages still to do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
…t does not exist

The remaining background pages still carried the port's mechanics. Most were
prose edits; the User Exit page was not, because writing it truthfully meant
first finding out that the thing it documents did not work.

THE USER EXIT. The page described the port's own scan: a JS class implementing
z2ui5_if_exit, found by walking the app directories. That is gone with the
port, and upstream's mechanism does not replace it - abap2UI5 asks the class
repository which classes implement Z2UI5_IF_UI5_EXIT, and open-abap has none,
so the lookup raises and is swallowed. Measured rather than assumed. The page
now documents defineExit( ), which cap2UI5 grew in response (see its commit),
and says plainly why the exit is registered rather than discovered.

Everything else on that page was measured against the running framework too,
and most of it had been wrong:

  - `favicon` is not a field of the config structure at all
  - `title` IS a field but is no longer read: the page carries a constant
    title and an app sets the tab title itself
  - `src` defaults to the OpenUI5 CDN, not to a locally served runtime - so
    the old claim that this "keeps the stack working offline" was exactly
    backwards, and a server without outbound internet renders NOTHING
  - five security headers, not seven; the caching ones come from the handler
  - the context is path / app_start / t_params, not method / session_id /
    tenant / body
  - two roundtrip fields were missing entirely: check_trust_forwarded_host,
    which matters behind a reverse proxy, and check_hide_error_details

The CDN correction propagates: Troubleshooting's white-page section told
readers to reinstall `openui5-dist` and check a `/resources` route, neither of
which exists (the route 404s - measured), and its draft section named
Z2UI5_DRAFT_TTL_HOURS, srv/server.js and engine.set_store, all of them the
port's. What-is-cap2UI5 listed a mirrored `app/z2ui5/webapp/` as a moving
part. Why-cap2UI5 drew a project tree with a service definition, a server.js
and a frontend folder in it - the plugin's tree is package.json plus one app
file. Roadmap's "rendered against 1.108 locally" is now about where UI5 comes
from at all.

Configuration and the API reference gained defineExit, since "everything the
plugin reads lives under cds.cap2ui5" is only true of the plugin's own options.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
…turning it off

The gate that guarantees "what the docs claim about the code is still true"
was itself claiming the old world: repo paths under core/ srv/ db/ app/,
classes as FILES in the cap2UI5 checkout, imports resolved through
core/package.json's exports map. After the plugin cutover it reported ten
problems that were all its own - srv/apps lives in the example now, ZCL_JS_HELLO
is a defineApp registration rather than a file name, z2ui5_cl_ui5_view_builder
is in the runtime package.

Disabling it would have reintroduced exactly the defect class it exists to
catch, and its own header says why: the leniency of skipping "is right on a
laptop and wrong in CI".

The ground truth split in two, so the checker reads two checkouts:

  cap2UI5    repository paths, the app ids defineApp registers, what
             require("cap2ui5") exports, the plugin's configuration keys,
             and the pinned @abap2ui5/runtime release
  abap2UI5   the framework classes the docs name - they are NOT in cap2UI5.
             They are upstream's ABAP, transpiled into @abap2ui5/runtime,
             whose content is assembled and gitignored

That second checkout is the point. Resolving classes against the assembled
runtime would have passed on a laptop, where runtime/output exists, and
checked nothing in CI, where it does not - the silent-pass failure mode
--require-checkout was added to prevent, one level down. Same reason the path
inventory skips runtime/output, runtime/setup and runtime/webapp.

New checks, each replacing something that stopped being checkable:

  - ?app_start= now resolves against defineApp registrations, and accepts an
    id the PAGE itself defines - a page teaching an app may name it
  - require("cap2ui5") is checked by its DESTRUCTURED NAMES against the
    package's real exports, and the port's require("abap2UI5/…") is reported
    by name rather than silently passing
  - cds.cap2ui5.<option> must be an option the plugin defines. The regex is
    case-sensitive on purpose: cap2ui5.Drafts is the ENTITY, which the docs
    name constantly
  - the release pin is read from runtime/package.json

srv/, db/ and app/ left the path roots deliberately: since the plugin those
are the READER's project, and checking them against this repository would
flag correct prose.

Verified by planting one defect of each of the eight kinds in a page: all
eight are reported with file and line, and the page restored goes green.

AGENTS.md carries the new ground truth and the lesson the User Exit page cost:
the runtime is upstream's ABAP on open-abap, not everything upstream does
works here, so boot it and measure before porting a claim from abap2UI5's
documentation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
Two corrections that came out of measuring the plugin rather than reading it.

The retention section said drafts are deleted after four hours, full stop.
That WAS true and was a defect: the sweep was hard-coded while the framework
asked the user exit for draft_exp_time_in_hours, so a project raising the
expiry got drafts the framework would have resumed and the sweep had already
deleted. The plugin now asks the exit the same way; the page says so, and
Persistence points at the one knob instead of repeating the number.

The SQLite warning is new and is the kind of thing a reader meets on their
second worker rather than their first. A cap2UI5 app writes a draft on EVERY
roundtrip, so it hits the database far harder than a typical OData service.
@cap-js/sqlite 3 uses node's built-in node:sqlite, whose busy timeout is zero
- WAL keeps readers out of the way, but a second writing process now fails
immediately with "database is locked" instead of waiting a millisecond.

One line of project config fixes it, and the page gives it with the evidence:
in this project's own example, five of six suite runs failed without the
timeout and six of six passed with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
@oblomov-dev
oblomov-dev merged commit 9784ffe into main Sep 20, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants