Skip to content

Close the agent's loop: see the view in seconds, read the right rulebook, start a project - #19

Merged
oblomov-dev merged 5 commits into
mainfrom
claude/abap2ui5-repo-analysis-c2qhj9
Aug 18, 2026
Merged

Close the agent's loop: see the view in seconds, read the right rulebook, start a project#19
oblomov-dev merged 5 commits into
mainfrom
claude/abap2ui5-repo-analysis-c2qhj9

Conversation

@oblomov-dev

Copy link
Copy Markdown
Member

Part of an ecosystem-wide pass over the two goals: make it maximally easy for AI agents, and for people, to build abap2UI5 apps. This server is the half that serves goal A, and four things were missing from it.

screenshot_view — an agent can SEE the view in seconds

The linter gained --screenshot: it reconstructs the view from the builder calls, seeds it from the class's own TYPES/DATA and photographs it in the same headless harness its render gate runs. This server had no way to reach it, so the only way to look at anything was run_app — which boots the REAL app and therefore needs the whole framework transpiled first, tens of minutes on a cold checkout. A dedicated tool rather than a flag on validate_view: different question, different arguments (viewports, theme, preview model) and a different failure mode.

deploy_app accepts the app the agent was told to write

The regex was ^z2ui5_cl_[a-z0-9_]+$ — the corpus' own porting convention. An agent following the ecosystem's recommended path builds something like app-template's zcl_app_001 and could not deploy, build or screenshot it at all. Widened to the customer namespace ^[zy]c[lx]_, with the safety property kept as a whitelist and tested with escape shapes through both the write and the delete path.

app_guide and scaffold_app — the right rulebook, and a project to put it in

generation_rules serves a prompt whose first line is "You are porting one official UI5 demo kit sample to abap2UI5" — the wrong instructions for someone building an app. app_guide serves the real thing (abap2UI5 docs/agents/building-apps.md, chapter by chapter); both tool descriptions now say which job they are for.

scaffold_app answers what neither could: what a repository for this looks like. The class is the half an agent can write; what it cannot invent is the abaplint config with the framework pinned under the branch key, the abap2ui5lint.jsonc the render gate needs to run rather than skip, the CI workflow, and the .clas.xml sidecar whose CLSNAME must match the class or the object does not activate. Served live from abap2UI5/app-template, renaming throughout — ABAP, sidecar and file names. Proven end to end: a scaffolded project installs and passes npm run check with abaplint at 0 issues and the render gate on.

Smaller, and found by using it

  • The catalogue ROW regex required a dash after the bold header, so all 430 samples-controls rows parsed as headerless — an agent saw title: "sap.m" instead of the sample name.
  • The docs: links samples' catalogue carries were parsed and then discarded.
  • Findings can carry the rule's explanation, via the linter's new ./rule-docs export.
  • AGENTS.md said "10 tools" (there were 11, now 14), claimed ~615 apps where the parser reads 614, and stated a 7-bit-ASCII rule no file in the repository satisfies.

Release readiness

npm pack verified from a scratch install: 12 files, 44.3 kB packed, and the level-1 tools drive correctly from the tarball. release.yml now proves the tarball before the version becomes immutable. Not publishednpm login, npm publish --access public and the Trusted Publisher setup need maintainer credentials; the steps are in RELEASING.md.

How to test

npm test    # 49/49, smoke included (14 tools, a real screenshot, app_guide, explanations)

Generated by Claude Code

claude added 5 commits August 17, 2026 21:55
Two things the SAMPLES.md parser dropped on the floor.

The TITLE of every port in samples-controls. Its rows carry the header
entirely in bold with nothing after it

  | **sap.m.Bar**<br>Each screen of a mobile application...<br><sub>...</sub> |

and the row pattern required a dash after that bold half. So 430 of the 614
apps parsed as rows with NO header of their own: `title` fell back to the
section, and every port in the file announced itself as the LIBRARY it belongs
to, while the control an agent asked this tool for survived only inside the
keyword blob and inside `sub` with its asterisks still attached. The dash is
optional now; the dashed shape the other two catalogues use is untouched, and
all three still parse to exactly 152 / 430 / 32 rows.

(What remains is upstream's: 241 of those 430 rows carry only `**sap.m**` in
the header rather than the control. Noted in AGENTS.md - the fix belongs in
samples-controls' generator, not in a parser guessing at a control name.)

And the `docs:` links. Each row names the cookbook chapters somebody decided
that app is the worked example of, and this parser knew the block existed only
well enough to SKIP it while looking for the keywords. The agent got a class
to read and no way to reach the prose explaining what it demonstrates - the
half a human reviewer opens first. They travel as `{ topic, url }`, an ANSWER
rather than a search term: almost every row in `samples` carries one starting
`cookbook/`, so putting them in the haystack would make one query match the
entire catalogue.
deploy_app enforced ^z2ui5_cl_[a-z0-9_]+$ - the naming convention of the
demo-kit PORTS - and this server exists for an agent building its OWN app.

abap2UI5/app-template, the starting point the ecosystem recommends, ships
src/zcl_app_001.clas.abap. So an agent that followed the recommended path
could not deploy it, could not build it and could not look at it: every tool
here refused the name, and the name was the one the template gave it.

Widened to the customer namespace, ^[zy][a-z0-9_]*$ and <= 30 chars, which is
the real rule - SAP reserves everything else. The dev lint config had the same
mistake one layer down (it relaxed the corpus' Z2UI5_CL_SMPC_ prefix only as
far as ^Z2UI5_CL_, so it would have failed the very class this had just
accepted); it is now the same ^[ZY] boundary, so the two cannot disagree about
what a legal app is.

The safety property is untouched and is why this stays a whitelist rather than
becoming a blacklist: every caller-supplied name becomes a PATH under
src/zz_dev and is validated before it is joined, so no separator, dot or space
can appear in one. A new test walks the shapes whose only purpose is to leave
the sandbox - `../../src/01/...`, `/etc/passwd`, `zcl_app/../../x` - through
both the write path and the delete path.
Three tools' worth of change, all one problem - this server was scoped to
PORTING demo-kit samples while its stated purpose is helping an agent build a
user's app, and the two need different things.

screenshot_view. The linter can photograph a view in seconds without a system:
it reconstructs the view from the builder calls, seeds it from the class's own
TYPES/DATA and renders it in the same headless harness its render gate already
runs. This server could not reach any of it. The only way for an agent to SEE
anything was run_app, which boots the REAL app and therefore needs the whole
framework transpiled first - tens of minutes once, minutes per rebuild. So the
loop was "write ABAP, get a verdict in seconds, then pay a build to look at it,
or never look at it at all".

A separate tool rather than a flag on validate_view, because it answers a
different question (is this legal / what does it look like), takes different
arguments (viewports, theme, preview data) and needs the render runtime and a
browser that the property gate does not. Several viewports come out of ONE
browser session - the launch and the UI5 boot cost more than every render put
together - each as an MCP image block, the way run_app already returns its
screenshot.

app_guide. generation_rules described itself as "the canonical rulebook for
writing an abap2UI5 app" and served a document whose first line is "You are
porting one official UI5 demo kit sample to abap2UI5". An agent building
someone's app was handed the porting brief: an input sample it does not have, a
z2ui5_cl_smpc_app_<n> convention that is not its app's, 1:1 fidelity to
something that does not exist. The right document already existed beside the
framework sources - abap2UI5's docs/agents/building-apps.md, written to be
self-contained precisely so an agent with no web access has the whole picture -
and nothing served it. Sliced by chapter the way pitfalls slices the skills,
selectable by number or by a word in the heading. The porting brief stays; both
descriptions now say which job they are for, because a tool description is the
only documentation the agent ever reads.

Rule explanations. A finding came back as an id and one terminal-width line,
with the paragraph behind it reachable only on the published rules page - a web
fetch mid-task. The linter exports that prose now (./rule-docs), so each rule
that fired comes back under `rules`, keyed by id: twelve findings of one type
cost one explanation, not twelve. The summary line always, the paragraph on
explain:true - a first run on an unfamiliar class can hit a dozen distinct
rules, and a dozen paragraphs would crowd out the findings they are about.

The linter is an UNPINNED sibling, so both new reads degrade: a checkout
without screenshotFiles says so, one without ./rule-docs costs the agent the
explanations and nothing else. Neither may cost it the findings.

Verified end to end against the real siblings: app-template's own
zcl_app_001.clas.abap comes back as two styled PNGs (390x844 and 1280x900) in
one call, which is the whole point - the app the ecosystem tells you to start
from could not be deployed, built or seen through this server at all.

Also here: the counts every document repeated by hand (10 tools -> 13, "~615
apps" -> the 614 the parser actually reads, samples-controls 431 -> 430), and
the AGENTS.md claim that source files are 7-bit ASCII, which no file in the
repo has ever satisfied - the rule that is real, and enforced, is about
literals.
CHANGELOG says @abap2ui5/mcp has never been on npm, and consumers - the VS
Code extension among them - run it as `npx --yes github:abap2UI5/ai-mcp`, so
an agent's setup changes under it day to day. The release plumbing was already
there; what it lacked was the one check that can only be made from the outside.

`npm test` runs against the WORKING TREE, where every file exists whether or
not `files` lists it. So the single defect this package can ship - a lib/
module left out of the allowlist, making the server import-fail on first use -
is invisible to the entire suite, and the release job only PRINTED the tarball
contents for a human to read. It now packs the tarball, installs it into a
scratch project and drives the installed bin over stdio: initialize,
tools/list, and one tool call with every checkout absent, which has to come
back as the actionable message rather than as a crash. That is the level-1
surface, and it is the only part a CI job here can exercise - everything else
needs sibling checkouts and a build measured in tens of minutes.

Checked by hand for the first release, and written into RELEASING.md so the
next person does not have to rediscover it: the manifest carries every field a
publish needs; the tarball is 12 files (server.mjs, lib/, README, AGENTS.md,
LICENSE, package.json - no tests, no workflows); the level-1 tools work from
the tarball with only AI_VIEW_CHECK_HOME set, validate_view returning ok:true
and screenshot_view returning a PNG.

One finding recorded rather than fixed: the install is ~45 MB and 19 MB of it
is playwright, which only run_app uses. Marking the dependency optional would
not help - npm installs optionalDependencies by default - so the fix is the
shape @abap2ui5/linter arrived at, a separate package declared as an optional
peer. That is a design change, not a release step.
The server could tell an agent how to WRITE an app class (app_guide) and where
to put one so it could be run (deploy_app, into the corpus' scratch package).
It could not answer the question somebody building an app of their own
actually arrives with: what does a repository for this look like.

That gap mattered because the class is the half an agent can do. The half it
cannot invent is everything around it - the abaplint config with the framework
pinned at a release under the `branch` key (`tag` silently does not work), the
abap2ui5lint.jsonc the render gate needs in order to run rather than skip, the
CI workflow, the .abapgit.xml that decides where abapGit puts any of it, and
the .clas.xml sidecar whose CLSNAME must match the class or the object does not
activate. An agent asked for "an abap2UI5 app" wrote a class into a directory
with none of that.

So scaffold_app, served live from abap2UI5/app-template - the repository this
ecosystem already points people at to begin. Not embedded here: a copy would
be a second answer to "what does a new project look like", and the template
moves. AGENTS.md ships with it, because a project whose agent has no briefing
is the same gap one level down.

`class` renames it throughout: the ABAP, the sidecar's CLSNAME - upper case
there, lower in the source, and that asymmetry is precisely why the template
ships scripts/rename.mjs instead of an instruction - and the file names. The
name is validated before it is substituted, since it reaches file paths; the
refusals are tested with the same escape shapes deploy_app is.

A missing template file is reported rather than skipped. This list is a claim
about another repository, and a project quietly missing its CI workflow is not
noticed until somebody wonders why nothing is checked.

  npm test              53/53 (4 new unit tests + the degradation contract)
  live call             12 files, renamed, through the real stdio server
  scaffolded project    npm install && npm run check -> abaplint 0 issues,
                        linter 0 findings with the render gate on

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XY3AoWMiCC52cuQjbce4SU
Copilot AI lite review requested due to automatic review settings August 18, 2026 05:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@oblomov-dev
oblomov-dev merged commit cf937b3 into main Aug 18, 2026
3 of 4 checks passed
@oblomov-dev
oblomov-dev deleted the claude/abap2ui5-repo-analysis-c2qhj9 branch August 18, 2026 05:56
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.

3 participants