Repository navigation
ADFA-6618: Prototype Help+Manual topics as JSON plus one derived Pebble template - #43
davidschachterADFA wants to merge 6 commits into
Conversation
scripts/hm_render_check/Render.java renders documentation.db templates with Pebble 4.1.1 at the app's settings (defaults, maxRenderedSize of 4 Mi chars) and parses the JSON context with Gson's LONG_OR_DOUBLE policy, as DocumentationContentSource does. Templates come from the caller over stdin, or from the Templates table with --db, by exact name like DatabaseTemplateLoader. It runs from source with `java Render.java` and stays up behind a small stdin/stdout protocol, so a pipeline step can check every page it would write against the real engine before writing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
…late
hm_pages_to_templates.py derives the skin shared by the i/ topic pages
from the pages themselves, stores it once in Templates as
helpmanual-topic.peb, and turns each topic's page row into its parts as
JSON with templateId pointing at that template. The app already renders
such rows, so this needs no app change.
The derivation aligns the pages line by line around their bodies, which
the jstopics hmBody locates, and explains each place they differ with the
jstopics fields, choosing between shapes with {% if field is empty %}.
Parts are stored as the page shows them and inserted with | raw. The JSON
also keeps hmHeader, hmTitlePath and, under js_ keys, any jstopics value
that differs from the page's, so jstopics/X.js can later be built from
the same row.
Every converted page is rendered with the real Pebble engine before
anything is written, and kept as HTML unless it renders back byte for
byte. A second run changes nothing.
On the 2026-10-13 database: 359 of 359 topics convert, stored bytes for
them go from 971,640 to 422,321 plus a 43,854-byte template, and the
phase-2 template rebuilds all 359 jstopics files' fields exactly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
… from the page JSON Phase 2. With --drop-jstopics the step stores a second derived template, helpmanual-jstopic.peb, beside the page template, and drops each i/jstopics/X.js row, chunks included, once the real Pebble engine has rebuilt that file from i/X.html's JSON with the same field values after JavaScript unescaping and the same text around them. A topic that does not match keeps its row, is named, and the run exits 1. Later runs build every dropped topic's file again from the stored template and check it against the fields its JSON holds. It is an option of the same step, not a new one: the jstopics template reads js_ keys this step writes into the page JSON, and the check before a row is dropped has to use exactly the JSON being stored. Without the option nothing changes, so a phase-1 database still works with the app as it is; a phase-2 database needs the app to build jstopics/X.js itself. On a copy of the 2026-10-13 database, one run converts the 359 pages and drops the 359 jstopics rows (401,072 stored bytes); the file goes from 175,982,592 to 174,927,872 bytes, and a second run changes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
Independent reviewAn independent Claude Code review covered this PR at 5e43917. It has no critical findings; one high, two medium, three low and four nits. The derivation and the render check do what this description says on the 2026-10-13 database, and every number in it was reproduced. Three findings need fixing before merge. All are open.
R43-01 (also CodeOnTheGo #2162's R2162-04).
R43-02. On a second run where a page no longer renders back, the page goes back to HTML, but its R43-03. After this step,
Meanwhile R43-05. On an export whose pages keep Help+Manual's Checked and found correct
Not verified:
🤖 Generated with Claude Code |
- R43-01: only <prefix>X.html is a topic page, since the app builds
jstopics/X.js from X.html and nothing else; a page named otherwise keeps
its jstopics row. A row is dropped only for a page that ends up stored as
JSON, written in this run or already identical.
- R43-02: a page that goes back to HTML after its jstopics row was dropped
gets the row written back, built with the stored jstopics template from
the page's last JSON and checked against that JSON's fields; the run then
exits 1. If the row cannot be built, the run writes nothing. Writing it
back keeps the run able to replace a template that no longer fits;
refusing the revert would leave the page as JSON under the new template.
- R43-05: a later run takes the body form from the stored jstopics
template, and a body form counts only when it maps the body back exactly,
so an export whose bodies keep their CRLF gets no filter and no js_hmBody.
- R43-10: the prefix is matched exactly; LIKE ignored case and read "_" as
a wildcard.
- R43-07: find_classpath pins Gson 2.12.1, the version CodeOnTheGo's
:common renders with. R43-08: Derivation.base, PebbleHarness's database
argument and Render.java's --db path, which nothing used, are gone.
- R43-03, R43-04, R43-09: the docstring gives the full run order with the
ADFA-6589 and ADFA-6596 steps, says check_remote_loads.py works on either
side, says a media rollback has to start from this step's pre-run backup,
and says the {% if %} conditions are fitted to the data.
- With --drop-jstopics, any jstopics row left in place fails the run.
- R43-06: tests for each of the above, and for pebble_empty against
Pebble's own is-empty test, rows under another template, subfolders, a
case-only prefix match, a chunked page, the NUL guard and the shared-lines
threshold at a second boundary.
The 2026-10-13 database converts to the same bytes as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
Where it runs (after every step that edits i/ pages, with check_remote_loads.py on both sides), what --drop-jstopics needs, and that a media rollback has to start from its pre-run backup, which belongs with the rollback bundle (R43-03, R43-04). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
Review answeredAll ten findings are fixed in bab8567 and fd9d6b2. For the 2026-10-13 database the output is byte-identical before and after the fixes (sha256
Checked:
New behaviour: with 🤖 Generated with Claude Code |
Independent review of the fix commitsAn independent Claude Code review covered bab8567 and fd9d6b2, which answer the first independent review (R43-01 to R43-10). It has 3 nits. Every finding is fixed or documented as the response says, and the real-data output is byte-identical to the one the description names (sha256
R43-11.
R43-12.
R43-13.
Earlier findings
Checked and found correct
Not verified:
🤖 Generated with Claude Code |
- R43-11: a topic found unfit before it is read as a Topic (its jstopics file has no hmBody, its page is not UTF-8), or one that cannot be read, now counts as keeping its jstopics row, so --drop-jstopics exits 1 for it as for any other row left in place. The docstring says so. - R43-12: tests for the three guards: a written-back row whose fields the stored template gets wrong, a refusal with the template stored but no text/javascript type, and a case-only I/jstopics/ prefix under --drop-jstopics. - R43-13: CLAUDE.md and the docstring say which steps come with PR #41 (ADFA-6589) and PR #42 (ADFA-4195), and that this step ships after both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3
Response to the review of the fix commits
Decisions to check:
Checks:
🤖 Generated with Claude Code |
ADFA-6618
Prototype: store each Help+Manual topic once, as JSON, and render its page through one Pebble template derived from the export. Code On The Go already renders such rows (a Content row with a
templateIdholds JSON; the app renders the named template with it), so this first phase needs no app change.Draft: a prototype for review of the approach. Phase 2 needs CodeOnTheGo #2162. With an older app, in-page navigation, printing and topic popups break on a phase-2 database.
Ships after #41 and #42, which add steps 1 to 5 of the run order (see Notes).
Why
Every Help+Manual topic is stored twice:
i/X.html(359 pages), each carrying a ~43 KB skin shell that is almost identical across pages;i/jstopics/X.js, holding the topic's fields for Help+Manual's in-page navigation.Change
scripts/hm_pages_to_templates.py DB [--dry-run] [--workers N] [--verbose]:i/X.htmlwith ani/jstopics/X.js. Thejstopicsbody locates the page's body.{% if field is empty %}.helpmanual-topic.peb: 43,854 bytes, with 15 holes.fileplus the tenjstopicsfields, each as the page shows it, inserted| raw), withtemplateIdset. A few pages carryjs_orpart_keys where their text differs.scripts/hm_render_check/Render.java: renders with Pebble 4.1.1 built as the app builds its engine (default settings, the samemaxRenderedSize, GsonLONG_OR_DOUBLE). It runs from source withjava, with the jars taken from the Gradle or Maven cache.Results on the 2026-10-13 database
i/are left alone.outerHTML);_hmXtopic.htmpage, toolbar icons, and the phone navigation bar.Device check
On a Pixel 6 Pro (Android 17), with the installed debug build and the database in
/sdcard/Download. The log confirmed each switch ("Swapped to the debug database").Font scale 1.0, with the unmodified 2026-10-13 database and then with this step's output:
js_andpart_keys, plus the search page and the print window (which loadsjstopics).xmessage.jsreadingsearchSession); it isn't new.Font scale 2.0, five pages plus the same interactions:
Phase 2 (
--drop-jstopics, 5e43917)jstopicstemplate readsjs_keys that this run writes into the page JSON, so the check before dropping a row has to use exactly the JSON being stored. Without the option, a phase-1 database works with today's app.helpmanual-jstopic.peb(606 bytes, plain text like the page template).i/X.htmlpages are topics, as in the app's mapping.ad12ee32...).i/jstopics/X.jsrendershelpmanual-jstopic.pebwithi/X.html's JSON astext/javascript. Both transports strip?_=query strings before the lookup.jstopicsURL, served by a local stand-in for the app rule, has the stored file's field values;--drop-jstopicsdatabase: in-page navigation and the print window loaded theirjstopicsfiles through the app rule (status 200). Every check was identical to the unmodified database at font scale 1.0 and 2.0; details are in #2162.Notes
Run order:
remove_skin_font_commentbundle_remote_imagesremove_remote_loadsshare_skin_spritecheck_remote_loadscheck_remote_loadsagaincheck_remote_loadsreads Templates and JSON pages, so it works on either side.Media rollback: it can't undo the converted pages. Start it from this step's pre-run backup, kept with the rollback bundle, then rerun this step. This is documented in the docstring and in CLAUDE.md.
Combined database: a database with every step from ADFA-6589: Keep documentation content on the device #41, ADFA-4195: Move Help+Manual's inline icon sprite into one shared script #42, ADFA-3305: Convert animated GIFs to VP9 WebM and pack documentation.db #38, ADFA-6596: Convert the documentation videos to VP9 WebM from their originals #40 and this PR ran end to end on the 2026-10-13 database: 112,283,648 bytes, with all checks passing.
ZOOMSTOP/ZOOMRESTART: the markers sit in the skin, outside the body, so nothing needs stripping.
CI: the step needs Java 17 and the Pebble jars, which no workflow provides yet.
ADFA-4195: Move Help+Manual's inline icon sprite into one shared script #42 still helps: run first, it keeps the 16,817-byte sprite out of the uncompressed template.
Testing
scripts/test_hm_pages_to_templates.py, 36 tests, run under pytest and as a plain script. They skip ifjavaor the jars are missing.X.htmlrule, which make them unreachable on their own. The second review found three more survivors; tests added in 3c98b71 kill them.🤖 Generated with Claude Code
https://claude.ai/code/session_018CMZQuXT3GT7nEct2w4tg3