Repository navigation
Terminal as a system app and native-apps.json: one manifest, hosting per target (ADR 0004 steps 1–2, §10) - #113
Merged
Merged
Conversation
ymote
force-pushed
the
feat/terminal-system-app
branch
from
September 28, 2026 21:37
e7ef0ca to
5b458a8
Compare
ymote
force-pushed
the
adr0004/manifest
branch
from
September 28, 2026 21:38
f6e4650 to
a0260ba
Compare
… draws A focus that cannot land at launch (the tile is not drawn yet) is kept in `pending_focus` and re-asserted when the client's first frame arrives, on the PresentableDraw path. A module tile never sends a frame, so its pending focus was never retried: ModuleView stayed unfocused, its gate dropped every key, and a module opened from the dock or launcher took no typing until the person clicked into it. After the UI handles a Draw, retry a pending focus that belongs to a module client. On success it clears; otherwise nothing forces a redraw, so a tile that still cannot take it just tries again on the next natural draw. Found with the Terminal module: typed keys never reached its root (a probe in its key path logged nothing) until the tile was clicked; with this change `echo` typed right after opening it from the dock ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Terminal was a developer program: built on first launch from the Makepad checkout, then run as a child process. It now links in-process like Rinx: the new `app-terminal` feature (on in the desktop's default set) registers makepad-terminal's TERMINAL_MODULE, and `terminal` joins robrix and finance as a module hosted in-process by default unless the person switched it (`terminal: Process` in wm/apps.splash keeps the process form, whose config/apps.json row stays). - Opens instantly: no compile on first launch, no swapchain round trip. - On macOS its PTY helper is `octosense` itself (Cx::pre_start answers `--exec-pty`), so nothing ships beside the binary. - It declares `process` and `clipboard` and no `octos.*` service. The assistant gets read_screen and read_scrollback only; `run` (an unsandboxed shell, rated Risk::Act) is not offered by a default system app. - Phone builds are unchanged: `mobile-apps` does not include it. Builds on the current makepad pin (75e3e24d), which already carries TERMINAL_MODULE, the PTY-helper fallback and the macOS signal-wake fix (OctoSense-org/makepad #40, #41); Cargo.toml pins makepad-terminal there. Tabs, panes, settings and terminal-ctl (makepad #47) arrive with a later pin bump. Verified locally with those makepad commits applied under .sources/makepad: - cargo check --locked -p octosense (and --features mobile-apps) - bash tools/check-shell-graph.sh -p octosense - cargo test --locked -p octosense-shell --features app-terminal: 333 passed - from phone/: cargo test --locked --features mobile-apps -p octosense-shell: 342 passed - hidden desktop over MAKEPAD_REMOTE: Terminal opens from the dock as "in-process, terminal"; the shell's parent is octosense; octoscode's composer stayed clean on 3 of 3 launches; closing the tile ended zsh and octoscode and freed the isolate; two Terminals ran two shells. Rebased onto main: cargo build --locked -p octosense and tools/check-shell-graph.sh pass against the pinned makepad with the reviewed runtime patches; cargo test -p octosense-shell --features app-terminal: 345 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…us change Focusing a process tile moves key focus to it (its forwarding needs that), but a module tile deliberately never moves key focus: its own widgets claim it. Nothing told the previously focused tile to let go, so after switching between the tabs of a window group the hidden tab kept the keyboard and every key typed into the visible one was dropped. Track the client whose tile last took the keyboard (`keyboard_holder`); in `focus_client`, release that tile (TileHost::release_keyboard) before focusing another. The layout already reports the new client as focused by then, so the previous one is tracked here rather than read from it. Needs the matching makepad-terminal change (claim the keyboard on a key that arrives while nobody holds it, OctoSense-org/makepad#40). Verified: Omarchy style, a two-tab Terminal group; after Cmd+Alt+Tab text typed runs in the tab now shown, both directions. cargo test --locked -p octosense-shell --features app-terminal: 333 passed; from phone/: --features mobile-apps: 342 passed; cargo check --locked -p octosense. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0004 §1 (#110): the script apps are the only versions. The native crates were kept "for comparison" behind `app-news`, `app-photos` and `app-maps`; nothing shipped them. - apps/{news,maps,photos}/native, their workspace members and pins, the shell's optional deps, `app-*` features and place in `mobile-apps`, the desktop and phone forwards, and their linked_modules() lines. The makepad-map-nav pin and patch go with Maps (nothing else used it). - Photos' sample library, which Home mounts into the script app, moves to apps/photos/resources/ with its provenance (SOURCES.md, generated-scenes.json); phone/system-apps.json points there. The native crate's catalog and icons go. - The phone catalogs' native News/Maps/Photos rows, phone.yml's builds and tests of the three crates, phone/docs/{photos,maps}.md, and the README and ADR mentions (both languages). Cargo.lock updated by cargo metadata (four packages removed). Verified: cargo check --locked -p octosense; from phone/: cargo check --locked -p octosense-home --features mobile-apps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0004 §1. Adding a native app took five hand edits that nothing
checked (the root pin, the shell's optional dependency and `app-<id>`
feature, the package defaults, a #[cfg] line in linked_modules(), and
Cargo.lock). native-apps.json at the repository root now declares every
native app the shell links: Rinx, Reference, Sheets, Terminal, AppCard and
App Hub, each with its crate and source {git, rev, local} or {path}, its
module path(s), bin, crate features, hosting per target, the shells that
link it, whether native mobile links it without flags, and its sandbox,
storage and agent grants.
tools/native_apps.py (stdlib only) owns what follows from it, between
`# BEGIN native-apps: <block>` / `# END` markers:
- Cargo.toml: the app crates' pins, and the [patch] entries that resolve
a git source to its `local` checkout;
- crates/shell/Cargo.toml: the optional deps, the native-mobile deps, the
`app-<id>` features and `mobile-apps`;
- desktop/ and phone/Cargo.toml: `default`, `mobile-apps` and the forwards
(turning on the package's own optional dep where it has one);
- crates/shell/src/native_apps.rs, whole: `link()`, which linked_modules()
now calls, and the per-target hosting table the shell will read.
`--check` exits 1 on drift. It refuses `process` on android, ios, ohos or
wasm, `process` without a `bin`, and a second revision of a repository an
app comes from. A write updates Cargo.lock only through cargo (`cargo
update -p` for a changed pin, then `cargo metadata`).
Behaviour-neutral: the generated TOML equals the previous hand edits
(compared as parsed TOML), Cargo.lock is unchanged, and link order is the
same. Hosting is unchanged in this commit. A shell test checks every
linked native module is a manifest entry and that Policy::shipped() grants
exactly the manifest's `agent.octos` (Rinx's four octos.* services).
CI: desktop.yml and phone.yml run `native_apps.py --check` (and trigger
on native-apps.json); desktop.yml's unittest discovery runs
tools/test_native_apps.py. AGENTS.md and the READMEs point at the manifest.
Verified: python3 tools/native_apps.py --check; python3 -m unittest
tools/test_native_apps.py: 16 passed; cargo check --locked -p
octosense-shell --features app-terminal --tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… process on macOS and Windows ADR 0004 §2 and §10 (#110). A linked native app the person has not switched is now hosted as its manifest entry declares for this target (native_apps.rs, `NativeApp::on_this_target`), replacing the hard-coded "robrix | finance | terminal are modules" rule (robrix and finance are not linked by any build): - Terminal: its own process on macOS and Windows; on Linux only with a Vulkan build (`MAKEPAD=vulkan`, new `makepad_vulkan` cfg in the shell's build.rs) in a Wayland session; in-process on phones. A crash in it no longer takes the shell down. - A declared process falls back to in-process where it has no process form (`apps::process_form`: its launcher row resolves neither to a checkout to `cargo run` from nor to a sibling binary). - App Hub, Rinx and AppCard are modules everywhere; Reference and Sheets keep their process default on the desktop. - `terminal: Module|Process` (and any `<id>: ...`) in wm/apps.splash and `--module <id>` still win. The process is found the way every catalog app is (clients.rs): out of a checkout, `cargo run --release --manifest-path .sources/makepad/Cargo.toml -p makepad-terminal --bin terminal --target-dir <home>/build/makepad`; otherwise the `terminal` binary beside `octosense`. Release packages do not ship that binary yet: that depends on OctoSense #94; until then a packaged build falls back to the in-process Terminal. The standalone terminal binary publishes `run` on the AI services bus. The bus now withholds it (ai_bus::WITHHELD_TOOLS, keyed by the app id the shell launched the client as): stripped from the registration and replay, and a call to it is dropped, so the assistant gets read_screen and read_scrollback only in either hosting. Behaviour changes: Terminal opens as a process on macOS/Windows; Rinx (default) and AppCard (opt-in) now open in-process without `--module` (they have no process form, so before they were not launchable on the desktop without the flag). Verified on macOS: cargo build --locked -p octosense; tools/check-shell-graph.sh -p octosense and -p octosense-home; cargo test --locked -p octosense-shell --features app-terminal: 348 passed; from phone/: cargo check --locked -p octosense-home --features mobile-apps, cargo test --locked --features mobile-apps -p octosense-shell: 358 passed; native_apps.py --check; tools unittests. Hidden desktop (MAKEPAD_HIDE_WINDOWS, MAKEPAD_REMOTE, scratch OCTOSENSE_HOME, MAKEPAD_WM_TEST_APP=terminal): "launched terminal as client 1" (a process, not "in-process"), cargo built the `terminal` bin into the scratch home, first frame drawn in the tile, zsh running under it, typed text reached it; /quit ended both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… home directory - launch_argv passes `--locked` to `cargo run`, so launching a process app from a checkout never rewrites that checkout's Cargo.lock (the pinned Makepad under .sources/ is shared and stays as prepared); a stale lock now fails the launch visibly. The current .sources/makepad lock satisfies it. - A Terminal with no focused terminal's directory to inherit (and no demo home) opens in the person's home (HOME; USERPROFILE on Windows) through the terminal's own `--cwd`, instead of wherever the launch ran: the process Terminal used to start its shell in the catalog row's checkout, .sources/makepad. Warm instances get the same directory. Verified: cargo test --locked -p octosense-shell --features app-terminal: 348 passed; from phone/ --features mobile-apps: 358 passed; cargo build --locked -p octosense; check-shell-graph.sh for both packages; native_apps.py --check; tools unittests. Hidden desktop, scratch home: the Terminal process ran `terminal --stdin-loop --cwd $HOME`, `pwd` printed /Users/ychen, text rendered normally, .sources/makepad/Cargo.lock unchanged; /quit ended both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… confirmation
Accepted ADR 0004 §10: the Terminal's AI may read and type commands in
every hosting; each command goes through the shell with a live approval
showing the exact command, `auto_approvable: false`. This replaces the
earlier stripping of the process Terminal's `run`.
- native-apps.json: `agent.tool_policy` ({tool: {confirm: host|app,
auto_approvable}}, §8); the Terminal's `run` is `confirm: host`,
`auto_approvable: false`. The generator validates it and emits
native_apps::{Confirm, ToolPolicy} and NativeApp::tool().
- ai_bus: for a `confirm: host` tool (process clients by the app id the
WM launched them as, in-process modules by their id) the registration
keeps the tool, forced to Risk::Destructive with no self-confirm claim,
so the pane parks every call on its live confirm card; nothing on the
bus approves for the person (there are no standing rules, and
auto_approvable: false keeps it so). The card shows the arguments cut
at 60 bytes, so a call whose arguments it cannot show in full is
refused back to the pane and never reaches the terminal.
- module_host: the in-process Terminal's module answers the reads only;
the shell now runs its own executor for it (makepad-terminal's full
manifest and ai::answer on the live MpTerm), so both hostings offer
the same tools under the same confirmation.
- desktop README (both languages).
Verified: cargo test --locked -p octosense-shell --features app-terminal:
349 passed; from phone/ --features mobile-apps: 358 passed; cargo build
--locked -p octosense; check-shell-graph.sh for both packages;
native_apps.py --check; tools unittests: 46 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ral [skip ci] The new-app test hard-coded Makepad 75e3e24d and broke when main moved the pin to f08eec53. It now reads the revision from the Sheets entry it copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ymote
force-pushed
the
adr0004/manifest
branch
from
September 28, 2026 21:56
a0260ba to
bebed14
Compare
ymote
added a commit
that referenced
this pull request
Sep 29, 2026
… [skip ci]
- approvals/contacts.rs: ContactsSource (is_known for email addresses and
Matrix IDs), ContactList/NoContacts test doubles, MailContacts reading
Mail's host service data, and ContactsGate: off by default, on only by
the person's Settings tap ("Use my contacts in approval rules"), kept
owner-only in approvals/contacts.json. The router evaluates "recipients
in contacts" through the gate.
- mail host service: contacts::known_addresses (own accounts, recorded
send recipients, To/Cc of sent folders; never the secret store); the
decoder keeps To/Cc; a removed account forgets its recipients.
- docs/ai-services(.zh-CN).md refreshed against main (#106, #113, #117,
#118, #120, #124, #95, #86), linking architecture.md; architecture's
contacts lines updated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
chrislearn
pushed a commit
to chrislearn/OctoSense
that referenced
this pull request
Sep 29, 2026
The README layout tables (root, apps/, phone's `mobile-apps` row) still listed the native comparison modules (`apps/*/native`) that OctoSense-org#113 deleted; the script apps are the only versions. ADR 0004's Context "Today" row now says the crates were deleted by OctoSense-org#113 (not OctoSense-org#110, as a few pages had it). The desktop README drops the now-redundant `--module reference --module sheets` example. Docs only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chrislearn
pushed a commit
to chrislearn/OctoSense
that referenced
this pull request
Oct 8, 2026
HUB-03's fix (930f1b2) had no test. The binary-name lookup moves into find_app_in, which takes the rows, and the_card_binary_names_no_card_runner_app checks that "card" names no Card runner app while another binary still resolves; it fails with the bin != "card" guard removed. CAL-01 is obsolete: first-party apps are script apps (home ADR 0004), a native app needs a reviewed native-apps.json entry (ADR 0004), and the native Calendar source is gone from OctoScript-App-Design-Flow main. RUNTIME-01 is obsolete as written: Maps' native crate and its isolate tests went in OctoSense-org#113, and no test or workflow here calls init_cx_os() or skips for it. The entry records that AppleGameInput::init is unchanged at makepad a1c81312. HUB-05 keeps its two open points (is_linked only used by its test; dead android conditions in mobile_surface.rs) with current paths; the launch_module_as log and the duplicate card filter are done.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ADR 0004 plan steps 1–2, including the former #112 (Terminal as a desktop system app), folded in at the reviewer's request so Terminal never lands in-process-only with a read-only AI.
From #112 (three commits at the base):
app-terminalfeature (in the desktop default set) links makepad-terminal'sTERMINAL_MODULE; on macOS the PTY helper isoctosenseitself (--exec-pty), so nothing ships beside the binary for the in-process form.Keyboard focus fixes for module tiles: a pending focus is re-asserted after the tile draws, and the tile that last took the keyboard releases it on a focus change.
Phone builds unchanged (
mobile-appsdoes not include Terminal).native-apps.jsondeclares every native app once. For each app it gives:bin;tools/native_apps.pygenerates what used to take five hand edits, as marked# BEGIN native-appsblocks:Cargo.tomlpins and[patch]entries;crates/shell/Cargo.tomloptional deps,app-<id>features andmobile-apps;desktop/andphone/defaults;crates/shell/src/native_apps.rs(whatlinked_modules()links, and the hosting table).--checkfails CI on drift (desktop.yml,phone.yml). It refusesprocesson mobile/wasm,processwithout abin, and two revisions of one repository.Cargo.lockchanges only through cargo.The native News, Maps and Photos crates are deleted; the script apps are the only versions. The Photos sample library moves to
apps/photos/resources/, where Home mounts it.Hosting comes from the manifest. Terminal is a process on macOS and Windows (on Linux only with Vulkan and Wayland). If no checkout or sibling binary exists, it falls back to in-process, so packaged builds keep the in-process Terminal until Desktop release packages (macOS .app/.dmg, Windows NSIS, Linux .deb/.AppImage) and release-desktop workflow #94 ships the
bin. App Hub, Rinx and AppCard are in-process, so Rinx and AppCard now open on the desktop without--module.Process launch fixes:
cargo run --locked, so a checkout's lock is never rewritten;~(via--cwd);runstays registered, markedconfirm: host,auto_approvable: falsein native-apps.json (agent.tool_policy); the shell registers it as destructive so every call waits for the live confirm card, refuses a command the card cannot show in full, and runs its own full executor for the in-process Terminal so both hostings offer the same tools.The generated TOML parses identically to the old hand edits.
Cargo.lockdiffers from main only by the four deleted packages.Verification
cargo build --locked -p octosensecargo test -p octosense-shell --features app-terminal: 348 passedmobile-apps: 358 passednative_apps.py --checkpwdprinted$HOME; ended with/quitNot done
processon Linux (unchanged).Policy::shipped()by a test, but not generated yet.🤖 Generated with Claude Code