The stall: a leaf-PC sampler in the daemon ($JIHAD_STALL_SAMPLER, GoannaRenderPage.cpp) and
tools/host/stall-resolve.py (the toolchain's addr2line against the unstripped libxul) name what
holds the main thread — the pages' own JavaScript two thirds, compile and the collections the
rest; the clock sat at 192–384 MHz under load (Palm's ondemandtcl, up_threshold 95). The clock
hold (JihadBrowserServer.cpp, jihad.cpu.hold) raises the frequency floor while the engine has
work and releases it after two seconds of idle ticks, on the no-page path too (daemon 18); it
never touches the governor, which wedged the kernel's cpufreq lock and cost a reboot.
H.264 and AAC: build/webos-oe/fetch-ffmpeg.sh + build-ffmpeg-arm.sh cross-build FFmpeg 4.4.5's
decoders (libavcodec.so.58, libavutil.so.56, LGPL v2.1+, decoders and parsers only); the bundle
ships them, NOTICE and licenses/runtime/ carry the terms; the codec page plays both to the end on
the device.
The layout viewport (T-499): GoannaRenderPage::SetLayoutSize and the once-per-page rule in
emitGeometry (jihad.viewport.followOverflow); the engine window resizes but the page still reads
innerWidth 768 — the paint path restores the card size; open.
Rendering options: tools/device/gpu-bench.c times the Adreno 220 through the vendor EGL/GLES2
(composite 4–13 ms, readback 11–14 ms, whole-texture upload 9–12 ms per 3 MB, tile sub-updates
160 ms) beside the CPU equivalents; docs/RENDERING-OPTIONS.md keeps the numbers; the report with
the decision table is the owner's doc. Ledger T-499…T-501, build site, kit, PICKUP, memories.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
The owner: "dont just breeze past that. document and fix issues loading the
modern web. build in an ad and tracker blocker if possible (following license
terms)", then "set up a new round of tests where you browse the modern web as a
pretend user for about 30 minutes … also address any performance issues".
Kits: cavekit-modern-web.md (R1 ledger, R2 slow-script guard, R3 Cloudflare,
R4 GitHub, R5 Wikipedia, R6 a user's half hour) and
cavekit-content-blocking.md (R1–R7); build site Tiers 85 and 86.
docs/MODERN-WEB.md: 19 sites on the device, every row with cause and
disposition; the console method (JIHAD_JS_CONSOLE=all, log windowed by line).
Fixed: dom.max_script_run_time 60 (The Verge renders, the endless script still
asks). Named from the console: GitHub (Turbo is not defined) and Mastodon
(import.meta) are ES-module loading, upstream; Cloudflare's Turnstile runs and
fails on its own verdict, the challenge-host UA rule alone does not pass it.
The blocker: render/goanna/components/jihadContentBlocker.js — EasyList and
EasyPrivacy (third_party/easylist/, snapshots 202610151429, GPLv3+ of the dual
licence, sha256-pinned, bundled raw) compiled on the device in slices (5.9 s,
cached): host, third-party, typed and path rules through nsIContentPolicy,
generic and site-specific hiding rules as one user sheet, 4-day updates from
easylist.to, Settings → Privacy → Content blocking (text and button row types),
NOTICE, CREDITS, about:jihad. Device (Mochi): google-analytics, doubleclick,
ads-twitter, facebook /tr refused; MDN's banner and sidebar advert gone. Open:
memory (+36 MB idle against a 12 MB budget), the updater's tests, the other two
variants after the package push, the inspector's review.
Also: tools/device/browse-round.sh + sites + analyse.py (the pretend-user round),
tools/host/count-blocklist.py, three test pages, the README's new section and
candid Known issues, the Frutiger Aero card-view backdrop in readme-art.py.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
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
The owner's asks of 2026-10-12 and the server agent's work order (PROMPT-PC.md), first pass:
- about:personas opens at once and moves nothing while it loads (the worn Persona's card no longer
flickers): Your Personas (wear, take off, remove with a second tap, Undo in the message at the
bottom, Install from file), the bundled ones not installed, then two previews each from the add-on
site and addons.mozilla.org with a full search of either in place and a link to each website.
addons.mozilla.org's pages show this engine a robot check, so its themes come from its public API;
Install fetches the file from AMO's hosts only (6 MB at most) and converts it as any theme file,
asking first and naming addons.mozilla.org. Installed Personas' pictures resolve even when the
gallery is the first page opened after a start.
- Two Personas come with the browser: Free Palestine — from the CC BY 2.0 photograph "Village of
Battir" by Labour Palestine (Battir's terraces), with the flag and the words drawn over it,
credited on its card, in CREDITS.md and NOTICE — and Aurora Silk. The rest moved to the add-on
site's catalogue (Riverstone-Addons catalogue/); profiles keep the ones they have. Bundle edition 8.
- Each shell's buttons on Jihad's pages (shell.js marks the page from JIHAD_BS_NAME before it is
drawn; shell.css): Enyo's dark pill, Mojo's dark-scene glossy pill, Mochi's flat buttons. The
settings page's tabs wrap instead of hiding About off the right.
- about:addons in Jihad's dark look (addons.css) with the shell's buttons, toolbar buttons and
dropdowns, light text throughout, links as links; its Personas category shows the worn one,
Default look and a link to the gallery (the card cannot scroll its lists); attached reliably when
reached by a link. The site's new Get Add-ons pane is darkened to match (only that pane).
- Momentum: the daemon echoes no scroll position while the card streams them (an echo reset the
coast); Mochi hands flicks to the adapter. Both wait for a finger to confirm.
- Mojo: the lost-connection message floats under the bar instead of moving it.
- Index colours arrive as a JSON string from the site's feed; the push script removes bundled
Personas a new bundle no longer has.
Kits: cavekit-chrome-look.md (new), cavekit-personas-ui.md R8/R9, cavekit-persona-formats.md R4;
build site Tier 78.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
Owner, 2026-10-08: "implement custom 'get personas' repository similar to what other UXP browsers
have", "remove personas we dont have a license to bundle, include by default those we do",
"rename aero personas to be more creatively titled", "shorten the readme to focus on important
features". Kit cavekit-personas-ui.md R7-R9.
- about:personas (packaging/prefsui/content/personas.*; registered with about:settings' module):
the gallery Get Personas now opens (Basilisk's lightweightThemes.getMoreURL can point it
elsewhere). Cards with picture, name, version, author, licence and Wear; an optional online
index (jihad.personas.galleryURL, https, same format) installs through the usual confirm.
The component answers the page on "jihad-personas-gallery" (list / wear / install).
Device: 15 cards in Enyo; Wear on Space Fantasy wears it.
- The bundled set: five drawn for Jihad, renamed Bubble Lagoon, Cloud Nine, Meadow Morning,
Aurora Silk and Lime Fizz (byte-identical art, renamed in place in every profile; one
uninstalled under its old name stays uninstalled), plus ten Firefox-era Personas licensed CC
BY / BY-SA by their authors (import-third-party.py fetches them with pinned sha256; credits in
CREDITS.md, NOTICE and on every card; survey in docs/research/persona-licences.md). Added once
to every profile's list; listed Personas with a bundled picture get its credits. Device: all
three profiles.
- Removed: the Water-Like test assets (All Rights Reserved; the tests use Carbon Light, CC BY-SA),
and every README picture showing an unlicensed Persona (all retaken on the device today).
- README: 333 -> 201 lines; the features that matter, no long-press notes, developer notes cut to
pointers (docs/ENGINE.md, DEVICE-BUILD.md, PICKUP.md); html5test re-measured (447/588).
- Kits: finger checks marked deferred (owner: "Defer additional finger tests for later").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
Owner: "add palefill add-on to all 3 stock browsers, while respecting its
software license."
Palefill (Martok & contributors, MPL-2.0) injects polyfills into sites that
need them. Its v1.30 release file is vendored byte for byte in
third_party/palefill/ with a README recording origin, version, licence and
SHA-256; make-device-bundle.sh refuses to bundle a file with any other hash, so
nothing else can ship under that notice. NOTICE names the authors (and the
JustOff / SeaHOH work it builds on), the licence and the origin, and says the
file is unmodified; every package already carries licenses/MPL-2.0.txt, and an
XPI is the add-on's source form. about:jihad credits it.
How it reaches a user: every variant carries the file in the engine's
bundled-addons/ directory, and components/jihadBundledAddons.js offers it to a
profile ONCE, through the engine's own add-on manager - the path a user opening
the file takes, with the same compatibility check. After that it is the user's
add-on: listed in the add-ons manager, removable there, and not put back once
removed (a later package with a newer version upgrades a copy the user kept).
It would not have been usable as shipped. The engine preferred an add-on's
Firefox-GUID entry over its toolkit entry whenever both were present; Palefill
writes the GUID entry for Basilisk (52.9.*), this engine presents AppCompat
version 56.9, and so the add-on installed and was at once disabled as
incompatible - although its "generic UXP" entry (toolkit 4.8.2 - 10.*, platform
6.9.0 here) describes this application exactly. Rather than alter the add-on,
the engine rule is fixed (third_party/uxp db070f0003): the GUID entry is used
only if it fits or there is no toolkit entry. An unmodified Palefill also keeps
working through its own updates.
push-engine-update.sh pushes bundled-addons/ and the add-on manager's provider
module (a loose file a libxul push does not carry).
On the device, all three variants: installed on first start, active, and (with
its own debug output switched on for one run) "applied fixes to
https://developer.apple.com/: std-customElements". On first install it tries to
open its settings in a browser window this embedding does not have; the engine
logs that one exception and the add-on runs.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcrVQFRMC2ANGDfVaRG9Ed
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>
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
Rotation: the landscape "3× tiling/scanlines" was LunaCE reading the raw plugin
dstBuffer at a fixed ~256px stride (768=3×256 tiled; 1024=4×256 clean). Composite
the offscreen through the device's transform-aware Piranha PGContext instead
(useGraphicsContext=true + PGSurface::wrap + bitblt) — correct in both orientations,
superseding the white-frame guard. Minimal PGContext.h/PGSurface.h ABI headers
(releaseRef is inline on the PGShared base, not a PGSurface symbol — Codex F1).
Verified on-device; the two PG symbols resolve against libWebKitLuna.
Zoom: SetResolution/SetFullZoom never magnified the offscreen RenderDocument capture
(its internal AppUnitsPerDevPixel/AppUnitsPerCSSPixel scale cancels the engine zoom ->
a 1/zoom quadrant = "things get cut off"). JihadRenderDocument now pre-scales the
gfxContext by Z and renders an absolute DOCUMENT rect (RENDER_DOCUMENT_RELATIVE) so
w/Z x h/Z CSS px fill the buffer at Zx and the whole page pans in both axes. Layout
viewport stays device-width (no reflow -> can't re-arm the landscape feedback loop ->
rotation safe); near-1 fit-zoom keeps the engine-scroll path (onscroll/fixed/sticky).
Versioned the extern-C entry point (_v2). UXP change is build/desktop/patches/0013.
Both adversarially reviewed with Codex 5.6-sol; all findings resolved. Detail in
context/impl/{rotation-fix-2026-07-26,zoom-fix-2026-07-27}.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByeKWo8E8f23afU2ZKtfkx
T-051 (R3): add app-mochi/source/JihadWebView.js — the Enyo 2 equivalent of
the Enyo 1.0 BasicWebView. Renders the BrowserAdapter NPAPI <object> with the
self-contained MIME application/x-jihad-browser (matching
app/source/JihadEngineOverride.js) so LunaSysMgr loads BrowserAdapterJihad.so
and the two browsers coexist. Exposes the callBrowserAdapter(method,args) proxy
as the single app-facing adapter surface; registers node.eventListener=this and
wires the shell-relevant adapter->app callbacks (loadStarted/loadProgress/
loadStopped/titleURLChange/urlChange) with arg orders verified against
render/adapter/BrowserAdapter.cpp. openURL/setPageIdentifier go straight to the
node (as BasicWebView does), keeping the app-level callBrowserAdapter method set
frozen. Verified: app-mochi callBrowserAdapter set {findInPage,goBack,goForward,
reloadPage,stopLoad} and palm://com.palm.browserServer/{clearCache,clearCookies}
URIs are byte-identical to the Enyo variant (app/source/Browser.js). No
goanna/uxp/moz identifiers in app-mochi/.
T-052 (R4): rebuild JihadBrowser.js from scaffold into the real shell composed
from Mochi controls — mochi.Header holding a FittableColumns toolbar
(mochi.IconButton back/forward/reload/stop + mochi.InputDecorator/Input address
box, fit:true), mochi.ProgressBar, the JihadWebView region, and mochi.Popup menu
+ dialog scaffolding for the T-053 parity views. Fittable/relative layout, no
hardcoded pixels beyond the shared 1024x768; nav glyphs are inline base64 SVG
data URIs (no bundled image assets). Address bar normalizes input to URL/search;
progress + address reflect load state. New source files registered in
source/package.js so build-mochi-ipk.sh packages them.
T-050 (R5): new app-mochi/ source files carry Apache-2.0 headers; NOTICE gains
Enyo 2 core + layout library and Mochi (LG, Apache-2.0) attribution (codex
F-390). README no longer names the engine (self-contained; grep clean).
Validation: bash build/webos-oe/build-mochi-ipk.sh runs end-to-end OK (1384 KiB
ipk, 398 entries; JihadWebView.js/JihadBrowser.js/.css + updated NOTICE confirmed
in payload). node --check clean on all new JS. Device launch/dual-install remain
DEVICE-GATED (offline) — pending, not faked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTDsTcrUkBYzor1aaS9RUG
Survey of Atlas (WPE), the stock Palm BrowserServer, and the low-spec Goanna
forks (Arctic Fox, Mypal68), applied to the T1-T5 device-test failures:
- Engine-driven repaint (T3 stale-frame root, UXP patch 0012): sticky
PuppetWidget::Invalidate() dirty flag + jihad_offscreen_take_dirty export;
the daemon drains it each tick after PumpFor and repaints on dirty. This is
the frame-delivery loop the stock QtWebKit server got from Qt paint events
(BrowserPage::paintEvent -> flushBuffer -> msgPainted) — without it JS/SPA
DOM updates, async image decode, and incremental render never reached the
shared buffer ("old page sticks around until you tap or drag"). Weak-linked
so the daemon still runs against a pre-0012 libxul. Progress increases also
repaint so pages visibly build up during load.
- Low-memory guardrail (Palm memchute watcher + Atlas memory-budget lesson):
EngineHost::CheckMemoryPressure — rate-limited /proc/meminfo poll from the
pump; below JIHAD_MEM_LOW_KB (default 48 MB) fires the engine
"memory-pressure" observer flush (JS GC/CC, image discard, caches) +
malloc_trim(0), throttled 30 s against GC thrash. 512 MB Pre 3 floor.
- Cookie/disk-cache persistence (Atlas consent-bounce lesson): profile dir
provider (ProfD/ProfLD -> $JIHAD_PROFILE_DIR, default <greDir>/../profile)
passed to XRE_InitEmbedding2 so cookies.sqlite + disk cache survive daemon
restarts; consent/login cookies no longer die with the process.
- goanna.js low-RAM prefs (Arctic Fox ships high_water_mark=128/slice=10,
Mypal68 slice=5; scaled for 512 MB): js heap high_water_mark=32, 5 ms GC
slices, slow heap growth, 32 MB media cache, fewer http connections, no
prefetch, bounded 64 MB disk cache on /media/internal.
NOTICE credits Atlas (Apache-2.0, Herrie82), the Palm BrowserServer memchute
pattern, and Arctic Fox/Mypal68 (MPL-2.0). Kits updated (offscreen R3 fix
note, browser-services R2 persistence + guardrail).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MYQTq14u2BT67nroRijWA