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
12 changes: 12 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,18 @@ jobs:
if: ${{ !cancelled() }}
run: npm run check:line-length

# every prose word against a dictionary, and then against the words this
# project uses that a dictionary does not carry. test/spelling.test.mjs
# answers WHICH spelling of a word (it names British forms from a list);
# it cannot see a word that is neither spelling of anything. "thant",
# "havte", "fullfll" and "requoirememnts" went through the browser
# editor, through this workflow and out to the published site in two
# days, one of them in the first sentence of a page - which is where the
# search index, llms.txt and the card subtitle take their description.
- name: vocabulary
if: ${{ !cancelled() }}
run: npm run check:vocabulary

# every complete app class either carries a Run button or a marker on its
# page saying why it cannot run in the playground. The rules that offer
# the button fail towards NOT offering one, so without this an example
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ jobs:
- name: line length
if: ${{ !cancelled() }}
run: npm run check:line-length
- name: vocabulary
if: ${{ !cancelled() }}
run: npm run check:vocabulary
- name: Run-button coverage
if: ${{ !cancelled() }}
run: npm run check:playground
Expand Down
13 changes: 8 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ person reads the page. Do not put "as an AI, …" prose back into `docs/`.
| `docs/.vitepress/playground.mjs` | Decides which fenced ABAP example gets a **Run** button, and wraps the fence; `theme/playground.js` is the browser half |
| `scripts/list-runnable.mjs` | The measurement's worklist: every fenced example that carries a Run button, out of the same `playground.mjs` that decides the button. `--json` adds each example's ABAP verbatim - what the button sends - so the measurement below can be driven rather than clicked |
| `scripts/check-playground.mjs` | The Run-button bookkeeping: every complete app class either gets a button from `playground.mjs` or carries a `<!-- playground: no Run button — … -->` marker above its fence saying why it cannot run; a stale marker fails as loudly as a missing one. `--list` prints the deliberate exclusions with both reasons |
| `scripts/check-vocabulary.mjs` | Every prose word against a dictionary and against `scripts/lib/vocabulary.txt` (grouped and commented, one word per line). `scripts/lib/vocabulary.mjs` is the tokenizer - what is a word here and what is an identifier - and is pinned by `test/vocabulary.test.mjs`, whose first case is the four typos that reached the published site |
| `scripts/check-line-length.mjs` | The wrapped-page rule and `--fix` (`npm run fix:line-length`), which rewraps a drifted paragraph. `scripts/lib/line-length.mjs` is what a prose line IS here and what a rewrap may touch; pinned by `test/line-length.test.mjs`, which holds the manual to the rule as its last case |
| `scripts/check-conventions.mjs` | The two house conventions the sample corpora gate and this one did not: the view-chain layout in every fenced chain (the linter's `chain-house-layout`, which is opt-in — `check-examples.mjs` writes its config without a `rules` block, so the rule was never emitted), and the three class section blocks in every fenced app class. `--fix` (`npm run fmt:chains`) reformats a drifted chain; the sections are a judgement and stay by hand |
| `scripts/lib/catalogue.mjs` | Parses and counts a sample catalogue, for `link-samples.mjs` and for the figures `generate-llms.mjs` writes into `llms.txt` — from a sibling checkout when one is here, else from the `catalogue.json` each sample repository commits at its root; pinned by `test/catalogue.test.mjs`, because it has stopped matching twice and both times answered wrongly instead of failing |
Expand Down Expand Up @@ -57,8 +58,8 @@ person reads the page. Do not put "as an AI, …" prose back into `docs/`.
npm run check # test + check:version + docs:build + check:cross-site + check:design + check:images + check:examples + check:conventions + check:playground + check:api-names + check:api-reference + check:samples
```

A documentation repository has no compiler for its prose, but fourteen things
in it are decidable, and all fourteen are decided before a merge:
A documentation repository has no compiler for its prose, but fifteen things
in it are decidable, and all fifteen are decided before a merge:

| | |
|---|---|
Expand All @@ -70,6 +71,7 @@ in it are decidable, and all fourteen are decided before a merge:
| `check:api-reference` | the committed client API reference — the generated block in `resources/api.md` and `docs/public/api/client-api.json` — regenerated from `z2ui5_if_client` on `main` and compared byte for byte. Goes stale whenever the interface changes over there and the committed reference still describes the shape before it. `npm run generate:api` rewrites both |
| `check:conventions` | the fenced ABAP against the house style the reader meets next: the view-chain layout, and the three section blocks of an app class. `check:examples` asks whether an example compiles and names real API — both questions about the framework; neither can see that a snippet is written in a different style from every sample. Measured against [samples-controls](https://github.com/abap2UI5/samples-controls) (637 classes, gated, at zero): five chains here showed the reader a different tree than the one that renders, and 57 of 86 app classes carried neither `PROTECTED SECTION.` nor `PRIVATE SECTION.`. What this gate deliberately does NOT take over is the blank-line and `t_arg` continuation rules — those are pattern-lint *warnings* over there and that corpus carries 382 of them |
| `check:line-length` | **a page that is wrapped has to stay wrapped.** The manual is not written to one column and should not be: 39 pages are wrapped, 59 are one line per paragraph, 64 are in between, so a site-wide column would be a reformat of two thirds of the pages rather than a gate. The drift is the decidable part - a page whose prose already sits inside 80 characters may not acquire a line outside it. That is what an editor does to a paragraph it rewrites: it hands it back as ONE long line, invisible in the editor, and from then on every diff of that paragraph is one changed line instead of three, so each later correction reads as a rewrite. It happened twice in two days on the same page. Held: 66 pages; the other 97 are left alone and start being held the day somebody wraps them. Not prose, and the reason each one is not: fenced code, frontmatter, tables (they wrap at the cell), HTML, headings and image alt text (one line by construction), the caption under an image when it repeats that alt text word for word (the two are meant to be comparable at a glance), a line that is only long because of one unbreakable URL, and the generated `samples:` and `api:` blocks (wrapping one by hand is a change the next regeneration undoes). `npm run fix:line-length` rewraps a drifted paragraph - whitespace only, and verified as such: the words are identical and the rendered article is identical, which is the check that caught the two bugs in the rewrapper (it tore the punctuation off a code span, and it swallowed a `:::` container into the paragraph) |
| `check:vocabulary` | every prose word of the manual, against a dictionary (`dictionary-en`, the Hunspell en_US) and then against `scripts/lib/vocabulary.txt`, the 215 words this project uses that a dictionary does not carry. **`test/spelling.test.mjs` answers which SPELLING of a word; this one answers whether it is a word at all**, and nothing did: that test names British forms from a list, so a word that is neither spelling reads as prose it has no opinion about. `thant`, and then `havte`, `fullfll` and `requoirememnts`, went in through the browser editor, past all fourteen other gates and out to the published site in two days - and the second set stood in the FIRST SENTENCE of a page, which is where `scripts/lib/pages.mjs` takes the description from, so it read wrong in the search index, in `llms.txt` and under the card as well. The prose is `scripts/lib/prose.mjs`'s, the same reader the spelling test uses, so an ABAP keyword, a CSS property or a path is never a word here. Not a word either, each for a reason: a token carrying a digit or an underscore (`abap2UI5`, `z2ui5_if_app`), an acronym (`ICF`, `APIs`), camelCase (`liveChange`) - all identifiers, not English; a hyphenated compound is judged part by part, and emphasis inside a word (`**f**rontend`) is joined back up first. A word goes on the list when it is right and the dictionary simply does not have it; a word you are unsure about does not, because a list that absorbs doubt is how a gate like this stops catching anything. `--unused` names entries no page uses any more |
| `check:playground` | every complete app class on the site either carries a **Run** button or a marker on its page saying why it cannot run. The rules that offer the button fail towards *not* offering one, so without this an example nobody ever measured is indistinguishable from an example that can never run — which is exactly how the coverage ledger below went stale. What stays undecidable by CI — does a *buttoned* example actually start — is the measurement the Run-button section describes |
| `check:cross-site` | every link that leaves this deployment for a neighbouring one on the same origin — the playground, the catalogue, the linter's rule pages — carries a `target`. Without it VitePress's router treats the link as a route of THIS site, finds no page behind `/playground/` and renders the 404 *at that URL*, which reads as the other site being broken. Every way out of the manual was in that state at once: both bar items, the Linter rules row in the menu and the Run bar's link. Judges the built HTML, so it runs straight after `docs:build`. What it cannot see is the Run bar's link — built in a browser, in no built page — which is why `test/cross-site.test.mjs` pins that one as source |
| `check:design` | the values the four bars are made of — the seven palette colours, the two type stacks, the two radii — against the copy [abap2UI5/playground](https://github.com/abap2UI5/playground) keeps, in **both** schemes. They are copied by hand on purpose (a stylesheet fetched across two deployments is a request in front of the first paint), and until this gate nothing compared the copies: they agreed because whoever touched one remembered the other. One had already drifted — two font stacks leading with different families, which is the same face on macOS and Windows and two different ones on Linux, so the same four words in the same bar measured 59/122/78/97px here and 65/141/87/110 there. It compares the EFFECTIVE value (a property the dark block does not redeclare keeps its light one), because the two sides switch schemes differently: `.dark` here, `[data-theme]` over there.
Expand All @@ -78,18 +80,19 @@ in it are decidable, and all fourteen are decided before a merge:
| `check:images` | every image under `docs/public`, against the three things a page can afford and the one it cannot: a screenshot is WebP (the PNG captures were 200 to 335 kB each, 2.9 MB across the manual, on pages of 20 kB of text; the same captures as WebP are a fifth of that), a deliverable is one of the PNGs the logo page hands out, nothing is over its budget, and the build can measure every one - an image it cannot size gets no width and height and moves the page when it lands |
| `check:samples` | the **Working Samples** blocks and the source links a page writes by hand, against [abap2UI5/samples](https://github.com/abap2UI5/samples), [samples-controls](https://github.com/abap2UI5/samples-controls) and [samples-stack](https://github.com/abap2UI5/samples-stack) — a page may declare a class of any of the three |

**All five walking gates carry a floor.** A gate that checked nothing reports
**All six walking gates carry a floor.** A gate that checked nothing reports
the same shape as a gate that found nothing wrong — which is precisely how
`check:examples` passed for years on an abaplint config with no rules in it. So
`check:examples`, `check:conventions` and `check:playground` each exit 1 when
their walk finds no example at all, and say which of the fence language, the
page layout or the builder name is the likely cause. `check:line-length` exits 1
when no page of the site counts as wrapped, which would mean its fence handling
or its glob stopped matching rather than that the manual went loose. `check:cross-site` carries
or its glob stopped matching rather than that the manual went loose, and
`check:vocabulary` when it walked no page or read an empty word list. `check:cross-site` carries
the same floor twice over: no HTML in `dist` at all, and no cross-site link on
a site whose bar carries three of them on every page.

The fourteen are written out in **three** places, and all three have to name the
The fifteen are written out in **three** places, and all three have to name the
same set: `package.json`'s `check` script, `.github/workflows/check.yml` for a
pull request, and `.github/workflows/deploy.yml` before the site is published.
`check.yml` runs them in the script's order; `deploy.yml` cannot, because it
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md

All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source of
truth for this repository (how the site is built, the fourteen gates, the playground rule engine, and what may be written where).
truth for this repository (how the site is built, the fifteen gates, the playground rule engine, and what may be written where).

Read `AGENTS.md` before making any change.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ Nothing here is a source of truth about the framework: the API reference, the
sample catalogue and the corpus figures are generated or checked against
`abap2UI5`, `abap2UI5/samples` and its siblings. If a fact is wrong, it is
usually wrong there first. [AGENTS.md](AGENTS.md) is the full contract — how
the site is built, the fourteen gates, and what may be written where.
the site is built, the fifteen gates, and what may be written where.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Every contribution makes the documentation better for the community!
```sh
npm ci
npm run docs:dev # the site, with hot reload
npm run check # what CI runs, all fourteen steps
npm run check # what CI runs, all fifteen steps
```

### What CI checks

A documentation repository has no compiler for its prose, but fourteen things in
it are decidable, and `npm run check` decides all fourteen before a merge — the
A documentation repository has no compiler for its prose, but fifteen things in
it are decidable, and `npm run check` decides all fifteen before a merge — the
prose builds (`docs:build`), the four bars are still made of the same palette,
type and radii as the playground's (`check:design`), every link into a
neighbouring site on this origin
Expand All @@ -45,7 +45,7 @@ passed. Several of these go stale without anybody touching this repository (a
release is published elsewhere, a sample class is renamed elsewhere), which is
why the deploy re-runs them rather than trusting the merge.

**[AGENTS.md](AGENTS.md) describes each of the fourteen**, what a failure means and
**[AGENTS.md](AGENTS.md) describes each of the fifteen**, what a failure means and
which of them need a sibling checkout to say anything at all — read it before
changing anything beyond prose.

Expand Down
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"check:examples": "node scripts/check-examples.mjs",
"check:conventions": "node scripts/check-conventions.mjs",
"check:line-length": "node scripts/check-line-length.mjs",
"check:vocabulary": "node scripts/check-vocabulary.mjs",
"fix:line-length": "node scripts/check-line-length.mjs --fix",
"fmt:chains": "node scripts/check-conventions.mjs --fix",
"check:playground": "node scripts/check-playground.mjs",
Expand All @@ -31,7 +32,7 @@
"link:samples": "node scripts/link-samples.mjs",
"check:samples": "node scripts/link-samples.mjs --check",
"test": "node --test test/*.test.mjs",
"check": "npm run test && npm run check:version && npm run build && npm run docs:build && npm run check:cross-site && npm run check:design && npm run check:images && npm run check:examples && npm run check:conventions && npm run check:line-length && npm run check:playground && npm run check:api-names && npm run check:api-reference && npm run check:samples",
"check": "npm run test && npm run check:version && npm run build && npm run docs:build && npm run check:cross-site && npm run check:design && npm run check:images && npm run check:examples && npm run check:conventions && npm run check:line-length && npm run check:vocabulary && npm run check:playground && npm run check:api-names && npm run check:api-reference && npm run check:samples",
"llms": "node scripts/generate-llms.mjs",
"search": "node scripts/generate-search.mjs",
"check:version": "node scripts/check-version.mjs",
Expand All @@ -42,7 +43,9 @@
"devDependencies": {
"@abap2ui5/linter": "^0.6.1",
"@abaplint/cli": "^2.120.52",
"dictionary-en": "^4.0.0",
"esbuild": "^0.28.2",
"nspell": "^2.1.5",
"vitepress": "^1.6.4"
},
"engines": {
Expand Down
Loading
Loading