Skip to content

Roadmap §§8–22: the conformance findings, the host strategy, a security review, and the handover - #20

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

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

Conversation

@oblomov-dev

@oblomov-dev oblomov-dev commented Sep 19, 2026

Copy link
Copy Markdown
Member

The written record of the work in cap2UI5/builder-abap2UI5-js#29, cap2UI5/cap2UI5#72 and the upstream seams PR, plus a page for whoever does the parts that need a person.

Important

This PR changes no documentation page. It adds ROADMAP sections and rewrites HANDOVER.md. The 36 pages under docs/ are left exactly as they are, on purpose — see §22 below.

ROADMAP.md §§8–22

Fifteen sections, each written when the thing it describes was measured, not afterwards:

  • §8–9 the wire-conformance findings: the port's frontend and backend speak different protocols, and build_core produced a hanging core.
  • §10 two corrections, including one to my own earlier analysis: I claimed the 41 transpiled framework classes were "neither maintained nor understood" and should be dropped. Measured, none is shadowed by src/ — all are fill-ins. The recommendation was withdrawn before anything was implemented.
  • §11–12 the four upstream seams, the JS-app finding, the CDS draft store, the cold-restart proof.
  • §13 the synchronous app API.
  • §14 the plugin shape, and a correction to §12: owner coming out anonymous was reported there as "no auth configured". It was not — the route was mounted outside cds.middlewares.before, so the store could never see a user, and a draft answered to the wrong person. Fixed and tested.
  • §15 everything that could be done without an org owner: tables and structures as app state, SQLite and performance measured, concurrency tested, the runtime package job, ADR-008.
  • §16 it renders in a browser, and the final round is prepared.
  • §17 the facade's second slice (popups, nested views, navigation, event arguments) and the three defects it exposed — including two facade members named after the wrong thing, one of which was documented obsolete and does nothing.
  • §18 the claim the project rests on, finally tested: a plain CAP OData service running beside the apps, one authorization for both doors, and cap2ui5.Drafts not reachable through it.
  • §19 "unsupported" turned out to mean "not attempted": nested structures and tables were called a framework limitation in three documents and were a guard in my own type derivation.
  • §20 a security review of my own authorization code, which found a real defect: the draft store's three ownership checks tested the truthiness of the stored owner rather than its presence, so an ownerless row belonged to everybody rather than to nobody — with the proof of concept, the three smaller findings fixed alongside, and what the review cleared.
  • §21 "it fails on main too" is an attribution, not a diagnosis — the three red checks across two PRs, root-caused rather than merely attributed (one test was merged red by main's own head), plus a correction to two numbers I had posted about the builder-abap2UI5-js ratchet.
  • §22 the documentation is correct today and wrong on merge day — the audit below.

docs/reference/protocol.md gains a danger callout: the page documents S_FRONT.PARAMS, which is what cap2UI5 emits and no longer what abap2UI5 emits.

HANDOVER.md — rewritten, not patched

It had gone stale in a way that mattered: it told a human to open pull requests that are open, with suggested bodies naming an interface that has since been renamed, a workflow job that was reworked, and a file that moved — and it sent step 3 to the builder repository the plugin has since left. It also said to create an npm organisation that already exists and carries three packages.

It now says what to do with each pull request rather than repeating its body, carries the root cause of every red check a reviewer will see, and lists the facade slice as done instead of pending. The one warning that matters is unchanged: disable update_cap in builder-cap2UI5 before merging the plugin, or it overwrites it the next night.

And the part step 4 had left out: what the cutover does to these pages

Merging the plugin invalidates most of this repository's documentation on the same day. Measured over docs/36 pages, 5,434 lines, 27 of them describing the port. Only ROADMAP.md, HANDOVER.md and one line of reference/database.md know the plugin exists.

It is not stale paths. The whole guide teaches the port's app API, and the plugin's is a different API rather than a renamed one:

class my_app extends z2ui5_if_app {     // the plugin: defineApp("ZCL_X", class {
  async main(client) {                  //   main(c) {           <- synchronous
    if (client.check_on_init()) {  }   //     if (c.isFirstRun) { … }
    client.view_display(xml);           //     c.view(xml);
  }                                     //   }
}                                       // })
pages lines what happens
A structure & reference 12 1,949 rewritten from scratch
B app-authoring API 15 2,416 rewritten against defineApp
C survives with corrections 9 1,069 arguments hold, mechanics do not

One finding stands on its own: docs/guide/samples.md is generated and its generator breaks. scripts/gen-samples.mjs line 39 hardcodes …/cap2UI5/blob/main/core/srv/app/samples as its source and lines 122/188 write that path into the prose. The plugin PR deletes that folder, so the page's 98 rows lose their input — silently, into a stale page rather than an error.

Why nothing here is rewritten. These pages are correct today: they describe the cap2UI5 that is published and working. Rewriting them into the future tense would swap accurate documentation for speculative documentation, and a rejected approach would leave the repository with neither. The trigger is step 4.3 of the handover. The maintainer decision about where the docs live belongs before that rewrite, since it decides whether 3,400 lines land here or in the plugin repository.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF


Generated by Claude Code

…an old wire

Building the conformance gate the earlier analysis called for turned up two P0s
in the shipped product, so ROADMAP.md gains a section §8 with the measurements
and two corrections — one to its own §1 (the health crons watch each hop's
success, and every hop succeeded while republishing a frozen artefact), one to
the 2026-09-17 analysis that preceded the work (its recommendation to drop the
41 transpiled framework classes was wrong: none of them is shadowed by a
hand-port, they are all fill-ins).

docs/reference/protocol.md opened by claiming the wire format is "identical to
abap2UI5". Measured against upstream's live Node runtime, it is not: the page
documents S_FRONT.PARAMS, upstream now sends S_FRONT.S_ACTION action rows and
has flattened two-way bindings out of MODEL.XX. The page is still accurate for
debugging cap2UI5's own backend, so it keeps its content and gains a danger
callout saying which of the two it describes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
Adds ROADMAP §9: the draft-store and app-serializer seams that landed upstream,
and the spike they made possible — a serializer written in plain JavaScript,
installed through set_serializer( ), carried hi_world's app state across two
roundtrips with no CALL TRANSFORMATION and no S-RTTI in the path.

Also records what the spike does NOT prove, because the distinction decides
whether anyone can plan on it: the probe kept the live container in a Map rather
than projecting it to a persisted shape, so reconstruction from a real JSON
document in a cold process is still untested. The §8 blocker is gone; the next
unknown is one step further in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
Naht 3 (guarding the codepage fallback upstream) closes the second P0 from §8:
the modified upstream was mirrored into builder-abap2UI5-js, re-transpiled and
rebuilt, and the rebuilt core answers a roundtrip instead of hanging — so the
hi_world fix from §8 reaches the package too. Naht 4 puts a version on the wire,
which does not fix the protocol break but ends its silence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
ROADMAP §11. Measured with a transpiled ABAP app as the control on every run:
_bind( ) over a JS field resolves to {/NAME}, the two-way delta lands on the JS
object, and the state survives a roundtrip with app_cont's process buffer
cleared, so the second roundtrip comes back through the draft document.

The enabling detail is a declared 'static ATTRIBUTES' map in the shape a
transpiled ABAP class carries - without it the framework answers BINDING_ERROR.
So a cap2UI5 app is a plain class plus a schema declaration, which is an API
decision rather than a derivation.

Not proven: a genuinely cold second process. The default SQLite client is
in-memory only, so that test needs the CDS-backed store, which is the next item.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
ROADMAP §12. A CDS entity plus a ~90-line JS implementation of
z2ui5_if_ui5_draft_store, installed with one set_instance( ) call, puts the
framework's session state in the project's own database under its connection,
transactions and authorization.

Proven across a SIGKILL: process A writes the draft, process B is a fresh boot
with an empty app_cont buffer, and both the transpiled ABAP control app and a
plain JS app answer correctly from the persisted row. That is what the two
earlier spikes were explicitly flagged for not showing.

Boundaries recorded too: the UI has never rendered in a browser here (the
sandbox proxy blocks the UI5 CDN), no authorization was exercised, and the raw
ATTRIBUTES map still wants an ergonomic wrapper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
ROADMAP §13. The ATTRIBUTES schema derives itself from the class's own typed
fields, so an app is a plain class with plain values; and the app surface is
synchronous, because the awaits were never real - _bind( ) and _event( ) await
nothing but their own internal calls. Queries resolve before main( ), commands
are recorded and replayed after it, and c.event( ) returns a token that is
substituted at flush time.

Also records that the prototype moved out of the scratchpad into
builder-abap2UI5-js/docs/prototypes/open-abap-cap - 548 hand-written lines plus
reproduction steps, with the transpiled output gitignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
… what needs a person

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

Everything in this project had been reviewed for whether it worked. The owner
binding - the one piece that decides who sees whose data - had never been
reviewed for whether it could be DEFEATED. So I ran a security review over the
cap2UI5 branch diff with my own code as the suspect.

It found a real one, and a proof of concept confirmed it: all three ownership
checks in draft-store.js were guarded by the TRUTHINESS of the stored owner
rather than by its presence, so a Drafts row whose owner was NULL or "" belonged
to everybody rather than to nobody. bob replayed alice's draft id against such a
row and was handed alice's running app, complete with her model. The only
difference between the refused request and the served one was the emptiness of
one column.

The section carries the measurement, the two realistic ways such a row arises
(`?? ` does not catch "", and cds.User permits an empty id; and cap2ui5.Drafts
is an ordinary entity in the project's model), the fix, and the three smaller
findings fixed in the same pass - internal error text reaching the client, the
body being buffered in front of the authorization guard, and CI running another
repository's code with a write-capable GITHUB_TOKEN.

It also records what the review CLEARED, because each looked like a finding
until it was checked: cds.ql parameterizes, the event token cannot be forged
from app state, and @abap2ui5/runtime is not a dependency-confusion target -
the package is unpublished but the npm scope is claimed.

The lesson, which outlives the fix: an authorization check compares presence,
never truthiness. `&&` in front of a comparison in an access decision turns a
missing value into a wildcard. I wrote that line three times in one file and
reviewed it twice for whether it worked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
@oblomov-dev oblomov-dev changed the title Roadmap §§8–16: the conformance findings, the host strategy, and the handover Roadmap §§8–20: the conformance findings, the host strategy, a security review, and the handover Sep 19, 2026
…DOVER

Three red checks across two PRs carried the same note: "fails identically on
main, therefore not this branch's". True, and verified - but that is an
ATTRIBUTION, not a diagnosis. It says whose the failure is and leaves the
reviewer to find out what it is. §21 does the second half.

devtoolsConsole: `git log` on both files ends at 1f2cccb (#2771), which is
main's HEAD and which added MAX_NODES and the test together - so the test was
merged red and has never passed. The cause is a property a JSON.stringify
replacer cannot have: the third assertion asks it to remove a KEY, and a
replacer answers a key's VALUE. Three-line patch proposed on the PR, verified
green (29/29) and verified to discriminate. Measured what the cap is worth
while I was there: 11% on the test's own input, 52% on the row-shaped lookup
the code comment names.

shared-file-gate: #2771's own commit message predicts one of the four drifts
("check:shared stays red until that follow-up lands"); the other three are
sync-shared.yaml against the samples repositories, from #2719 on 2026-09-05 -
a fortnight before this branch existed.

And a correction I owed. Re-measuring the builder-abap2UI5-js ratchet to check
my own proposed patch, I found I had read one of two arrays and reported the
total as if it were the one: it is 104 regressions and 57 fixed-but-still-
listed, not 161 and 0. 57 entries of a 131-entry list now pass, which makes
the re-baseline recommendation better than I described it. cs_event likewise
carries 42 constants on both sides, not 42 against 36 - six are renamed, which
my prose had said and my numbers had not. Both are posted where the wrong
numbers are.

HANDOVER.md is rewritten rather than patched. It had gone stale in a way that
mattered: it told a human to OPEN pull requests that are open, with suggested
bodies naming an interface that has been renamed (z2ui5_if_ui5_app_serializer),
a workflow job that was reworked (release.yaml -> backend-prebuilt.yaml), a
file that moved (node/package.json -> node/setup/runtime.package.json), and it
sent step 3 to the builder repo the plugin has since left. It also told them to
create an npm organisation that already exists and carries three packages. It
now says what to DO with each PR rather than repeating its body, carries both
root causes, and lists the facade slice as done instead of pending.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
@oblomov-dev oblomov-dev changed the title Roadmap §§8–20: the conformance findings, the host strategy, a security review, and the handover Roadmap §§8–21: the conformance findings, the host strategy, a security review, and the handover Sep 20, 2026
This morning I rewrote HANDOVER.md because it had gone stale, then wrote a
step 4 for the repository cutover that said nothing about the documentation
the cutover invalidates. Same failure I had just fixed, one section further
down.

Measured over docs/: 36 pages, 5,434 lines, 27 of them describing the port.
Only ROADMAP.md, HANDOVER.md and one line of reference/database.md know the
plugin exists. The sums check exactly against find/cat, and the three buckets
add back to 36 pages and 5,434 lines.

It is not stale paths. The guide teaches the port's app API, and the plugin's
is a different API rather than a renamed one - synchronous main( ), c.isFirstRun
and c.isDisplay in place of the two check_on_* predicates, c.bind / c.event,
t.table( ) for state. So the split is 12 pages of structure and reference to
write from scratch (1,949 lines), 15 pages of app-authoring API against
defineApp (2,416), and 9 that survive with corrections (1,069).

One finding stands on its own: docs/guide/samples.md is GENERATED and its
generator breaks. scripts/gen-samples.mjs line 39 hardcodes
.../cap2UI5/blob/main/core/srv/app/samples as its source, lines 122 and 188
write that path into the prose, and the plugin PR deletes that folder - so the
page's 98 rows lose their input. It fails into a stale page, not into an error.

I deliberately rewrote NONE of it. These pages are correct today: they describe
the cap2UI5 that is published and working. Rewriting them into the future tense
would swap accurate documentation for speculative documentation, and a rejected
approach would leave the repository with neither. The trigger is step 4.3.
The "where do the docs live" decision belongs before the rewrite, not after -
it decides whether 3,400 lines land here or in the plugin repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoANwtaK8Jm9srGEqSxmUF
@oblomov-dev oblomov-dev changed the title Roadmap §§8–21: the conformance findings, the host strategy, a security review, and the handover Roadmap §§8–22: the conformance findings, the host strategy, a security review, and the handover Sep 20, 2026
@oblomov-dev
oblomov-dev merged commit c07e043 into main Sep 20, 2026
1 check 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