55 Commits
Author SHA1 Message Date
NotAlexNoyleandClaude Fable 5.1 62af6e82c7 The engine on UXP RB_20261027 (T-551..T-554): the queue rebased, 0060 dropped, every pin, the device checked
The owner's choice of 2026-11-02 after T-551's check found the tag: the update now, patch 0060 (the
roots, byte-identical upstream) dropped, special attention to the certificate and HTTPS changes.
- update-engine.sh RB_20261027 / --finish: the 72 patches on the tag (0060 fell away as already
  applied), jihad/uxp-RB_20261027 at 52ad9ab66e in third_party/uxp, the queue rewritten and
  renumbered from 0060, the pins (engine-version.env, the OE recipe, .gitmodules, the UA and the
  three start pages: UXP/6b18be9), JIHAD_APP_BUILD_ID 20261103000000; patch-queue.sh --check,
  check-engine-pins.sh and check-engine-api.py (baseline rewritten, unchanged) agree.
- docs/ENGINE-SOURCE.md: the base, the dropped 0060, the renumbering, and the range's 12 commits
  each with its disposition — the two NSS certificate commits (emSign removed after the eMudhra
  compromise; certdata to Sept 2026) were on the device since the roots of 2026-10-25 and are the
  base's now; no PSM, TLS protocol, HSTS or certificate-UI change in the range; the memory-safety
  fixes (the JS OOB array copy, the jar: flag, feTile, the layout frame table, the mutation
  observer's strong ref, SQLite 3.54.1) taken with the base.
- audit-roots.py against the fetched release certdata and the CCADB report: identical set and
  trust, none removed, RESULT OK (docs/rounds/certs-2026-11-03).
- Desktop: the full build clean; 14 of 18 tests pass, the four left are the desktop GTK path's
  known read-back limit. ARM: libxul, the daemon, the bundle, the three packages; full pushes to
  all three variants; default-check x3 faults 0; the daemon's start line 28 of 29 optional
  exports as before. The areas the commits touch, on the device (tools/device/engine-checks.sh,
  new): about:settings, about:personas and about:addons through jar: under the flag change,
  cookies through the new SQLite, an SVG filter page with feTile and its overflow case
  (render/goanna/test/jihad-svg-filter.html, new), a long press's selection; faults 0.
  The certificate kit's checks (tools/device/cert-checks.sh): the TLS 1.0 refusal, the clean loads,
  expired / wrong.host / self-signed / untrusted-root with the SSL confirm's right flags, OCSP
  revocation enforced on grc.com, ssl.com and DigiCert's demos, revoked.badssl.com's CRL-only case
  invisible as before: nothing changed with the base.
  Records: docs/rounds/engine-rb27-2026-11-03/notes.txt.
- tools/device/cert-checks.sh and t502/enyo-restore-job.sh (new): the certificate kit's device
  checks under a hand-started daemon, and the Enyo job put back after any spike round.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 10:25:46 -05:00
NotAlexNoyleandClaude Fable 5.1 62df604c54 Licensing audit: notices for everything bundled, no borrowed marks
The owner: "ensure that all software patent and trademark licenses in
the repo are being respected". A read-only audit of what the packages
carry found no blocker and these gaps, all closed here (the record is
impl-review-findings-6.md, "Licensing audit"):

- NOTICE names the runtime the engine ships with: glibc 2.23 as
  crosstool-NG modifies it (LGPL-2.1), the GCC 9.4.0 runtime (GPL-3.0
  with the Runtime Library Exception), and the eight Debian jessie
  libraries, with the FreeType disclaimer, Lato, the hyphenation
  patterns, and where the corresponding source is. licenses/ gains
  LGPL-2.1, GPL-3.0 and the GCC exception from the pinned tarballs and
  Debian's copyright files from the sysroot; every package ships all of
  licenses/ and CREDITS.md, checked in the payload preflight.
- NOTICE names the modified engine's source (the fork, branch, pinned
  commit, base release, the patches), as MPL-2.0 3.2 asks.
- README and LICENSE no longer call the packages Apache-2.0 and MPL-2.0
  only; the dead licensing.shtml reference is replaced by the UXP
  LICENSE and TRADEMARK files and Moonchild's legal and redist pages.
- Apache-2.0 4(b) change notices on the 29 HP-derived files changed
  since import.
- A drawn magnifier is the DuckDuckGo engines' icon; Google's logo is
  Google's only; the unused Bing logo is gone.
- The engine's services-sync.js defaults pointed at Moonchild's Sync,
  key server and add-on host: blanked at bundle time, checked.
- The "Blazer" keyword, the Goanna-mark tagline, a stray screenshot and
  a .bak dropped; the HP SDK's terms stated where it is downloaded;
  CREDITS records the logo's origin and the screenshot strip's licence.

Deferred to the next engine build: about:credits' URL and
--disable-ffmpeg (the system-libavcodec path is dead on the device).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM9EDSp83ZFRhggY4RCT9X
2026-10-14 13:57:20 -04:00
NotAlexNoyleandClaude Opus 5.5 cfbfbcf293 T-469: review round 24 (the inspector, for Codex): webOS's store gives roots only, the CA question only for a page
Codex's owed review of Tier 78, round 22 and Tier 83, run by the Cavekit inspector (the owner).

Tier 83 (REQUEST CHANGES):
- P1: Trust Always puts a site's own certificate in Certificate Manager's store, and a router's or
  NAS box's (openssl req -x509) also says cA=TRUE, so R4 would have made it a root for every site
  at the next start. R4 now takes only roots: self-issued, keyUsage allowing keyCertSign, no
  subjectAltName or extendedKeyUsage. The daemon tells the component (jihad-platform-cert) about
  every certificate its SSL dialog installs, and R4 never takes one (its SHA-256 kept in
  jihad.security.webos_certs.accepted). An intermediate is no longer taken either.
- P2: a hidden blob: frame reached the engine's CA import and raised the question over the page.
  The question now needs a one-use offer made by a CA certificate opened as the page itself (HTTP
  in _caLoad, data:/file: in the content policy), or it refuses without a word.
- P3: only frames (subdocument, object) are cancelled, not a script's fetch; the question also
  refuses a site's certificate that says cA=TRUE; the proxy-login marker is "[proxy] ", which no
  site's host can produce; webOS's proxy setting keeps its last plan (jihad.proxy.webos.last) when a
  read fails instead of going direct; card-selftest marks hook lines at or after the screenshot's
  second, across midnight. Matching removed roots by Subject+SPKI was tried and dropped: Mozilla
  removed old issues of roots it still trusts with the same name and key (GTS R1-R4, GlobalSign,
  Firmaprofesional).
Round 22 (APPROVE): card-picture updates have their own observer topic (a Persona id of "thumbs"
was ambiguous); the cost of making them on the engine's thread is written down.
Tier 78 (APPROVE): the gallery offers the bundled set too, so one removed there stays removed;
link-style buttons and the toast's Undo are 44 px tall; the Personas test server reads Carbon
Light's picture from its package.

Device (CE 3.1.0, all three on daemon 06e75a68): R4 trusted the same 5 roots; a router certificate
with an IP SAN skipped as siteLike, one without keyUsage as noCertSign, a CA with keyCertSign
trusted, then removed; Trust Once logged the site certificate as never to be taken and the session
serial was swept; a blob: CA frame refused with no dialog; the test CA link still asked ("Offered by
192.168.1.245:8896"); a router certificate link refused; proxy logins read "The proxy ..." (Enyo,
Mochi) and "[proxy] ..." (Mojo's sheet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-13 15:31:18 -04:00
NotAlexNoyleandClaude Opus 5.5 e223591f81 T-457, T-458, T-459, T-464: proxies and webOS's certificates
The settings page gets a Proxy section (Advanced) and a Certificates section (Security),
cavekit-proxies-certs.md:

- Proxy: No proxy (now the default), webOS's own proxy setting, the Riverstone Relay,
  WebOne, my own proxy (HTTP/HTTPS/SOCKS, direct list) and a PAC URL, read from and written
  to the engine's network.proxy.* prefs. The engine's "system proxy" (type 5) reads GNOME's
  settings and does nothing in the headless build; it is gone from the page.
- webOS's proxy setting: the daemon reads com.palm.connectionmanager/getNwProxiesConfig on
  its own Luna connection at start, at every card connect and on request, and
  components/jihadProxy.js applies it when the user chose to follow it.
- components/jihadCerts.js: at the first page (the daemon announces "jihad-nss-ready"), the
  CA certificates in webOS's stores that are not HP's 3.0.5 roots, not removed by Mozilla,
  not Jihad's own and not expired are trusted in memory (NSS temporary certificates). The
  lists come from HP's doctor image and CCADB (render/goanna/certs/make-cert-lists.py).
  Switching the setting off takes the trust away at once and clears TLS sessions and idle
  connections that were checked under it.
- The Relay's CA is carried and pinned; trusted only when the user turns it on while the
  Relay is in use, never through a store or a link.
- Opening a CA certificate asks on the card (a JS nsICertificateDialogs), including one sent
  as a download (WebOne's); the added CAs are listed in Settings with Remove.
- A proxy's 407 names the proxy; Enyo and Mochi say "The proxy ... requires a username and
  password".

Found on the way: JS components are read as Latin-1, so non-ASCII in their strings came out
garbled (the print status's ellipsis, the persona importer's BOM strip); escaped. The install
scripts leave a byte-identical adapter alone: replacing one LunaSysMgr has mapped left a
deleted file on / that kept it read-write.

Device (Enyo, CE 3.1.0): the five newer roots trusted and nothing written to cert9.db; a
SwissSign 2022-1 site loads with the setting on and fails off; a Certificate Manager CA
trusted after a start and gone after removal; the Relay re-signs with its CA off and loads
with it on; manual, PAC, webOS's setting and a login proxy through a PC test proxy; WebOne
0.18.3's CA from its link, then https through it; the Relay's CA refused from a link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-13 11:09:13 -04:00
NotAlexNoyleandClaude Opus 5.5 23ffd9af83 Engine: UXP RB_20261006; updates in two commands; a release build in Thumb-2
The owner, 2026-10-12: "Proceed with the UXP update, with a focus on making future updates
easier, and optimizing the code where possible, while supporting modern web standards. target
the latest release".

The engine (third_party/uxp) moves from master b2594a4 (2026-06-30) to the release RB_20261006
(5f290d1, about 170 upstream changes: NSS 3.90.14 and security fixes, constructable style sheets,
URL.parse(), revert-layer, aspect-ratio for grid items, :has(), typing speed, leak fixes), branch
jihad/uxp-RB_20261006 at 706d4782ba. Our 39 commits came across with git's three-way merge and no
conflict, now one commit per patch (the squash of 0001-0031 split); the six files both sides
changed were read: only --disable-npapi-gtk2 had to go (upstream now offers GTK2 NPAPI to GTK
toolkits only). check-engine-api.py: 0 FAIL, baseline re-written, no signature changed.

Future updates:
- build/engine/update-engine.sh [TAG] puts the queue (build/desktop/patches, now git mails) on a
  release with git am -3 in a worktree, lists the files both sides changed and runs the API check;
  --finish writes the queue again, checks the branch out and rewrites every pin (engine-version.env
  with the new UXP_TAG, the OE recipe, the user agent, the three start pages); --abort, --pins.
- make-device-bundle.sh reads the ICU data file's name from engine-version.env.
- render/goanna/EngineCalls.h: the 15 positional engine calls behind named wrappers.
- docs/ENGINE.md §6 is the procedure in those terms, with the full variant push an engine update
  needs (push-engine-update.sh does not carry the engine's own JS, CSS and chrome).

Optimised: mozconfig.goanna-arm is a release build (--enable-release --enable-gold: dead-code
removal and identical-code folding, both off in developer mode) in Thumb-2 (--with-thumb=yes).
Stripped libxul 46.2 -> 30.2 MB (-35%), its code -43%; an idle daemon 33.8 MB resident against
36 MB before.

Modern web: a Settings switch for CSS :has(), off by default as upstream releases ship it (slow
on some pages). render/goanna/test/modern-web.html reports the features.

Fixed on the way: postinst/prerm took the initramfs rootfs line for a read-write /, so a read-only
root was never remounted and the install failed; they read the real root mount now. The daemon
includes nsIDocument.h before nsIPresShell.h, which the new StyleSheet.h needs. JIHAD_APP_BUILD_ID
bumped. The UA and start pages say Goanna/6.9 UXP/5f290d1.

Device, all three (a full push-variant.sh each): 11 of 11 optional exports; Enyo: the feature page
before (no adoptedStyleSheets, replaceSync, URL.parse, revert-layer, :has) and after (all, :has
with its switch on), TLS 1.3 on howsmyssl.com, a Persona worn and taken off, Palefill enabled,
about:config, Flash at 23-25 fps; Mochi: the feature page; Mojo: Wikipedia. Nothing pushed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-12 20:35:46 -04:00
NotAlexNoyleandClaude Opus 5.5 272ce0cdb4 Tier 79: the keyboard on Jihad's pages, card pictures, add-on preferences that fit; round 20
The owner's evening list:
- Opening about:settings, about:preferences, about:personas or about:addons takes the focus off
  the address field in all three shells, so the keyboard goes down and the whole page shows
  (device, all three: Jihad page -> BODY, example.org -> INPUT kept).
- The daemon clears the page's focus when the window grows back by a keyboard's height at the
  same width with a field focused, so no caret is left behind (built; needs a finger).
- Installed Personas' cards draw an 800 px copy of their picture, made once beside it and pruned,
  instead of the full header that the image cache kept dropping (device, Enyo).
- An add-on's preferences stack as blocks, dark text fields, trimmed desktop margins: nothing is
  cut off or needs a sideways scroll (device, Mochi, Palefill).
- Mochi defines Mojo.handleGesture, LunaSysMgr's way of giving a card its flicks, which Enyo 2
  lacks: a flick on the web view reaches the adapter, in whole numbers.

Inspector round 20 (Codex out of credits): about:addons measures only the shown view's items and
can shrink again; a page's own scroll jump inside the streaming window is judged once the stream
pauses instead of being lost; CREDITS keeps the three CC pictures in the README strip; AMO card
state, https-only redirects, a full /tmp answered; licences for the formerly bundled Personas;
the two sheets loaded apart and checked in the bundle; dead code removed; kits corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-12 17:13:09 -04:00
NotAlexNoyleandClaude Opus 5.5 df590b3be4 Codex round 17: the site-test restore checks copies by md5; Persona update and colour fixes
build/webos-oe/test-site-profile.sh:
- Every copy (backup and restore, files and both folders) is judged by md5, not by cp's exit
  status: the device's BusyBox cp can report EBADF for a copy that landed.
- The backup records each folder's entry count (hidden and nested entries too) and every file's
  md5; restore refuses a backup that does not match, before touching anything.
- extensions.ini and addons.json are backed up too (an add-on install rewrites them).
- A restore that did not finish leaves the daemon stopped and the backup kept, and still reports
  whether the test CA is in the profile; an unreadable certificate database is never taken for
  "CA gone".
- Setup checks cert9.db and user.js arrived whole and that the daemon came back up; a setup
  stopped part-way says to run restore. Each device script has its own name and removes itself.

about:personas: only a higher version is offered as an update, and an answer about another
Persona or without a version counts as not checked.

jihadPersonas.js: a colour-only Persona's drawn picture keeps translucent colours translucent
(an opaque pair is written byte for byte as before, so its id is unchanged); a multi-picture
static theme that fails part-way removes the pictures it had put in place.

Enyo: the ink reload/stop glyphs for a light address field no longer depend on the lazy browser
view existing. Mojo: highlighted menu labels take the theme's highlight text colour.

cavekit-personas-ui.md R3: an update or re-install of the worn Persona replaces it without Undo,
by design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-12 07:46:59 -04:00
NotAlexNoyleandClaude Opus 5.5 948e1c88be T-390..T-394, T-400..T-413: inspector round 14 fixed; kits and build site brought up to date
The inspector's review of 69c44675..efa6844a (Codex out of credits until 4:59 PM):

- A colour-only Persona's gradient is stretched over the bar (fit "stretch"), not drawn cover,
  which showed one blended row. The component, the three shells and the Personas pane.
- The pane draws a layered Persona's first picture at its own size, not cover.
- Re-installing the Persona being worn under the same id repaints the cards (wearTheme through
  rewear; LWTM sends nothing when currentTheme is set to the same id).
- Colour slots are parsed (inIDOMUtils.colorToRGBA) and sent as rgb()/rgba(); anything else is
  dropped, so a value cannot carry more CSS into the shells' stylesheets.
- The per-Persona pixel budget is checked from each picture's header before it is copied or shrunk.
- about:personas ignores replies to requests it has since replaced, and takes the page number from
  the reply.
- test-site-profile.sh: the backup is removed only after the copies and the CA check pass; files
  absent at setup, an existing user.js, what the test added, a missing certutil and an empty
  address are all handled.
- lightweightThemes.update.enabled is named false beside extensions.update.enabled
  (check-engine-api.py knows both it and xpinstall.whitelist.add are read by the engine).

Found on the device: the restore's clean-up used grep -x, which the TouchPad's BusyBox 1.17 grep
lacks, so every file looked unlisted and Enyo's profile jihad-themes/ and extensions/ were emptied
by both of the day's restores. Membership is now decided in plain sh and must find the list's own
first line before anything is removed (run on the device against a scratch directory). Enyo was
repaired by hand: its 13 pictures copied from Mochi under the same names (md5-identical to Mojo's)
and Palefill re-offered (1.30, active).

Checked on the device (Enyo, Mochi, Mojo): the stretched gradient (Enyo measured #3e2164 top,
#7c3aa8 bottom for #3b1d5e/#7a3fa8), the pane's layered row, a same-id re-install repainting
(#cc2222), hsl() arriving as rgb() and a shadow value dropped, the setup/restore cycle, and against
the local site: the front page, an add-on page, a 404, about:personas' pager (page 1 of 2 → 2 of 2)
and "More on the web".

Kits: cavekit-persona-formats.md and cavekit-addons-site.md ticked with what was measured; R1/R2's
about:personas card, R4's index fields and R11's getMoreURL narrowed. Open: the site's layouts on
other screens, a typed search (a finger), the deploy (the owner's). Review round 14 recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-11 15:51:10 -04:00
NotAlexNoyleandClaude Opus 5.5 efa6844a64 T-392: a Persona's other colours reach the address field, the menus, the second bar and the start page
cavekit-persona-formats.md R3, in all three shells, each as one generated stylesheet (menus are
rebuilt each time they open, the fields swap nodes or come twice): toolbar_field / _text / _border
and the _focus pair on the address field; popup, popup_text, popup_border and popup_highlight on
the shell's own menus and sheets; ntp_background and ntp_text on the start page; icons choosing the
light or dark glyph set; Mojo's command row filled with the toolbar colour. A classic Persona or
none empties the sheet and every part looks as before.

- Enyo: the app and popup menus keep the framework's border widths (their content is laid out
  inside them with negative margins — a 1 px border pulled the app menu off the left edge), filled
  with the theme's colour, the theme's border as an outline; the field's reload and stop glyphs
  switch to the ink set on a light field.
- Mochi: the field's ink glyphs sit on a light disc when the field is dark; a hook step shows the
  start page overlay.
- Mojo: the start page is a document in the WebView, so its two colours travel in its url like its
  shortcuts, and start.html now applies both again on hashchange — re-opening it with a new fragment
  is a same-document navigation that never re-ran its script (its shortcuts had the same problem).

Device (all three, 2026-10-11): the colour-only test Persona paints the bar, the field (cream,
gold border, purple text; white and black while focused), the menus (purple, gold, light text) and
the start page (Enyo and Mojo on screen; Mochi's computed colour, its overlay not showing over a
loaded page); the four-layer test Persona draws its moon, sun, hills and dots in Mochi and Mojo as
in Enyo; wearing Space Fantasy / Aurora Silk / Lime Fizz afterwards puts every part back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-11 14:29:31 -04:00
NotAlexNoyleandClaude Opus 5.5 9e10469957 T-390/T-391: colour-only themes and layered pictures become Personas
A theme package's extras are kept beside its Persona (extras-<hash of id>.json in jihad-themes,
checked when read, deleted with the Persona), because the engine's theme manager keeps only its own
fields and insists on a header picture:

- colour-only packages (no usable picture, a frame colour) get a small picture drawn from the
  frame and toolbar colours (a hand-written PNG, stored deflate) and are fitted to the bar;
- every picture of theme_frame and additional_backgrounds (up to 6) is kept, bounded (4 MB, 4096^2
  pixels each, 6 Mpx per Persona), with its alignment and tiling; SVG is left out;
- the colour slots of cavekit-persona-formats.md R3 are read and sent to the cards (painting them
  is next); a site or index Persona may carry them too;
- the cards and the Personas pane draw the layers as CSS multiple backgrounds; a one-picture
  Persona keeps its old id.

Device (Enyo): the colour-only test package paints a purple gradient bar; the four-layer test
package shows its moon, sun, tiled dots and repeat-x hills where the theme puts them. The shells
read the layer list with a cross-window array check (instanceof failed: the theme is parsed in
another window). render/goanna/test/personas/make-format-tests.py draws the test packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-11 12:34:07 -04:00
NotAlexNoyleandClaude Opus 5.5 faa34d132a Personas pane in about:addons: art, Wear with Undo, author and version, install from file, a Frutiger Aero set; wallpaper pictures fitted
cavekit-personas-ui.md, with the owner's same-day direction folded in ("try and wear seem
duplicative", "author and version fields should be shown for all personas", "default version
1.0", "call themes Personas consistently", "add fruiter aero style themes", "blue nebula looks
like it isnt working properly", "dark fox preview in about:addons not working", "the aero themes
dont display consistently ... like the others").

- components/jihadPersonas.js decorates the engine's own about:addons (no engine change): the
  category is "Personas"; each row shows its picture over its accent colour with the name in
  its text colour on a readable pane, its version (1.0 when it names none), its author, the one
  in use marked and first; Wear / Take off / Remove at 44 px; a "Default look" entry; an Undo bar
  for 30 s after any change; Get Personas (AMO) and Install from file (the .xpi files in
  /media/internal/downloads or /media/internal). Rows added while the pane is open are picked up
  when their binding attaches. Refreshes run on the component's own timers (the page's
  setTimeout, called from the component, never fired).
- Removing a converted Persona deletes its picture from the profile.
- The resource://jihad-themes mapping is made whenever a theme URL is resolved: the constructor
  runs before the profile is known, so after a daemon restart every converted Persona's picture
  failed to resolve.
- Wallpaper-sized pictures (AMO's 3840x2160) are kept 1600 px wide at install (decoded straight
  at that size; full size they exceed the 32 MB image cache and never drew in the engine), and
  the cards fit them to the bar (headerFit) instead of showing their dark corner.
- render/goanna/personas/: five original Frutiger Aero Personas drawn by make-aero-personas.py,
  bundled and added to each profile's Persona list once (not worn).
- Settings -> Appearance is a summary with Manage Personas (opens the pane); "Persona" in the
  confirm, the toasts and Settings.

Device (Enyo, 2026-10-08): wear from the pane, Undo back, Remove (picture deleted), install a
downloaded AMO Persona from file (confirm, applied), Blue Nebula re-installed as 1600x900 and
drawn fitted, the Aero set listed and worn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-08 18:14:08 -04:00
NotAlexNoyleandClaude Opus 5.5 cefb03a88e Owner's finger test: Mojo width loop, inspector P1 (unbounded URLs / NULL strings), menus and theme polish, About page, README, Flash games
From the owner's 2026-10-07 finger-test report:
- Mojo: pages no longer shrink to a stripe. The daemon re-sent the meta viewport whenever its
  computed initial scale changed; the Mojo WebView sizes its node from that scale, so each
  re-send narrowed the page (768 -> 526 -> ... -> 76 px). Re-sent only when the page's
  declaration changes, as the stock server does.
- Mojo: the command-row circles take the theme's accent colour (bookmark glyph included).
- Mochi: rounded dropdowns; the app menu sits flush with the left edge.
- Enyo: refresh glyph centred in the address field; a typed file:/// address opens instead of
  searching.
- Home button default is DuckDuckGo Lite in all three shells and the settings page; a stored old
  default moves once.
- Settings gains an About pane (interface, version, engine, Flash, licences), read at run time.
- README rewritten around what only Jihad does on webOS, with device screenshots.
- Flash test page (render/goanna/test/flash-test-page/) with five real archive.org games.

Inspector round 8b on the previous fixes (Codex out of credits mid-review):
- P1: every URL the daemon sends is bounded, and the shared YAP codec never drops a field
  (an oversized string is sent empty) or yields NULL (an unreadable one reads as "") - a page
  could otherwise crash the card host through strlen(NULL).
- History traversals keep their old re-drive; Open In New Card takes web addresses only; the
  preview mark matches the engine's 30 s; the hold generation is per request and counts
  pass-through gestures.

Still open (docs/PICKUP.md): Mojo pinch in-then-out goes grey (structural), Mochi text soft at
~1.43 zoom, long-press by finger not reproduced by script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-07 09:13:29 -04:00
NotAlexNoyleandClaude Opus 5.5 86f0eee1d5 Codex r7 + inspector r8 fixes; menus match on all three shells; Flash pacing for games; Mojo pinch; DuckDuckGo Lite default
Review fixes (device-verified where noted):
- Re-driven navigations carry the engine's own referrer and policy (content navigations) or the
  link's rules (taps: rel=noreferrer, referrerpolicy, <meta name=referrer>), and ALWAYS the
  initiating document's principal - a withheld referrer no longer makes the load a system one
  (file: and top-level data: are refused again). Six referrer cases and both escapes checked.
- Page titles and URLs bounded on the wire (1024 / 8192 bytes) and title changes coalesced to
  one per 500 ms (a 50 Hz flood: 12 sends in ~6 s).
- Personas: theme origin and base taken at the event; previews only from https sites with the
  install permission, reset when the page is left; only the installed theme is remembered;
  card JSON sanitised and bounded; requests from a document with no site ignored. Static-theme
  XPIs: manifest <= 64 KB, image <= 4 MB copied through the bounded reader stream (a size-lying
  entry stops at its declared size), pending file kept only on yes, hash-named.
- Enyo: the theme lives in ActionBar and reaches the lazily created start page and browser views.
- Settings page migrates only its own stored search engine; confirm text overload-proof.

Menus (owner: the upper-right menu the same on all three, Atlas as reference):
- Mochi: Share / New Card / Bookmarks menus and the app menu Find on Page / Settings / Print,
  opened from the card's title bar through Mojo.relaunch (palm-command open-app-menu).
- Mojo: no icons beside the URL (owner); Share and Bookmarks are command-menu submenus on the
  bottom circles; reload/stop inside the address field (owner); app menu Find / Settings /
  Print; new Bookmarks, Downloads and Print scenes and models.

Flash (owner: optimize for gaming):
- Partial paints were never taken since debd1934: every full frame bumped the strip
  generation, so frames with no off-screen rows always looked stale (16 -> 34 fps paints,
  25 -> 4 ms). The stats line now says why a frame missed the partial path.
- The child's own repaint timer is off by default and the daemon pulls only after 150 ms of
  plugin silence: Flash is the only producer, the gap histogram gathers at 32-39 ms and tick
  work drops by a third. Content frame rate measured with a grid SWF is unchanged
  (22.5 vs 22.1 fps), i.e. the old drivers only redrew frames. JIHAD_PLUGIN_SELF_DRIVE /
  JIHAD_PLUGIN_QUIET_MS restore the old behaviour for A/B.

Mojo pinch (owner: "the page turns grey and I get the copy/select all/reload/find on page
menu"): the long-press timer is cancelled on every gesture start and a hold overtaken by a
gesture is dropped; in the Mojo build the widget owns the zoom (the adapter no longer zooms and
scrolls on top of it). Verified by a finger-like hook step with a held-still control.

Search: DuckDuckGo Lite is the default, DuckDuckGo HTML added, the old default migrated once.

Kits (addons R7, new R10; mojo-ui R6; mochi-ui R2; preferences-ui R2), build site Tier 72,
review findings rounds 7-8, PICKUP night delta. Codex re-review of this commit is owed when its
credits return (Oct 7 1:53 AM).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-06 23:19:27 -04:00
NotAlexNoyleandClaude Opus 5.5 20310b3e09 Personas on all three shells with Firefox's light/dark icon sets; AMO static themes; DuckDuckGo Lite the default search
Personas (lightweight themes) are now verified on the device in all three
shells with Water-Like by stick177, the owner's test persona: installed
from a theme site (confirm in the card's own dialog), painted on the top
chrome at once and at every start, removed live from Settings >
Appearance. The toolbar glyphs follow the theme the way Firefox 52, Pale
Moon and Basilisk do: an ink icon set when the theme's text colour is
dark (generated by luminance inversion from the isis-browser art and our
own icons; Mochi's as data URIs). A -webkit-mask approach was tried first
and renders inverted on this WebKit. Enyo's Onyx toolbar border-image is
turned off under a theme (it painted over the header). The notification
door lets a theme's JSON through whole.

AMO serves every former persona as a WebExtension static theme, which
this engine refuses (ERROR_WEBEXT_FILE). A theme-only package is now read
by components/jihadPersonas.js, its image unpacked into the profile and
applied as a lightweight theme with the user's yes; the cards get the
image as a file URL. Verified with AMO's water_like-2.0.xpi.

DuckDuckGo Lite and DuckDuckGo HTML join the search engines in every
shell and on the settings page; Lite is the default, and a stored
"duckduckgo" from before (the old default) moves to Lite once, recorded
so a later deliberate choice of the full site stays.

Test hooks: Enyo answers a confirm through the popup's own Accept; Mojo
gains a click step and an address-field blur; the toolbar probe counts
ink glyphs; the persona test site lays its buttons out as large fixed
blocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-06 20:53:15 -04:00
NotAlexNoyleandClaude Fable 5.1 5ccc50f424 Personas (lightweight themes) — in progress: engine events, component, Luna getTheme, Mochi verified; Enyo/Mojo/buttons/static-theme XPI untested
Firefox 52, Pale Moon and Basilisk personas are one format: the lightweight
theme JSON a theme site dispatches with InstallBrowserTheme / Preview /
Reset. The engine already has LightweightThemeManager; this adds the
browser's half: GoannaRenderPage catches the events on the content
document, the pump hands them to components/jihadPersonas.js, which asks
the user through the existing confirm door, applies through the manager,
records the theme in a pref and pushes it on the notification channel;
the daemon answers getTheme over Luna for a card that starts later. The
notification door lets theme JSON through whole (it cut everything at
200 characters). The three shells paint the header image and colours on
their top chrome and tint their button glyphs with textcolor; the
settings page gains an Appearance section; a theme-only WebExtension
static theme (what AMO serves every former persona as) is converted to a
lightweight theme when its install fails as corrupt.

Device-verified on Mochi only: install from a theme site, confirm, toolbar
painted live and at start. Untested: Enyo and Mojo, the button tint on any
shell, the static-theme conversion, the Appearance section. Test assets
under render/goanna/test/personas (Water-Like by stick177, the owner's
test persona). Tabs kit: the strip takes the theme too. PICKUP has the
exact state and the next steps; stopped at the usage limit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-06 13:44:44 -04:00
NotAlexNoyleandClaude Fable 5.1 2b1e749133 Go-ahead session: reinstall audit for Enyo and Mojo, reboot test, a real print, Codex round 6 fixes, tabbed-browsing groundwork
With the owner's go-ahead for everything that needs no hands on the device:

Enyo and Mojo were removed through the supported installer and installed
from nothing, with the citizen-audit snapshots around each step: prerm
left no app dir, job, shim, impl, role/service file, state dir or socket;
the hub forgot the name; the other daemons kept their PIDs; the fresh
postinst registered the role live, the db8 kinds (Enyo), and started the
daemon from the app's own deviceroot with the Luna service up on first
start. Palefill landed in each fresh profile. Enyo's history was exported
from db8 and put back; Mojo's localStorage history restored from a backup.
The device was then rebooted: all three daemons up from the shipped
upstart jobs, registered on first start, cards rendering, root read-only.
One page was printed from the Mochi dialog, to the IPP sink first and then
to the Epson over IPPS (job accepted).

Codex reviewed the range only the inspector had seen. Fixed: the padlock
followed the requested address, so a typed https url showed the previous
page's padlock (and its certificate) under the new address — Enyo and
Mochi now show it only for the address the engine has committed; Cut
compared text alone, so the same text in another field could be deleted —
the copy now remembers its field (weakly) and its time, and a cut needs
the same field within 15 s; copied text is let go after 15 s instead of
living with the page; the host test sinks return the count OnNotification
now asks for; push-engine-update.sh refuses a full push from a stale
bundle before touching the device.

Tabbed browsing: groundwork only, by the owner's word. A kit
(cavekit-tabbed-browsing.md), a design note (docs/TABS-GROUNDWORK.md:
pages held in the daemon, one connection per card, tab commands over the
variant's own Luna service, the YAP contract untouched; one adapter per
tab rejected on 37.7 MB of shared memory per connection) and Tier 71 of
the build site, every task pending.

The OE recipe's SRCREV moves to db070f0003, the engine commit the gitlink
already carries. Kits, build site, review record, loop log, evidence and
PICKUP updated. Pushes are the owner's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-06 11:03:13 -04:00
NotAlexNoyleandClaude Fable 5.1 31c5f445e9 Review round 4: Palefill installer (Codex) and the last fix round (inspector)
Codex, on the Palefill installer: an add-on the manager would disable at once is
not installed (and not recorded as offered); the bundle requires the installer
component as it requires the file; a copy counts as handed over only when the
notification reached a subscriber (OnNotification returns the count); the
add-on manager's provider module is pushed only with the libxul it was staged
with, and its companion goes with it. Palefill's own whole-response buffering is
left as its design and documented.

The inspector, on the last fix round: the "pressed field must be the focused
one" rule passed for <body>, which contains everything - the focused element
must now be a text control or inside an editing host; Cut said "Cut." over a
target it could not edit - it refuses unless an editing host and reports only
what it removed; read-only fields had lost Copy (a control's own selection is
invisible to the window's) - they stay on the field path, readOnly rides the
hold answer and the menus drop Paste and Cut; script and style text is skipped
by the bounded copy walk; an unreadable protocol version is no longer reported
as SSL 3; the push script no longer dies on a bundle without bundled-addons/.

On the device (Enyo, scripted): a hold on a read-only field offers Select All,
then Copy, no Paste, no Cut, and the copy lands; a word copy and a hold on a
normal field are as before. Daemon b7be3e3c on all three variants.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-06 02:23:10 -04:00
NotAlexNoyleandClaude Fable 5.1 debd19341d Pages stay painted: frames are composed; padlock shows the certificate; review fixes
Three pieces of work that share files, plus what two adversarial reviews found
in them (Codex until its credits ran out, then the Cavekit inspector).

THE PAGE STAYS PAINTED (owner: "pages only load when observed ... the entire
page should stay rendered at once unless it's very long, so that navigation and
zooming is smooth")

- The painted region uses the whole shared segment: 4096 rows in portrait, up
  from a 2048 cap that stood on a worry about a GPU texture limit. Measured: a
  4096-row region wraps and blits. JIHAD_REGION_ROWS_CAP is the way back.
- Rendering that region whole for every frame does not survive its size: 4096
  rows of a real page measured 300-1400 ms, with the daemon deaf to input
  meanwhile. Frames are now COMPOSED: rows the newest frame already holds are
  copied from its buffer, rows it lacks are rendered (at most 768 a frame, so a
  region grows over a few frames), the visible band is drawn as before, and at
  z~1 the band of the frame copied from is redrawn from the document where it
  is no longer this frame's band (fixed headers, the caret). Same page: 110-180
  ms a frame.
- What a copy cannot know is whether the page changed off screen, so after a
  frame that reacted to a change the off-screen rows are swept, a few hundred
  at a time, by maintenance frames once the page is loaded and still, with a
  pause between sweeps. Rows that are owed a sweep do not count as covering a
  pan. A large change at the same scroll repaints the band only.
- Zoomed in, the region is as wide as the content, up to two viewports, so a
  sideways pan has something to show. The adapter's rotation guard now compares
  bufferWidth (the viewport) rather than renderedWidth (the region); the header
  layout is unchanged.
- The coverage margin scales with the region, so a short or wide region is not
  judged uncovered at the position it was just painted for.
- <state>/dump-request writes one frame to /tmp for inspection (refused when
  /tmp is short of room: forgotten dumps once filled that 40 MB RAM disk).

CORRECTION to 3218caad: its message says a scripted scroll over a whole-page
frame "logs no uncovered rows". The adapter call the test used
(pageScaleAndScroll) is an empty stub; nothing scrolled, and the claim proved
nothing. What is measured now: on a colour-coded 16000-row page with a fixed
header, after jumps by its anchor links, every row of the dumped frames holds
what the page has there, the header is only at the top of the visible band, and
no row is stale. A finger pan cannot be scripted on this platform (script-made
pointer events do not reach the plugin, and the adapter's flick entry needs a
drag in progress), so how a real fling looks is still the owner's to judge.

THE PADLOCK (owner: "add clicking on the lock to view certificate information")

- pageCertificate on the daemon's own Luna service, answered to this variant's
  own card only, in the reply itself: the loaded page's certificate (from the
  docshell's security UI), the connection's protocol and cipher, and whether
  the certificate was believed (overridden / untrusted / mismatch / badTime) and
  whether the page counts as secure (broken).
- Enyo: the existing padlock is a button; it opens the certificate view, which
  gains fingerprint, connection and trust rows. Mochi and Mojo had no padlock:
  Mochi gets one inside the address field, Mojo one drawn as the field's
  background with the tap taken on its left edge. No padlock over a failed load.
- The adapter's pageFocused was an empty stub. Thumbnails, printing and now the
  padlock all rely on a card saying "I have the focus" before a Luna request;
  with two cards open the daemon answered for whichever connected last. It now
  sends the adapter's existing asyncCmdPageFocused, and the daemon clears the
  focus when the focused card gives it up.

REVIEW FIXES TO THE LONG-PRESS MENU (657282c4)

- Page commands are queued and run from the guarded pump, tied to the document
  they were asked of; copy answers the adapter's callback with the real result.
- Cut is two steps: the card copies, and only once the text is on the clipboard
  asks for the deletion, which the engine refuses unless the selection is still
  exactly what it copied, whole, in a field that accepts edits. The card is told
  how it ended.
- State is per page (one component serves every card); answers about a copy or
  a cut are acted on only by the card that asked.
- A page that handles its own contextmenu event gets no second menu. Read-only
  fields get the page's menu. Passwords are never copied or cut.
- Clip files have random names and plain-ASCII contents; a selection far too
  big to copy is measured and read with a bound instead of being built whole.
- Paste is bounded and sent in pieces: one IPC message holds 16 KB and a longer
  string was dropped whole, silently.
- A TLS failure that comes with no certificate is a failed connection, not the
  "certificate is invalid - Trust Always?" dialog it used to raise.

On the device (all three variants on one build from the installer): Enyo and
Mochi pass the scripted long-press flows, the padlock, the failed-load case and
the focus hand-over; Mojo shows its padlock on an https page. Mojo's menu and
padlock tap have no test hook and need a finger, as does any real pan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-05 18:40:50 -04:00
NotAlexNoyleandClaude Fable 5.1 657282c431 Long-press on a page raises a menu, as a right-click does on a desktop
Owner: "long pressing on the page doesn't do anything but it should, like on a
desktop browser". It did something only on links and images; everywhere else
the hold reached the engine as a contextmenu event and that was the end of it.

Now, in all three shells:

  on text       the word under the finger is selected and the menu offers Copy,
                Select All, Back/Forward (when there is somewhere to go),
                Reload, Add Bookmark, Find on Page
  on a field    the field takes the focus with the caret where the press was,
                and the menu offers Paste, Select All, and Copy/Cut when
                something in it is selected
  on nothing    the same page menu, without Copy

A press inside what is already selected keeps the selection (so Select All,
then a press, then Copy copies the page), as a right-click does.

How it is put together, within the frozen contract:

- The card does not claim a hold that is not on a link or an image. The
  adapter then sends holdAt as it always did; the daemon dispatches the page's
  contextmenu event, focuses a field that was not focused, and hands the press
  to a new bundled component, components/jihadPageCommands.js, which does the
  selecting and answers on the notification channel: page-hold
  {editable, selected}. The card that was waiting opens its menu on that.
- Select All, Copy and Cut are the adapter's own selectAll / copy / cut, which
  were stubs in the daemon; they now reach the component. Commands act on what
  the last press was on: the page's text even while a field has the focus.
- The clipboard belongs to LunaSysMgr and only a card can write or read it.
  Copied text therefore goes to the card as a one-call script in the app's own
  clip/ directory (the one place a card may load a local file from), announced
  as page-copied with the file's name; the file lives fifteen seconds. The
  hit-test schema forbids extra keys and the notification channel is public
  and cut at 200 characters, so neither could carry it.
- Paste is the card reading the clipboard and sending the text with
  insertStringAtCursor. The system's paste is asynchronous and was measured
  taking more than 60 ms, so the read watches for up to 600 ms.
- The engine's selectAtPoint adds a range (every word ever pressed stayed
  highlighted), takes two words when the press is on a word's last half-letter,
  and selects the nearest word for a press on empty space. All three are
  corrected in the component.

Mojo's web view claims no hold at all, so there every hold goes this way and
the card the user is in opens the menu.

prerm also removes the clip/ directory. card-selftest.sh reads the gzipped log
generations; the hooks gain h (hold), m (list/pick the menu), c/k (read/set the
clipboard) and p.

On the device (Enyo and Mochi, scripted holds through the adapter's
mouseHoldAt - not a finger): a hold on a word selects that one word and Copy
puts it on the clipboard; a hold on empty space offers no Copy; a hold on a
field, Paste, lands the clipboard at the pressed position; Select All then Cut
empties the field, fires its input event and leaves the text on the clipboard;
Select All on the page with a field focused selects the page, and Copy copies
it. Mojo's menu has not run: it has no test hook and needs a finger.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-05 16:23:32 -04:00
NotAlexNoyleandClaude Fable 5.1 b38a87fcd9 one Settings: the cards' own "Browser Settings" panels are merged into the settings page
Owner: "merge browser settings into settings", "remove Help", "print is
greyed out".

The settings page (about:settings / about:preferences) was already the one
surface the three shells share, but the Enyo and Mochi cards each kept a second
panel for what the page could not do. It can now:

  - Search engine: a row in the Browser pane. Stored with the home target and
    the start-page links in jihad.chrome.settings and adopted by the cards from
    the url fragment, like those. Fixed list (DuckDuckGo, Google, Wikipedia),
    the same three ids in each shell.
  - Clear Data: Bookmarks, History, Cookies, Cache, each asking on the page
    before it runs (the row turns into the question, with Clear and Cancel).
    Cookies and cache are the engine's and are cleared there. Bookmarks and
    history are lists the card keeps, so the page asks the cards over the
    notification channel ("chrome-clear-..." categories; a card carries the
    request out and shows the sentence as its toast). History also clears the
    engine's Places record.
  - Flash was already the "Adobe Flash" row in Content.

Shells: one "Settings" menu entry each. Enyo loses its Preferences view and
Mochi its JihadPreferences panel. Enyo no longer uses com.palm.universalsearch:
upstream wrote the SYSTEM-WIDE default search engine whenever the browser's
choice changed and wiped the system's optional engine list when browser history
was cleared. Mojo's address bar now follows the chosen engine too, and its
history list answers the page's clear request.

Enyo app menu: Help is gone (it launched the system help app at an HP address
that no longer exists). Print is never greyed out: upstream disabled it while a
page was still loading - measured, a page sitting at 99% left it dead for
seconds - and off a page; now it prints what is on screen, and on the start
page says there is nothing to print.

Mochi: a card opened ON a page no longer focuses the address bar, which put the
keyboard over the lower half of that page.

Verified on the device with real clicks sent through the adapter (clickAt, what
a tap becomes; card-selftest.sh <shell> run settings:<steps>): Enyo - engine
changed to Google and back, the card's default search followed; a test bookmark
cleared, toast shown. Mochi - the same, plus history 4 -> 0, cookies and cache
cleared (daemon: notify privacy ...). Mojo's change is not yet run on the device.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-05 15:11:09 -04:00
NotAlexNoyleandClaude Fable 5.1 de0bc0f183 Enyo shell: Print opens the browser's own printer dialog
The app menu's Print used the system PrintDialog and the frozen printFrame
command. The system dialog lists HP printers only, and printFrame is a no-op in
this daemon, so Print could not print on anything.

JihadPrintDialog asks the daemon to look for printers, lists each one as it
answers (name, then model and address), takes a typed address for a printer
discovery cannot see, and shows the job's progress and result. The daemon's
"print-..." notifications go to the dialog while it is open and to a toast
otherwise. $enyo-lib/printdialog is no longer loaded.

On the device, driven through the shell's own handlers by the test hook
(card-selftest.sh enyo run print[:address]): the dialog listed the Epson
ET-2800 at 192.168.1.253 and a Mac-shared ET-2720; with the test sink's address
typed in, it was found, picked and sent one sheet, the dialog ended on "Sent 1
page to ...", and the sink's copy of the sheet is the page.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-05 09:15:13 -04:00
NotAlexNoyleandClaude Fable 5.1 640ab93fc4 Enyo toolbar: the Bookmarks/History icon's circle is whole
The previous version copied its neighbours' habit of cutting the outline away
around the accent. On a circle that reads as a broken circle - the ring resumed
under the ribbon's notch, so the notch and the cut added up to a hole (the
owner: "the circle is incomplete, gap just below the bookmark glyph").

The ring is now left whole and the ribbon laid over its right edge, placed so
the ring runs into the ribbon's left tail rather than under its notch. Checked
in a device screenshot: same rows as Share and New Card (42..61).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
2026-10-05 09:02:26 -04:00
NotAlexNoyleandClaude Fable 5.1 ac972e214f Enyo toolbar: the Bookmarks/History icon built the way Share and New Card are
The owner asked for it to be more in line with the two icons it stands beside.
Those two share one construction, read off their pixels: a body drawn as a 2 px
outline standing on rows 24-25, a solid accent in the upper right (rows 5-15),
the outline cut away for 2 px around the accent and resuming at row 18, white
at an opacity falling 6.4 a row from 224 to 96 with every upward-facing edge
one step brighter, a one-row black shadow, and pressed a solid white core in an
rgb(83,183,255) glow that leaves the middle of the body dark.

The clock's ring is now that outlined body, its hands are 2 px, and the ribbon
is the accent where the plus and the arrow are. The previous redraw kept the
ribbon at the lower right and used the house's heavy finish, which left it
bolder and higher than its neighbours.

Measured in a device screenshot: share, new card and this icon occupy the same
rows (42..61) and the same width.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 08:08:22 -04:00
NotAlexNoyleandClaude Fable 5.1 904be8a0bf Enyo toolbar: the Bookmarks/History icon at its neighbours' weight and on their line
The design is kept (a clock with a bookmark ribbon over its lower right). Its
strokes were 2 px beside 3 px neighbours, it stood two pixels below their line
(down to y = 27) and a pixel right of centre, and its shading was its own.
Redrawn with the ring and hands at the neighbours' weight, in the box they use
(y 5..25, centred), and finished by the same code as the Home icon.

make-enyo-home-icon.py becomes make-enyo-toolbar-icons.py and writes both.
Measured in a device screenshot: share, new card and this icon all start at the
same row and end within a pixel of each other.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 07:57:43 -04:00
NotAlexNoyleandClaude Fable 5.1 5264c925af Enyo toolbar: a Home icon drawn like the icons beside it
The first home icon was a flat grey house filling its 32x32 cell edge to edge,
with a flat blue pressed state - larger and flatter than the stock webOS glyphs
it sits between. Read off those glyphs: white at an opacity that falls from
about 86% to 56% down the glyph, over a soft black shadow; pressed, solid white
inside an rgb(92,172,230) glow; about 24 px wide with a margin.

packaging/branding/make-enyo-home-icon.py draws the house to those numbers
(roof with eaves, chimney, walls, door) and writes the two-state sprite.
Checked in the toolbar on the device.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 07:45:56 -04:00
NotAlexNoyleandClaude Fable 5.1 aab915975b Enyo shell: View Certificate works on webOS CE
The certificate view asked com.palm.certificatemanager for the details and
enabled its button only when that answered; on webOS CE a card gets 'Service
does not exist', so the button was dead. It now loads the details the daemon
writes into the app's own directory while the warning is open (the same file
the Mochi shell reads) and falls back to the service if that file is absent.
A certificate with neither a common name nor an organization no longer throws
(the code set a control that does not exist), and a date that does not parse is
shown as sent instead of failing in the formatter.

Device: button enabled, issued to / issued by / validity / serial shown.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 15:50:34 -04:00
NotAlexNoyleandClaude Fable 5.1 8b27842ca2 Enyo shell: the three content rows leave the card panel; the Flash switch becomes real
Owner decision 2026-10-02 (cavekit-preferences-ui.md R2): Block Popups, Accept
Cookies and Enable JavaScript are owned by the settings page. The card panel
loses the three rows and gains a More Settings entry that opens the page, and
the card no longer re-applies a copy an older version stored in db8 - a stored
'blockPopups: false' with no row left to change it would otherwise be sent at
every launch.

The Enable Flash switch wrote the system preference 'flashplugins', which
nothing in this browser reads. It now reads and sets the daemon's value through
the variant's Luna service, and says so when no Flash plugin is installed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 14:53:00 -04:00
NotAlexNoyleandClaude Opus 4.8 57eb48724c Session 2026-08-15: audio backend, chrome-typing fix, cert store, device verification
Engine audio (gre-widgets R1/R7): switch cubeb ALSA -> PULSE backend, which is
what webOS's audio policy requires. mozconfig --enable-pulseaudio, pulse-0.9.22
sysroot headers matching the device ABI, and patch 0030 targeting JIHAD_PULSE_SINK.
Device-verified AUDIBLE (user-confirmed "I hear the tone"): decode -> cubeb pulse
-> DAC -> codec -> speaker/headphone. The prior ALSA backend was silent because it
presented as an anonymous ALSA-plugin stream that module-palm-policy muted; the
pulse-native stream identifies as the app and is left at full volume.

Chrome-page typing (preferences-ui R6): GoannaRenderPage::InsertText recovers a
null mFocusedEditable from document.activeElement, which dodges the focus-manager
window-scoping that made chrome documents (about:preferences) unable to receive
typed text. Device-verified: text now inserts into about:preferences fields.

Cert store (browser-services R5): new JihadCertStore dlopens libPmCertificateMgr
and integrates accept/trust with the webOS platform cert store; three defects fixed
(empty certFile, nsresult-in-Palm-ordinal-space, Trust-Always vs Trust-Once).
Device-verified end to end incl. the platform store write.

IPC contract (ipc-contract R1): vendored + patched the YAP generator so a
regeneration reproduces the shipped wire with zero hand edits.

Touch events (input-bridging R3): doTouchEvent enabled behind JIHAD_TOUCH_EVENTS,
28-byte NpPalmTouchEvent header re-staged, double-activation suppressor.

Non-blocking toast (gre-widgets R5): daemon PostNotification + Luna subscription +
card toasts; device-verified.

Mojo settings entry (preferences-ui R4): app-menu Settings item + settingsUrl(),
per product-owner direction reversing the earlier no-mojo-settings instruction.
All three variants now have a settings entry.

Plus: opal machine kernel string corrected (shortloin), prebuilt-input manifests,
build-goanna-arm.sh pipefail fix, desktop test harnesses (prefsui, xpi-mismatch),
and extensive device verification recorded in context/impl/impl-device-2026-08-15.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 18:17:25 -04:00
NotAlexNoyleandClaude Opus 5 c2106583af about:preferences + settings merge, home button, start-page links
Adds an in-browser settings page and moves the two settings the card shells own
(home button target, start-page shortcut list) toward it. Device-verified on a
TouchPad throughout; kit counts re-counted programmatically, not asserted.

about:preferences / about:settings
  - nsIAboutModule JS component + a chrome://jihad-prefs/ package. No C++ change
    and no daemon rebuild for the page itself: the engine's own about: table stays
    the two static credit pages it was written for.
  - In-content HTML, not <prefwindow>. That is a chrome-WINDOW binding and this
    embedding has no window to open one in -- the same assumption class that broke
    about:addons. Basilisk's shape with Pale Moon's panes.
  - Installed by BOTH builds; both now fail loudly rather than skipping when inputs
    are missing, which is how the two builds drifted in the first place.

Shell changes
  - Home button right of forward in all three variants, default
    https://start.duckduckgo.com/ (about:home is not a page this engine serves).
  - Start-page links customizable. Mojo's start page is a DOCUMENT rendered by the
    engine, so its list travels in the url fragment; isStartPage() now compares
    without it.
  - Mojo command row: scrolls, circular, centred, find is an icon.
  - Mojo url bar: TextField is two nodes that swap on blur, so the field chrome
    belongs on the container.

Engine / daemon
  - jihad::GetChromeSettings + a getChromePrefs Luna method (works, but private-bus
    only today -- see the handoff).
  - find_test.cpp now ASSERTS hit/miss and gates the exit code on it. It previously
    printed the result and asserted nothing, so it could not fail because of find.
    Runs green for find on the desktop harness.

Fixes found by adversarial review (see docs/HANDOFF-2026-08-06.md)
  - Restored the Enyo preferences panel's reachability and five handlers a bounded
    delete had removed, which had taken the search selector, the JS/Flash toggles
    and all four clear-data actions out of the product.
  - adoptFromUrl is gated on the url PATH, not the scheme: about:blank is
    content-loadable, so a scheme check let any visited page rewrite the home button.
    Stored urls allowlisted to https?: and our own about: pages.
  - Removed three decorative rows (a pref existing is not a pref being honored) and
    fixed the localized read of intl.accept_languages.
  - Mojo history had never persisted: Mojo.stringifyJSON does not exist in this
    framework build.

Known-broken, with the next step for each, in docs/HANDOFF-2026-08-06.md:
page->card settings sync, public-bus Luna, and an unexplained scroll report.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ8PNmUneWxA3QM3Z3W1aw
2026-08-06 07:03:47 -04:00
NotAlexNoyleandClaude Fable 5 0facbb4ffb The Luna service works: our daemon now answers clearCache and clearCookies
Registered per variant rather than as com.palm.browserServer, which is the stock daemon we
coexist with. On device the daemon logs `luna: serving palm://net.riverstonerelay.jihadBrowser/
{clearCache,clearCookies}`, luna-send returns returnValue:true, and -- the part that matters,
because a true from the STOCK browser is precisely the false pass this was written around --
our own daemon logs `luna: clearCookies` and `luna: clearCache`. The hub routes to us and our
handlers run, calling the same jihad::ClearCookies/ClearCache the YAP commands already use.

Three things had to be right and each was wrong first, so each is now written down where the
next person will meet it:

A Luna service name may not contain a hyphen. LS2 rejects it at the CALLER with "_UriParse:
Not a valid service name" before any daemon is involved, and LSRegister fails -1027. Our YAP
names all have hyphens, so the Luna name is the camel-cased form -- jihadBrowser,
jihadBrowserMochi, jihadBrowserMojo -- matching the platform's own idiom.

The hub needs prv and pub role files keyed on exeName, and exeName is the BUNDLED LOADER
ld-2.23.so, not jihad-browserserver, because the upstart job execs through it.

LSMethod has three fields on this device. I copied the two-field shape from luna-sysmgr's
sources in this workspace, which are LS1-era, but the TouchPad ships luna-service2. With two
fields the library walks the table at an 8-byte stride where it reads 12: registration
succeeds, the service appears on the bus, and every call answers `Unknown method
"clearCookies" for category "/"`. A registration that succeeds while registering nothing is
the worst shape of wrong.

Not re-verified in this run: that a non-empty store is emptied. The cookie store was empty
before and after, so only the routing was exercised end to end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 02:35:22 -04:00
NotAlexNoyleandClaude Fable 5 473ea279f1 New kit: in-browser preferences, and fix the Mojo launcher title
The Mojo variant showed as "Jihad Browser" in the launcher. The repo had said "Jihad Mojo"
since the rename; the DEVICE had a stale appinfo.json, because neither push-engine-update.sh
nor push-card-js.sh carries app metadata -- only a full variant push or a real .ipk install
does. So the repo was right, the device was wrong, and nothing in the fast loop would ever
have corrected it. Pushed the file and restarted LunaSysMgr; all three now read Jihad Enyo,
Jihad Mochi and Jihad Mojo. The trap is written into the device build README, since the next
rename would hit it the same way.

Adds cavekit-preferences-ui.md for about:preferences / about:settings in the Pale Moon and
Basilisk idiom, across all three variants. Two facts establish what the work actually is,
both by inspection rather than assumption: UXP ships the prefwindow/prefpane/preference XBL
machinery in toolkit/content/widgets/preferences.xml but there is NO about:preferences page in
the GRE -- it is application-supplied in both forks -- and this port already ships two
app-supplied chrome pieces, the branding package and the XPI install prompt, so the mechanism
is proven rather than speculative.

The requirement worth reading first is R5. Settings are currently three different stories: the
Enyo app pushes preferences into the engine through the frozen YAP commands, the Mochi app
persists the same toggles to db8 and deliberately never pushes them, and Mojo has no
preferences UI at all. A shared engine-side page is the only surface that can be the same in
all three, and R5 requires one source of truth per setting rather than two that disagree
silently -- which is the failure the Mochi omission already is.

R2 forbids decorative controls for the same reason, and R3 requires a change to be proven from
the profile rather than from the UI that made it, the pattern that closed about:config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 00:08:45 -04:00
NotAlexNoyleandClaude Fable 5 0b1dd43422 Docs: reconcile kits and READMEs with the current state of the project
The kits and READMEs had drifted from the code in ways that would mislead the next
reader — several said the card dev loop was broken, the <select> popup blocked, and
only the Enyo variant live on hardware. All three are now false.

Kits:
- overview: rewritten device-reality block (three variants live; dev loop restored and
  how; select popup closed on all three; the -webkit- prefix constraint), priorities
  re-ordered around the now-unblocked queue, and the per-domain requirement counts
  re-derived from the kit files — the old "12 domains, 62 requirements" summary had
  drifted from what it summarised. Totals are now counted, not asserted: 13 domains,
  77 requirements, 274 acceptance criteria (204 met / 18 partial / 52 open).
- ui-shell R5, mochi R6: card-side <select> popup, device-verified. The Enyo one records
  the standing rule the investigation established — there is no app-side popup code, and
  re-adding an onOpenSelect handler or patching BasicWebView.showPopupMenu breaks it.
- mojo: R1-R3 and most of R4 marked met against device evidence, plus a new R6 for the
  command-menu actions (new card / history / share, history without db8 kinds).
- offscreen R7 (new): "engine popups reach the user" — collects the separate-display-root
  defect that had only lived in impl notes. Its <select> criterion is met; the
  <menupopup> overlay-composite criteria are diagnosed and open, and are now the top
  priority. addons R2 gains a tools-menu criterion pointing at it.
- Both UI kits record the platform constraint: the card WebKit ignores unprefixed
  box-sizing/flexbox.

READMEs: root status/known-issues/roadmap rewritten (three variants live, scrolling and
long-press and <select> done, <menupopup> named as what remains); build/webos-oe gains a
deploy-scripts section explaining why push-card-js.sh distrusts a naive push; the three
app READMEs record their own popup story and the prefix constraint; AGENTS.md notes that
the adversarial reviewer is Opus, plus the novacom stdin-EOF race and the JS cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 17:16:01 -04:00
NotAlexNoyleandClaude Fable 5 ca4dcc52ec Start pages: same logo, name, engine line and hint on all three variants
The Mojo start page was a plain text document while Enyo and Mochi showed the
bundled logo; it now carries the same logo, title, engine line and colours, laid
out to match. All three gained the "Type a web address or a search in the bar
above, then press Enter." line, in the same position and wording.

The two card-chrome variants build the page from their frameworks; Mojo's stays a
packaged document (a Mojo scene has no equivalent overlay, and the file renders
through our own engine anyway). Device-verified on all three.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 16:55:02 -04:00
NotAlexNoyleandClaude Fable 5 322f26bda2 Select popup anchors under the tapped box (daemon ships the rect)
The stock anchor path is dead in this embedding: enyo.WebView.openSelect
positions from _selectRect, which only webviewClick(inSender, inEvent, inInfo)
sets — and BasicWebView declares no onClick event, so inInfo can never arrive
and every popup opened dead-centre (Opus review #8; user report).

The daemon owns the element, so it ships the geometry: BuildSelectPopup appends
an ADDITIVE "rect" key (card px) to the popup JSON — the framework's own parse
ignores unknown keys, so the isis shape consumers are untouched. Mapping is the
exact inverse of docToViewport: at z~1 card px == getBoundingClientRect px;
zoomed, engine scroll parks at 0 and the visual-viewport pan carries the offset
(card = (client + pan) * z).

Card side (JihadEngineOverride): delegating patches on enyo.WebView.showSelect
(capture m.rect) and openSelect (top edge at the box bottom, centred on the box,
clamped to the screen, flipped above when it would run off the bottom; no rect ->
original centre behaviour). Device-verified on fb1: the list opens flush under
the <select>.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 14:40:25 -04:00
NotAlexNoyleandClaude Fable 5 e3de7d8afa Select popup works: daemon emits the isis JSON shape; card side is the stock framework
Root cause of the empty popup (impl-select-popup-2026-08-03.md): the card never
needed ANY app-side popup. enyo.WebView consumes the inner BasicWebView's
onOpenSelect itself (showSelect -> createSelectPopup -> PopupList) and replies
via selectPopupMenuItem — it never re-publishes the event, so the app's
onOpenSelect handler was unreachable dead code. The framework parses
items[].text/isEnabled (+ top-level selectedIdx) — the isis BrowserComboBox
shape — while our daemon wrote label/enabled/selected, so the framework built a
list of undefined captions: the "empty popup". Daemon now emits the isis shape
byte-for-byte (text/isEnabled/isSeparator/isLabel + selectedIdx); the app's
custom popup and handlers are deleted.

Opus adversarial review (FIX-FIRST verdict) hardened the reply path, folded in:
- #1 disabled options were selectable: the framework PopupList never reads its
  disabled flag, so ApplySelectPopup is the only enforcement point — re-read the
  options, refuse disabled (attr or enclosing disabled <optgroup>, #2),
  out-of-range (#4, page JS can rewrite options while the popup is up), and
  no-op picks (#3: re-selecting the current option must not fire change).
- #6 a short/empty popup-json write now fails closed (unlink + drop) — the card
  otherwise throws inside createSelectPopup after showSpinner() and the modal
  scrim sticks until relaunch.
- #7 popup id sequence is process-global (isis idSeq parity) so a daemon page
  rebuild cannot reuse an id the framework has cached a stale popup under.

Device-verified: the popup renders all options with captions (fb1 + daemon log
paint after the injected tap). Follow-ups: optgroup header rows (#5, needs a
reply-index remap), mochi/mojo card-side handlers (both currently drop
showPopupMenu silently).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 14:28:25 -04:00
NotAlexNoyleandClaude Fable 5 512ede2fa0 Card dev loop restored: push-card-js.sh proves reloads; start page says Jihad Browser
Two root causes of the frozen card-JS loop, both nailed on device:
1. novacom run DISCARDS output that arrives after host stdin EOF (channel close
   races the reply). luna-send's bus round-trip lost that race under load, so
   running/close/launch "returned nothing" — hold stdin open (sleep N |) and the
   flake vanishes (2/2 vs 0/3, reproducible).
2. The WebAppMgr in-process JS cache really does serve the previous build after
   a plain close+relaunch (disk md5 new, card booted the old stamp). A
   LunaSysMgr restart per cycle busts it deterministically (now the default).

push-card-js.sh encodes the whole loop: per-push @DEV@ stamp sed'd into the JS,
md5 both sides, close by the REAL processid (response field is lowercase
"processid", close request wants camelCase "processId"), relaunch, then REQUIRE
the stamp in /var/log/messages — the only accepted proof of a reload. Also
wakes/holds the device (doze silently no-ops luna-send) and routes every device
command through a pushed helper (novacom mangles quoted args).

Browser.js: [JIHAD-BOOT] runtime boot marker + [JSEL] select-popup diagnostic.
StartPage.js / mochi JihadBrowser.js: brand title under the start-page logo is
always "Jihad Browser" (user request; verified on device for Enyo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 10:48:49 -04:00
NotAlexNoyleandClaude Fable 5 8cc69a5e14 Select popups: ClickAt never falls through for a dropdown; card list = plain buttons
Daemon: a dropdown <select> tap now always returns from ClickAt (whether it
queued the card popup or a dedup skipped a duplicate), so it can no longer fall
through to a normal click that focus-rings the <select> ("box around Apple") or
opens the engine's own 0x0 combobox.

Card: switched the option list from PopupSelect.setItems (which rendered empty on
device) to a plain Popup with an explicit Button per option built in
showSelectPopup — predictable rendering. UNVERIFIED on device: the card froze on
an early cached build all session (fresh JS would not load despite reboot /
killall LunaSysMgr / version bump / md5-verified disk) and card enyo.log stopped
reaching palm-log, so the empty popup could not be diagnosed between "still stale
JS" and "Popup content doesn't paint in this embedding". The two broken dev-loop
tools and the restore-the-loop-first plan are in
context/impl/impl-select-popup-2026-08-03.md. The daemon/engine/adapter half is
device-verified and solid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzkQtdXpngYfZ2yG1XDfkB
2026-08-03 09:24:29 -04:00
NotAlexNoyleandClaude Fable 5 ab86fa87e3 Select popups: card-native <select> list via the Atlas msgPopupMenuShow contract
isis/Atlas never rendered <select> dropdowns card-side — QtWebKit/WPE paint the
combobox into the plugin surface, so neither has an onOpenSelect handler. Goanna
can't (the native combobox is a separate 0x0 display root), so this takes Atlas's
IPC contract (inherited byte-identical) and renders the list in the card:

- Engine: a dropdown-<select> tap reads its options via nsIDOMHTMLSelectElement,
  serializes them, holds the element, and skips the normal click; a 700 ms/same-
  element dedup collapses the raw+gesture double tap.
- Daemon: writes the option JSON to <state>/popup-<id>.json (R8-safe 0644) and
  emits msgPopupMenuShow; the choice returns via asyncCmdPopupMenuSelect ->
  ApplySelectPopup (SetSelectedIndex + input/change; -1 = dismiss).
- Card: the framework BasicWebView ALREADY implements showPopupMenu -> onOpenSelect,
  so we do NOT patch it (an earlier patch shadowed it with a broken this.bubble()).
  Browser.js maps onOpenSelect to a native PopupSelect list and replies with the
  chosen index.

Desktop-proven end to end (build-popup-probe.sh): a <select> tap emits
popupMenuShow with the correct options to the adapter. On device the daemon side
is verified and the popup renders over the plugin, but showed empty under stale
WebAppMgr JS and the card began crash-looping from the day's LunaSysMgr churn —
full state + the reboot-and-retest plan in context/impl/impl-select-popup-2026-08-03.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzkQtdXpngYfZ2yG1XDfkB
2026-08-03 08:46:47 -04:00
NotAlexNoyleandClaude Fable 5 3590b93b46 Apps: rename to Jihad Enyo / Jihad Mochi / Jihad Mojo; start page follows the card
Display names per user direction — launcher titles plus each UI's own branding.
The Enyo start page drops its fixed 1024px box for flex centring, so the brand
block re-centres under the VKB (verified on fb1: card shrunk to 602, block
centred) and in landscape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzkQtdXpngYfZ2yG1XDfkB
2026-08-02 15:32:57 -04:00
NotAlexNoyleandClaude Opus 5 5895f45f34 Device: root-cause stale app JS and the "card never reaches the adapter" bug
Neither was a defect in this project's code.

1. App JS. WebAppMgr caches an app's sources in-process, so editing the file on
   disk and relaunching the card keeps running the old script. killall
   LunaSysMgr (upstart respawns WebAppMgr too) makes new code run on the next
   launch -- no reboot, no version bump, no reinstall.

   This looked already ruled out because earlier killall/reboot attempts had
   "failed". They were run while a palm-install from an earlier turn was STILL
   RUNNING and a second one had been started against the same app id: two
   installers writing the same app directory. Every datapoint from that window
   is untrustworthy. palm-install is also unusable for iteration here (44 MB
   hung twice, no staging file, no ipkg process on device); novacom put of the
   single changed file is ~10 s and md5-verifiable.

   Proven by reproducing the anomaly -- two adjacent statements where only the
   first ever logged -- in three code shapes (long message, plain message in
   try/catch, buffered stamps flushed from a known-good emitter) against a file
   md5-identical to local and provably the only copy in all six ApplicationPath
   scan dirs. After the restart every stamp appeared.

2. NPP_New. The isis UI keeps the browser pane display:none and 0x0 until the
   user navigates, and WebKit does not instantiate a plugin for a hidden
   zero-size element. Launch with a target URL and it flips to 768x942,
   hidden=NONE, openURL=function. The stock adapter behaves identically, so
   this is the UI's design and must not be "fixed".

Adapter paint diagnostics: log the graphicsContext pointer and the computed
blit rects. On device dstBuffer is legitimately NULL because webOS selects the
PGContext path, so "dstBuf=(nil)" alone could not distinguish "took the
PGContext path" from "bailed at the dstBuffer guard" -- the ambiguity that made
a blank card unreadable from this log.

Also documents, in DEVICE-BUILD.md, that the ARM scripts run INSIDE the
container (running them on the host gives a misleading "cross-build libxul
first" even with the ARM libxul present), that the image is jihad-goanna-build
rather than jihad-xtool-build (which has no pkg-config), and that /sysroot must
be arm-sysroot/root -- one level deeper than the directory name suggests.

Screenshots must come from /dev/fb1 (the app layer). fb0 carries the status
bar, app chrome and system alerts, so an fb0 capture shows chrome over a blank
content area and reads as "rendering is broken" when it is fine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDvWTswwcSHQHVA1gD795L
2026-08-02 01:38:08 -04:00
NotAlexNoyleandClaude Opus 5 c43c29ef64 Finding: app JS updates never reach the running card (P1)
Two adjacent statements in JihadEngineOverride.js call the same log() function.
The first appears in palm-log on every launch; the second -- a short canary added
specifically to disambiguate -- never does. The on-device file provably contains
both (grep + md5 match after install), only one file in the app patches the
prototype, depends.js lists it once and first, no rootfs copy shadows it, and no
extractfs entry exists. The card is running a stale parse.

Every method failed to change that: ipkg -o install, version bumps 1.0.0 ->
1.0.1 -> 1.0.2, five killall LunaSysMgr, a full reboot, palm-launch -c then
relaunch, and luna-send to com.palm.appinstaller/installNoVerify -- the service
Preware and WOQI actually use. The installs DO update the payload on disk; the
running card just never picks it up.

This matters twice over. It blocks iterating on app/ at all, and it silently
invalidated part of the card->adapter investigation, which was reasoning about
source the device was not executing. It is also the tenth-plus instance of the
session's recurring pattern: an instrument that stays silent is indistinguishable
from a system that is fine. The canary was added to break exactly that ambiguity
and is what proved this.

The user's requirement -- a LunaSysMgr restart should suffice to install -- is
therefore unmet in a stronger sense than first thought: today not even a reboot
is enough for app JS.

Leads recorded in context/impl/impl-stale-app-js.md, including checking for a
per-app copy under /var/palm/jail/<appid>/, logging a per-build stamp to compare
against disk, and reading the appinstaller response that was never collected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDvWTswwcSHQHVA1gD795L
2026-08-01 15:47:01 -04:00
NotAlexNoyleandClaude Opus 5 7e94b1bcb7 Fix all 8 XUL-input review findings; harness proven able to fail
F-1 (P0) fixed daemon-side rather than in the adapter: MouseEvent records a
tap-shaped down/up pair, ClickAt matches it (<=1000ms, <=24px) and stands down,
so one tap delivers one click sequence again. Chosen over an adapter fix because
no desktop harness runs the adapter (an adapter fix would ship on an argument),
because it does not depend on shouldPassInputEvents() staying what it is, and
because suppressing clickAt wholesale would also silence its hit-test, near-miss
link targeting and VKB classification on exactly the pages where the pen path is
live. Two measurements show nothing is lost: the raw pair alone both checks a
checkbox and navigates an <a href> via the engine's own default action -- which
is why clickAt's direct-navigate had to be suppressed too, or one tap starts two
loads.

F-2/F-3 replace state-inference with delivery detection: a one-shot capture-phase
click listener on the tapped control, so the fallbacks fire only when no click
reached it. defaultPrevented is now observable and logged rather than guessed at.
F-3 also adds the missing GetDisabled -- load-bearing, since a disabled control
gets no click and would otherwise read as a dropped event.

F-5: nine phases now gate rc and the script propagates it. The failure path was
DEMONSTRATED three ways (a real pre-fix defect, deliberately removed gates, and a
null deref), and that demonstration caught a fail-open in the fix itself: rc=384
printed as exit 128 through 8-bit truncation, colliding with the shell's signal
encoding. Tenth fail-open found in this project's verification code. Also fixed:
phase C's default click coordinate was the viewport centre, so D/E could never
observe the button or filter box without a hand-passed JIHAD_XUL_CLICK -- the
"operable" claim had rested on that.

F-4/F-6 reduce the code comments, handoff and kit to what is actually
established: the dropped-event defect is fixed and desktop-proven; the 2026-07-20
SIGSEGV remains unattributed because a discarded event cannot dump core;
keyboard-into-XUL is InsertText only, as KeyEvent is still widget-dropped.

F-7 requires the tap to have caused the focus before adopting a text control --
the first attempt broke phase E, caught by the harness one run later. F-8 tightens
the console bridge to an about: allowlist (the prefix admitted about:blank).

NEW F-9 (P1, not fixed, recorded): asyncCmdMouseEvent dispatches DOM events
synchronously in the YAP socket callback, which is the rule clickAt exists to
obey after a teardown there once rebooted the device. Harmless while events were
discarded; live since T-067 and now the primary tap path on user-scalable=no
pages. Needs the device to fix safely.

F-1 is desktop-verified only, and its device confirmation is BLOCKED (not
pending) behind the separate plugin-instantiation failure: with no adapter
connecting, the wire mouseEvent path cannot be exercised on hardware at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDvWTswwcSHQHVA1gD795L
2026-08-01 10:29:40 -04:00
NotAlexNoyleandClaude Opus 5 1f66f125c9 Kits: about:addons + XPI support as a requirement (and the P0's real cause)
The device SIGSEGV and the add-ons feature turn out to be the same missing
piece. Instrumented the daemon's init and localized the crash in one device
round-trip:

  init: XRE_NotifyProfile (DoStartup: prefs + addons + profile-after-change)
  Segmentation fault                                      EXIT=139

With JIHAD_NO_PROFILE_NOTIFY=1 it reaches "engine up; serving YAP" and binds its
socket. So the crash is inside DoStartup, and the cause is that the daemon
embeds via XRE_InitEmbedding2 with no application.ini and therefore registers no
nsIXULAppInfo at all. AddonManager.jsm:778 does setCharPref(..., appinfo.version)
on a value that does not exist -> NS_ERROR_ILLEGAL_VALUE, and the daemon dies in
the same window. Registering a real app identity is both the crash fix and the
prerequisite for every part of the add-on stack.

New cavekit-addons-extensions.md (6 requirements) + build-site Tier 10
(T-062..T-066). Verified while drafting that nothing needs adding to the engine
build: nsAboutRedirector already maps about:addons, and the manager UI, the XPI
installer UI, locale, skin and AddonManager modules all already ship in the
device bundle. Scope recorded honestly: UXP is a Firefox-52-era fork, so this is
the CLASSIC XPI stack, not WebExtensions; and about:addons is a XUL document,
which collides with this project's recorded XUL input hazard -- called out as
part of R2 rather than left to be discovered on hardware.

Also fixes the record: the addons.manager error is NOT pre-existing noise as I
first triaged it. XRE_NotifyProfile is new in 8953eab, so the 2026-07-27 binary
could not have emitted it -- it is a symptom of the crashing window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDvWTswwcSHQHVA1gD795L
2026-08-01 02:37:18 -04:00
NotAlexNoyleandClaude Opus 5 e36c8cccfd Three independent packages, run in place from cryptofs (R7 + R8)
Enyo, Mochi and Mojo now ship as three fully standalone browsers. Nothing is
co-owned, so nothing needs refcounting: each has its own MIME, adapter shim +
impl, YAP name, socket, upstart job, daemon process and runtime state dir. One
source builds all three -- identity arrives on the compiler command line, and a
half-defined identity is a compile error rather than a silent cross-variant
hijack.

Nothing is written to /media/internal any more. That vfat volume is the user's
USB storage; the engine, daemon and bundled glibc now execute in place from the
app's own cryptofs deviceroot, which is the Atlas model (attributed in NOTICE).
The rootfs footprint is bounded to the three files webOS forces on us and is
reversed exactly by prerm.

Verified on the TouchPad, Enyo variant, 0 failures: installs from a
self-contained .ipk, deploys its own shim/impl/upstart job, serves its own
socket, and runs its daemon out of the app bundle -- with every stock checksum
unchanged and /media/internal clean.

Four defects fixed to get there, each of which would have shipped:
  - ipkg without -o installs into the rootfs, not the app partition
  - the ARM dist's icudt78l.dat is a symlink into the build container, so the
    bundler silently shipped no ICU data and no GRE resources; missing required
    files now fail the build instead of the device
  - ICU_DATA was never set, so libxul aborted in u_init() on every start
  - `| grep -q` under `set -o pipefail` fails on SIGPIPE; the R8 "no
    /media/internal" assertion was failing OPEN

Also fixes all nine findings from the fable adversarial review of the download
lifecycle (context/impl/impl-review-findings-downloads.md), including two P1s:
downloads were addressed to the last-connected card rather than the originating
one, and a failed temp-file setup emitted a start with no terminal, forever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDvWTswwcSHQHVA1gD795L
2026-07-31 23:16:51 -04:00
NotAlexNoyleandClaude Fable 5 76401efecc db8: grant .mochi/.mojo app ids full CRUD on the shared Jihad kinds (F-A01)
A db8 kind's owner must equal the registering app id, so the variants can
never own the shared kinds; explicit per-variant caller grants (not a
trailing wildcard — wildcard-caller matching unverified on this build) in
the Enyo-owned permission files. Mochi db8 features therefore require the
Enyo variant installed; register-db-kinds.sh re-run applies the grant to
an already-registered device. On-device verification pending hardware.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:44:50 -04:00
NotAlexNoyleandClaude Fable 5 478c61650a Enyo start page: centred brand block matching the Mochi variant
Replace the single large logo with a centred block — a smaller (132px) logo, the
'Jihad Browser' name, and 'Enyo UI ★ Goanna/6.9 UXP/b2594a4' — so the Enyo and
Mochi start pages match (logo size + text). The old .startpage-placeholder is kept
(the resize keyboard-repaint workaround references it) but draws nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTDsTcrUkBYzor1aaS9RUG
2026-07-19 23:29:54 -04:00
NotAlexNoyleandClaude Fable 5 216bae1671 Bookmarks: save even when thumbnail generation fails (device fix)
Device: 'adding bookmarks via share menu doesn't show up in bookmarks'. The
bookmark record never reached the db even though the kind + write are fine
(history, same DbService, works). Root: addBookmark called createPageImages()
BEFORE the put — that runs saveViewToFile/generateIconFromFile/resizeImage via
callBrowserAdapter, writing under /var/luna/data/browser/icons (mode d-wxr----t,
owner luna) which the app user can't write; the failure propagated and aborted
addBookmark before bookmarksService.put ran. Wrap createPageImages in try/catch
(addBookmark + showAddtoLauncherDialog) so the essential bookmark always saves;
thumbnails stay best-effort.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTDsTcrUkBYzor1aaS9RUG
2026-07-19 11:33:03 -04:00
NotAlexNoyleandClaude Fable 5 39944f5c45 Fix universalSearch to query Jihad-owned db kinds (completes db-kind migration)
appinfo.json universalSearch dbQuery still pointed at the stock
com.palm.browser{bookmarks,history}:1 kinds — repoint to the Jihad-owned kinds
so launcher/just-type search reaches the app's actual bookmarks + history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTDsTcrUkBYzor1aaS9RUG
2026-07-19 11:14:09 -04:00
NotAlexNoyleandClaude Fable 5 da0152cf4e Fix History/bookmarks/prefs: Jihad-owned db8 kinds (self-contained)
Root cause (device 'History is not working'): the app wrote to the stock kinds
com.palm.browser{history,bookmarks,preferences}:1, which are owned by
com.palm.app.browser and whose permissions grant only com.palm.app.enyo-browser
+ com.palm.launcher — NOT net.riverstonerelay.jihad-browser. Every Jihad db
write was permission-denied, so History/Bookmarks stayed empty.

Fix (matches the self-contained architecture): ship app-OWNED kinds
net.riverstonerelay.jihad-browser.{history,bookmarks,preferences}:1 with
permissions granting the Jihad app id full CRUD (+ launcher read), and repoint
all 8 dbKind references (HistoryList/BookmarkList/URLSearch/BrowserApp). Proven
on device: putKind/putPermissions as the app identity succeed, and a history
put+find round-trips (was errorCode -3963 db:permission-denied before).

A real ipk install registers app/db/kinds via the OS appinstaller; the SDK
palm-install dev shortcut skips that, so build/webos-oe/register-db-kinds.sh
registers them on a dev-installed device (idempotent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTDsTcrUkBYzor1aaS9RUG
2026-07-19 11:12:13 -04:00
NotAlexNoyleandClaude Opus 4.8 1f5866724d Codex gpt-5.6-sol review fixes: F-208/209/210/212
F-208 (P1) / F-209 (P2): remove the app-side key-bridge from
JihadEngineOverride.js. It logged charCode/keyCode (keystroke leak, F-163)
and forwarded keys that the daemon also inserts (double-insert risk). The
webOS VKB does not raise document key events for our swapped-type plugin
anyway, so the bridge was dead code — typing is handled entirely in the
daemon's keyDown. The override now only swaps the plugin MIME.

F-210 (P2): validate the Unicode scalar before UTF-8 encoding in keyDown —
reject the UTF-16 surrogate range and code points above U+10FFFF so we never
emit malformed UTF-8 into the DOM value. (We deliberately do NOT reject
keycodes 0x25-0x28 as "arrows": the VKB delivers the actual character in the
key field, so those are the % & ' ( symbols.)

F-212 (P2): detach the cached shm attachments in freeze(). The production
segments are IPC_RMID-marked, so holding an attach kept them alive and could
exhaust the device's scarce shared memory while the card was backgrounded.
thaw() re-validates and maybePaint() re-attaches on demand.

F-211 (buffer flow-control) left as accepted/known: strict returnBuffer
gating risks deadlocking typing if the adapter doesn't reliably return a
buffer; 2-buffer alternation + fast blit keeps tearing unlikely and the next
keystroke's paint self-corrects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MYQTq14u2BT67nroRijWA
2026-07-14 19:16:07 -04:00