Skip to content

App Studio on the phone (ADR 0006 milestone 1): #313 rebased, reviewed, re-validated - #477

Merged
ymote merged 20 commits into
mainfrom
studio/313-rebase
Oct 11, 2026
Merged

ymote merged 20 commits into
mainfrom
studio/313-rebase

Conversation

@ymote

@ymote ymote commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

#313, the developer App Studio with on-device app testing (ADR 0006 milestone 1), brought up to today's main, reviewed twice, fixed and re-validated on a second phone. This PR supersedes #313 and keeps its four commits; the product owner assigned ADR 0006 to this session on 10 October 2026.

What #313 adds, unchanged: the in-process renderer and the studio.* host tools for developer builds; scoped bundle admission and local developer installs under dev.studio.*; --dev-mode and --package-name in the Home build script; the OnePlus 6 validation report and evidence.

What this PR adds on top:

  • Merges of main (3bd15ba; 5c28da7 after Open a script app at its manifest's window size; PDF Tools asks for 1536 x 1024 #478; then 3a56305, after rom tests: IME harness stubs follow the pinned makepad #483, feat: add OctoSense updater with stable, RC and preview channels #446, Support WASI TCP, UDP and DNS with bounded cancellable resources #484, PDF Tools v2 follow-ups: local comment dates, Edit alignment and colour, real storage line, kept covers, Combine's footer #479, The dock hides while OctoSense is full screen, as macOS's Dock does #485, deps: pdfcraft 615ff308, robustness fixes for malformed comment dates and page labels #494 and Maximize and snaps end above a floating dock; honour a native window's requested size #487): fourteen conflicts in nine files the first time, both sides kept in the shell sources; the studio tools join the system chat's declarations before session_set applies the cap; build-home.py keeps --dev-mode and --package-name with main's version-name, pending-pair verification and receipt. The later merges were clean apart from one line in build-home.py's receipt, where both the developer-options fields and the updater's release tag are kept; a studio row takes the default window size from launch_module_as and has no card_app, since it has no manifest.
  • ROM test test_receipt_requires_the_requested_apk_identity rewritten around main's verify_pair.
  • First review (independent, 40-file diff against main's declarative runtime): studio isolates fail closed on device consent and carry no host identity, and the vacuous may_run checks are gone; a preview's host.request is refused instead of left in the shared bridge queue; admission refuses WebAssembly files and shared components; receipts whose grant ended are reaped before the 16-install cap is counted; studio.open is an act tool; an isolate's service state is released when a preview or app closes.
  • Second review (by the OctoSense maintainer session), all findings addressed:
    • Development builds only. cfg(dev_mode) gates the tool names and declarations, SUPPORTED, the linked module, the launcher rows, the signal drain, the draw and event hooks and both launch fixtures. A release Home answers no studio.* call whatever it is launched with.
    • Owned app data and studio.uninstall. An installed app's data folder carries an owner record beside it; a folder left by another conversation's install is wiped when the id is claimed; a receipt from before owner records adopts its folder. studio.uninstall {app_id} (act) removes the caller's own receipt, snapshot, data and owner record, and is refused while an instance is open.
    • UI-thread locks. The studio queue tick and the renderer's event hook run only on signal, timer, frame, geometry and foreground events; grant checks run after the queue lock is released; grants are cached per developer-mode generation, read without the lock through a mirror every access stores; the launcher's installed list is cached and reused for launches.
    • Lows. Launch fixtures are accepted only from the host-owned studio-fixture folder (the package's private files/ on Android), judged before anything is created; a preview folder goes with its spec and stale previews are swept once per process; admission reads {{assets}} from the file as written and refuses a URL in main.splash, since the isolate withholds only the net module; the app's name is at most 64 characters; build-home.py refuses --dev-mode under the installed Home's identity without --development, and the Bridge's identity; the docs name the new tool, the offline rule, the fixtures folder and the device evidence, and lose a duplicate heading.
  • Re-validation on a stock Xiaomi (Android 15), docs/studio/xiaomi-revalidation.md, on the APK built from the merged head: the L0 render probe passed all four cases; a fresh Task Planner authored by Claude Fable 5.1 from the brief passed the acceptance flow, 134 tool calls all answered (developer install, process restart, Chinese input, scrolling), visual review passed; a second spool-driven check covered the owner record beside an installed app's data, studio.uninstall (refused while open, then ok) and an ended developer grant (studio.open refused with "developer grant expired", reinstall ok). The acceptance harness now begins by removing a previous developer install of the app, since the same owner's reinstall keeps its data.

The maintainer session re-reviewed the batch and gave its code-review OK; its two pre-merge doc notes are in (the developer build is standalone only, since the build script refuses --dev-mode for the ROM variant and under the installed Home's identity without --development; the admission scan for a URL or {{assets}} is a lint, the offline guarantee being the isolate with no network hosts and no host capabilities).

Follow-ups for the next PR

Tracked in #480. From that re-review, all in developer builds only: cache grant validity in the render path (Renderer::event still asks developer mode per signal, timer and frame event during a render); refresh the installed list when installs change rather than on a two-second timer, and off the UI thread; move the owner-record write out of open_bundle; tests that go through install() and cover "uninstall refused while open"; owner records under their own folder, since dev.studio.x.owner.json is itself a valid app id; a lock shared by open and uninstall.

Checks

  • On the final head 44f391c: cargo test --locked -p octosense-shell 1009 passed; from phone/, --features mobile-apps,dev-mode 1229 passed, 35 of them studio tests including the three new bundle-store tests; cargo test --locked -p octosense-app-peers 22 passed.
  • ROM tests (JDK 17): test_home_build 33/33 on the final head; the full rom/tests run, 140 tests, passes since rom tests: IME harness stubs follow the pinned makepad #483 fixed the IME harness stubs on main.
  • Device: Xiaomi 23021RAAEG, the separate test package only, Home and Bridge untouched; see the write-up.
  • Local CI gate, two runs on the final head because rom/scripts/build-home.py changes: on 44f391c, run A (--linux-host) 53 passed, 8 skipped, 0 failed, the skips being the ROM steps that need node, a JDK and the Android SDK, absent on that host; run B (Mac-only --only all) 59 passed, 0 failed, 0 skipped. Both last.json files are bound to this head.

Not verified

Fault injection (malformed tasks.json, failed save); landscape; an app-peer caller end to end; the ROM capture path; a release build of Home on a phone; revocation of a running app on the device (a developer-profile grant never expires, and only the Settings and banner gestures end it; the unit tests cover that path).

🤖 Generated with Claude Code

ymote and others added 10 commits October 2, 2026 21:14
Resolves the fourteen conflicts with main at 3bd15ba: both sides kept in
glance.rs (studio card preparation beside the generated-Mail checks),
host_tools/mod.rs (the studio modules beside admission), host_tools/tests.rs
(the studio tests beside the engines' tests) and lib.rs (the studio module
and its draw hook beside glance_style and the audio session hook); the
system chat's declarations add the studio tools to the agents' set before
session_set applies the cap; build-home.py keeps --dev-mode and
--package-name with main's version-name, pending-pair verification and
receipt, Home's expected package taken from --package-name; the
architecture walkthroughs keep main's UI-thread note before the studio
section; ADR 0006 keeps the branch's status line and main's wording of the
consequences, with the branch's disposable-storage detail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main replaced home_version with apk_metadata and verify_pair; the merged
build-home.py takes Home's expected package from --package-name, and the
test now checks that through verify_pair: a test package passes with its own
identity and is refused under the default one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
prepare_studio in glance.rs and the studio app-tools test in
host_tools/tests.rs ended inside their conflict hunks; keeping both sides
left them without their closing braces.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Findings of the independent review of #313 against main's runtime, where
capabilities, hosts and budgets are declarations:

- Studio isolates fail closed on device consent and carry no host identity:
  set_device_consent(true) and set_host_tag(None) before set_text, as a
  seated contained app does in glance_card::seat. The may_run budget checks,
  vacuous at this makepad, are gone; the jail, quota and memory cap are what
  is enforced.
- A preview's host.request is refused, not left in the bridge queue: every
  event (and every studio draw) drains take_splash_host_requests_for and
  answers with a fixed refusal, so a script sees an error instead of a hang
  and other apps keep their request slots.
- Admission refuses WebAssembly: a .wasm file while the bundle is copied and
  a manifest that pins shared components; tested.
- Developer-install receipts whose grant ended are reaped with their
  snapshots before the 16-install cap is counted; tested.
- The studio-render and studio-flow launch fixtures accept only a workspace
  inside this home's directory, since the intent extra that names them is
  readable by any app that can start Home; the flow spool logs when it stops.
- studio.open is an act tool.
- Docs: the enforcement claims match the code (net module absent, host
  requests refused, budget declared, jail/quota/memory cap enforced); the
  acceptance-contract SHA-256 in the validation report is corrected.

Tests: desktop 984, phone mobile-apps 1196 (32 studio), ROM test_home_build 32.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ory on Android

The first device probe on a stock Android 15 phone showed the fixture
workspace beside the OctoSense home (files/studio-fixture, not
files/.octosense). The confinement now allows the package's private files
directory on Android, the home itself elsewhere; the spool still never reaches
another root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The L0 render probe passed its four cases and a fresh Task Planner, authored
by Claude Fable 5.1 from the brief, passed the acceptance flow on a Xiaomi
23021RAAEG (Android 15) in the separate developer test package: 129 tool
calls all answered, developer install, process restart, Chinese input,
scrolling; visual review done. Three captures and the hashes that bind the
run; the ADR's status line names both devices.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ymote and others added 6 commits October 10, 2026 19:36
As glance_card does for a tile: answer the heap's queued host requests with
the refusal, drop its pending taps and cancel its service calls through App
Hub's cancel_heap, so the services' on_isolate_closed listeners hear it. Done
before the isolate's text is cleared, on close, on a finished render and on a
failed one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-thread locks

Second review batch for the rebased App Studio (ADR 0006 milestone 1).

Release builds have no studio: `cfg(dev_mode)` (a debug build, or the
`dev-mode` feature) now gates the tool names and declarations, SUPPORTED,
the linked module, the launcher rows, the signal drain, the draw and event
hooks and both launch fixtures, so a release Home launched with any flag
answers no `studio.*` call.

An installed app's data folder belongs to the conversation that installed
it: the owner is recorded beside it (`data/<id>.owner.json`), a folder left
by another owner's install is wiped when the id is claimed, and a receipt
from before owner records adopts its folder. `studio.uninstall {app_id}`
(act) removes the caller's own receipt, snapshot, data and owner record,
refused while an instance is open.

The UI thread takes fewer locks: `studio::apps::tick` and the renderer's
event hook run only on the signal, timer, frame, geometry and foreground
events; the grant checks run after the queue lock is released; grants are
cached per developer-mode generation, read without the lock through
`dev_mode::generation_relaxed()` (a mirror stored by every access); the
launcher's installed list is cached and reused for launches.

Lows: launch fixtures (`studio-render:`, `studio-flow:`) are accepted only
from the host-owned `studio-fixture` folder (the package's private `files/`
on Android), judged before anything is created; a preview folder goes with
its spec and stale previews are swept once per process; admission reads
`{{assets}}` from the file as written and refuses a URL in `main.splash`,
since the isolate withholds only the `net` module; the app's name is at
most 64 characters; `build-home.py` refuses `--dev-mode` under the
installed Home's identity without `--development`, and the Bridge's
identity; the device flow tool requires the fixtures folder; the docs
name the new tool, the offline rule, the fixtures folder and the Xiaomi
re-validation, and the walkthroughs lose a duplicate heading.

Tests: three new bundle-store tests (owner claim, uninstall, offline
refusals), the own-caller test covers `studio.uninstall`, and a Home build
test for the identity rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…error, Xiaomi evidence of the merged head

The acceptance harness begins by removing a previous developer install of
the app under test with `studio.uninstall`, since the same owner's reinstall
keeps its data and the flow expects a fresh install; a missing receipt is
reported as "no developer install with this id" instead of the raw I/O
error.

The Xiaomi report now covers the merged head: the L0 render probe (four
cases), the acceptance flow (134 tool calls, visual review passed) and a
second spool-driven check of the owner record beside an installed app's
data, `studio.uninstall` and an ended developer grant. The three captures
are from that run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
English and Chinese replacements from an independent language review of the
sentences this branch added: the READMEs' device-run summary, the ADR's
owner-record and tool-risk wording, the Xiaomi report's phrasing, the
walkthrough's offline rule and re-validation sentence, and the phone
README's fixtures sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The ADR, the ROM README (both languages) and the Home build contract still
described a platform-signed developer build on the ROM; `build-home.py` now
refuses `--dev-mode` for the ROM variant and under the installed Home's
identity without `--development`, so a developer build is always a
standalone APK under the development keystore or a test package.

The admission scan of `main.splash` for a URL or the `{{assets}}` route is
described as the lint it is: Splash builds strings at runtime, and the
offline guarantee is the isolate itself, which runs with no network hosts
and no host capabilities. The code comment says the same.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replacements from the independent language review of the sentences added
for the standalone-only developer build and the admission lint: the ADR's
build-script sentence, the Home build contract's subject, a consistent
apostrophe in the ROM README, and the Chinese wording of "identity" and of
the isolate's missing network hosts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ymote

ymote commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Local CI passed on 44f391c (tools/ci-local.sh --only all, 19m06s, Darwin arm64).

GitHub would run for this PR: desktop.yml, phone.yml, apps.yml, rom.yml. Merged on this local pass (macOS runner queue); GitHub CI runs on the merge commit on main.

Job Step Result Time
ci-local / drift The local mapping fits the workflows PASS -
desktop.yml / desktop Prepare the pinned framework sources PASS 0s
desktop.yml / desktop Compile the desktop with the shared runtime PASS 5m40s
desktop.yml / desktop One copy of every shell source PASS -
desktop.yml / desktop Desktop tools and scripts PASS 16s
desktop.yml / desktop In-app updater and installer handoff PASS 6s
desktop.yml / desktop One Makepad, App Hub, octos and Rinx in the locked graph PASS 3s
desktop.yml / desktop The native apps' generated places match native-apps.json PASS 0s
desktop.yml / native-host-api Prepare the pinned framework sources PASS 0s
desktop.yml / native-host-api Build the native acceptance host and pinned Hub CLI PASS 11s
desktop.yml / native-host-api Signed app tool calls a real OS permission API PASS 3s
phone.yml / home Prepare pinned sources and the reviewed runtime patch PASS 0s
phone.yml / home Validate launcher icons and Apple asset catalogs PASS 3s
phone.yml / home Compile Home and its bundled modules PASS 3m11s
phone.yml / home Verify the single runtime graph PASS 2s
phone.yml / home The native apps' generated places match native-apps.json PASS 0s
phone.yml / home The pinned octos revision PASS 0s
phone.yml / home Build octos (unless cached) PASS -
phone.yml / home Build Home, App Hub admission and runtime policy tests PASS 42s
phone.yml / home Test Home, App Hub admission and runtime policy PASS 39s
phone.yml / home Two-lane scenario (real kernel, scripted model) PASS 32s
phone.yml / home Build opt-in AppCard tests PASS 36s
phone.yml / home Test opt-in AppCard PASS 7s
phone.yml / home Build craft-engine relay and system-chat tests PASS 44s
phone.yml / home Test craft-engine relay and system chat PASS 33s
phone.yml / home Build linked Terminal tests PASS 34s
phone.yml / home Test linked Terminal PASS 5s
phone.yml / home Build Wasm and runtime policy tests PASS 7s
phone.yml / home Test Wasm and runtime policy PASS 15s
apps.yml / services python3 tools/setup.py PASS 1s
apps.yml / services Shared system app interface PASS 0s
apps.yml / services Kernel service PASS 12s
apps.yml / services OctoSense release updater PASS 9s
apps.yml / services App peers PASS 23s
apps.yml / services AI providers config PASS 2s
apps.yml / services Connected provider protocols and Linux host adapter PASS 8s
apps.yml / services L0 in-card chat (sys.chat, host side) PASS 3s
apps.yml / services System toolbox (workflow templates) PASS 1m12s
apps.yml / kernel-security python3 tools/setup.py PASS 0s
apps.yml / kernel-security The pinned octos revision PASS 0s
apps.yml / kernel-security Build octos (unless cached) PASS -
apps.yml / kernel-security Real-kernel tests PASS 28s
apps.yml / apps python3 tools/setup.py PASS 0s
apps.yml / apps Host services (Mail, AI providers, News) PASS 22s
apps.yml / apps Connected accounts and Rinx editor PASS 5s
apps.yml / apps Shell AI services (crates/ai-host) PASS 14s
apps.yml / apps AppCard runtime setup safeguards PASS 0s
apps.yml / apps AppCard clippy (own crates; shared runtime compiles) PASS 2s
apps.yml / apps AppCard transport and store PASS 6s
apps.yml / apps One Makepad, App Hub, octos and Rinx in the locked graph PASS 4s
rom.yml / product python3 -m unittest discover -s tests -v PASS 49s
rom.yml / product Check generated Agent Binder client PASS 0s
rom.yml / product bash -n scripts/stage-forks.sh scripts/publish-release.sh scripts/apply-to-tree.sh scripts/build-rom.sh PASS -
rom.yml / web-installer npm ci --ignore-scripts --no-audit --no-fund PASS 5s
rom.yml / web-installer npm test PASS 1s
rom.yml / web-installer npx playwright install --with-deps chromium PASS 2s
rom.yml / web-installer npm run test:browser PASS 4s
rom.yml / web-installer python3 -m unittest discover -s tests -p test_installer_manifest.py -v PASS 0s
rom.yml / web-installer bash -n scripts/release.sh PASS 0s

Notes:

  • GitHub runs Python 3.12; this run used Python 3.9.6
  • apps.yml:services runs on ubuntu-latest in GitHub; here on macOS, so #[cfg(target_os = "linux")] code in it is not exercised (--linux-host runs it on Linux)
  • apps.yml:kernel-security runs on ubuntu-latest in GitHub; here on macOS, so #[cfg(target_os = "linux")] code in it is not exercised (--linux-host runs it on Linux)

@ymote
ymote merged commit b4323bd into main Oct 11, 2026
7 of 8 checks passed
@ymote

ymote commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Run 1 of the two runs this PR needs (it touches rom/), on the same head 44f391c: tools/ci-local.sh --only all --linux-host, PASSED in 38m32s.

  • On the Linux build host: apps.yml services and kernel-security, and the Linux process sandbox (Landlock, seccomp): PASS.
  • rom.yml's Linux steps were skipped there as expected (the host has no JDK, Android SDK or node). The macOS run above (run 2) ran rom.yml in full: 9 of 9 steps passed.

Both runs were made by the App Studio session on this exact head. The merge was approved by the maintainer.

@ymote
ymote deleted the studio/313-rebase branch October 11, 2026 16:54
chrislearn pushed a commit to chrislearn/OctoSense that referenced this pull request Oct 12, 2026
Home's main activity is exported, so any installed app can start it with
intent extras, and Makepad copies `makepad.<NAME>` extras into the
environment. OctoSense code read several of them in release builds.

- shell: `launch_input` is the one reader of MAKEPAD_APP_CONFIG. Its test
  actions, Mail's demo mailbox and module open parameters apply only in a
  development build (cfg(dev_mode): debug or --features dev-mode); a
  release build ignores them and logs that once. Desktop --test-action
  command-line flags are unchanged.
- kernel: the launch proxy (makepad.OCTOS_PROXY) applies only in a
  development build; new `dev-mode` feature, forwarded from the shell
  through ai-host.
- AppCard: every MAKEPAD_* hook (provisioning, seeding, auto prompt, fake
  GPS, proxy, dumps) goes through `launch_var`, development builds only;
  new `dev-mode` feature, forwarded from the shell through the module.
- OpenHarmony: EntryAbility forwards no launch parameters unless
  build-home-ohos.py --dev-mode stages it (a separate bundle id required).
- Android: Home's `dev-mode` feature comes from build-home.py's
  `--dev-mode` (App Studio's, OctoSense-org#477): standalone only, never the installed
  Home's identity under its signer, and ROM staging refuses it. So a build
  that reads launch input is never the installed Home.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant