Skip to content

Fix built-app browser launch, chat links, exports and runtime isolation - #392

Merged
Broccolito merged 40 commits into
mainfrom
claude/biorouter-built-apps-testing-cc226e
Oct 12, 2026
Merged

Broccolito merged 40 commits into
mainfrom
claude/biorouter-built-apps-testing-cc226e

Conversation

@Broccolito

@Broccolito Broccolito commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Built apps could send Chrome to Electron’s secret-only relay, open deleted build chats, and ignore Full/bundled-daemon export options. Launch and managed previews now use the daemon origin, deleted chats show a disabled explanation, and all export modes share a binary-safe ZIP implementation with executable launchers and explicit payload selection.

Included knowledge is imported through the authenticated daemon API before skills are installed. Allocated base IDs are rebound without cascading mappings; durable receipts prevent duplicate imports and preserve recipient edits. Windows PowerShell 5.1 and Node share receipts across short/long aliases. Bundled exports pair the selected daemon with its pinned native compiler. Desktop and Linux CLI packaging now ship esbuild 0.27.3.

The remaining review fixes cover authored static handlers, same-app figure frames, build rollback, app-generation tokens and deletion retirement, bridge ownership, browser navigation policy, credential ownership and atomic desktop writes. Plain HTTP network addresses refuse app pages and the secret-bearing serve shell. Documented limits remain in the handoff; this change does not establish per-app origins/storage or universal extension bundling.

Validation completed locally:

  • Nine full CARGO_BUILD_JOBS=2 just check-everything runs; latest source checkpoint d74b2985d. Agent Drafter Rust namespace: 369 passed, 2 ignored. Focused Crew fixture: 53 passed, with the previously failing case repeated successfully.
  • Real Chrome policy: 12 assertions; real CLI serve protocol: 8 assertions. Actual generated Bash/Node/PowerShell launcher import, retry, consent, collision, receipt and edit-preservation fixtures passed. PowerShell 5.1 digest/payload and cross-launcher alias tests passed on Windows at 21b770408.
  • Native Electron: default-browser static and agentic Launch, existing/deleted build chats, old-port preview rebinding, visible figures, missing-app feedback, successful/refused workflow and hints writes, and all three native folder-picker exports.
  • Fresh Full and bundled recipients actually imported the included knowledge base, installed the selected custom skill, read its fictional verification page through the real configured provider and returned the expected markers. Bundled source was deliberately invalidated and rebuilt with its own native compiler; its figure rendered correctly. Full/bundled replay reused the daemon with unchanged registry/receipts. The thin recipient’s missing-prerequisite warning and operation after independently installing its prerequisites both passed.
  • Final production rebuild native Launch returned COBALT_PEAR_47 / FINAL_LAUNCH_QA_OK through knowledge__kb_read_page. All QA inputs were fictional; the main checkout, installed app and original QA profile were preserved.

Final verification at e5cd7e206a88355b491bee48aa2d707f8c4d93bd:

  • 23 PR checks succeeded; the conditional nightly-only cross-build was skipped. Rust workflow succeeded on macOS, Windows and Ubuntu, including sandbox integrations, Ubuntu’s remaining integration binaries under loopback-only network isolation, both cross-checks, the production socket guard and the actual informational host-clippy step. macOS workspace: 9,230 passed / 10 ignored, plus sandbox integration 18 passed / 1 ignored. Windows workspace: 8,758 passed / 6 ignored, plus sandbox integration 17 passed / 1 ignored. Ubuntu workspace: 9,222 passed / 10 ignored, sandbox integration 18 passed / 1 ignored, and 164 offline integration binaries: 1,158 passed / 55 ignored (nested filtered child invocations excluded from those totals). Actual PowerShell 5.1.26100.33438 passed all four digest cases, authenticated payload import and cross-launcher receipt reuse.
  • Full frontend workflow succeeded: 754 files, 10,553 Vitest tests passed / 19 skipped (474.93 seconds), generated API contract, Electron preview, Windows console, registry, shelf and static checks. Native payload workflow, reference apps and commit-message check succeeded.
  • Final artifact-only Linux packaging succeeded at the same SHA. The linked production backend uses glibc 2.30 against the 2.31 floor. All four GUI/CLI Debian/RPM packages built and passed clean Debian/Rocky installation/runtime checks. The installed CLI compiler executed, reported esbuild 0.27.3 and compiled source to a nonempty 49-byte bundle in both distributions. GUI compiler staging/shipped-payload checks passed separately; this workflow does not independently execute the compiler extracted from the GUI package.

Linux package artifact: linux-packages-1.92.1, 664,023,807 bytes; ZIP SHA256 76eb21a73588ab41decf09def27843f25ae3cfcf81d4aaa2dc399ed1fe16c282.

Package SHA256
GUI deb ee483a48336c5e5006d80f94cc2d8ebf2b3a720ddf4a3a5533d5987d1e131f6f
GUI rpm d1cc8e871c865871d4f6189a5d667d25e31497260359fe477d00481203c8b9d9
CLI deb 5b0acaadcc94569754618958b7a0b0197c3db1c996d7da0ad3423cd11bfa09bf
CLI rpm 8d3b1a8a6373886ce2ef1bb197af15c58d30168190223f019fcfa167d5406c58

Handoff · Verification ledger. No merge, release publication or replacement of the installed app is part of this PR.

…rden built apps

Launch in browser: the desktop sent the browser to its secret-only proxy,
which answered an empty 403 ("Access to 127.0.0.1 was denied"). The launch
route now also returns the daemon's own loopback url; the desktop, the
in-chat preview and `biorouter apps open` send the browser there, and the
launch page opens in the default web browser. Failures are reported.

Build chat: the button checks the chat through the privacy-gated session
read, is grayed out with a visible reason when the chat is gone, and never
opens a dead tab.

Export: one shared implementation (agent_drafter/export.rs) behind
export_app, POST /apps/{id}/export (zip) and the desktop. Full mode,
bundle-daemon, exec bits, binary files and the raw entry page now work;
workspace, vault and key files never travel; launchers install into the
right store per OS and never clobber another app.

Also: ship a pinned esbuild with the desktop and harden the fallback
bundler, serialise and atomically write builds, show build failures,
hash-pin authored inline scripts for static apps, idempotent assembly,
figure frames with their own policy, app_uid-keyed sessions, delete
hygiene, a Fetch Metadata navigation gate for app pages and the serve
shell, and broken-manifest listing.

Open review findings and remaining verification are recorded in
docs/agent-drafter/built-apps-hardening-handoff.md.
@Broccolito
Broccolito marked this pull request as ready for review October 11, 2026 08:27
@Broccolito
Broccolito merged commit 6eb1bce into main Oct 12, 2026
26 checks passed
@Broccolito
Broccolito deleted the claude/biorouter-built-apps-testing-cc226e branch October 12, 2026 07:30
Broccolito added a commit that referenced this pull request Oct 12, 2026
Semantic fixes made inside the merge (merge plan 2026-10-12, section 4):
- 4.1 routes/apps.rs opening_is_not_activity_app_tests: the test manifest
  sets app_uid: None, and load_page mints the socket token the way
  serve_index does, ws_token_for(app, manifest.app_uid.as_deref()) from the
  stored manifest, so #392's page-token gate admits the page (3/3 pass).
- 4.2 #391's CommandChip restyled to the composer chip idiom (no
  --motion-fast); its copy moved into COMPOSER_COPY.
- 4.5 app export goes through #392's IPC (window.electron.exportApp); the
  renderer's buildExportUrl/ExportOptions/ExportMode path is gone.
- 4.6 ChatInput.bugCommand.test no longer passes the removed
  disableAnimation prop.
- landing/docs.html regenerated from the merged docs/website/pages.

cargo check --workspace --all-targets and tsc --noEmit are clean.
Broccolito added a commit that referenced this pull request Oct 12, 2026
…the restyle never covered follows the redesign's rules: the export dialog's app check face is ModalShell anatomy with a warning Note, both faces scroll their body, an unreadable app is a two-line row, the export toasts and the open-in-browser failure read from copy registries [R-15, R-27]

ExportAppDialog (applications/):
- The app check (lint) face: the consequence stays the visible subtitle; the advice ("Fix them in the chat that built the app, or export it as it is.") moves behind the title's InfoTip (principle 2); the findings sit in one warning Note (AlertTriangle) whose own eight-line ceiling and "Show more" replace the hand-rolled 8-item cap and "N more", so every finding is readable; no nested max-h scroller, the shell body is the one scroller (scrollBody).
- The face stays while Export anyway runs (button "Exporting…", disabled, neutral by ruling 22). It used to fall back to the mode choice under the pointer. Phase becomes running + lint state.
- Focus moves to the named findings list when it arrives (the button that asked turned disabled, which drops focus to body in Chromium).
- The choose face takes scrollBody too: a full export of an app reaching four knowledge bases, a skill and an extension clipped its last rows (Include Biorouter included) at the 1048x600 minimum window, with no scrollbar, because the shell hides overflow. Radios are disabled while an export runs, like the items. "Nothing to include" is a neutral Note (the empty-store line).

ApplicationsView: an unreadable app's row is two lines like every row (principle 4): the danger "Can’t be read" badge carries what it means as its InfoTip help, the second line is the parser's reason (mono, a machine string). Launch's failure uses failureSentence (one IPC unwrapper, same words). The export notes toast stays unclamped under the app's name, but each note is now a sentence (exportNotesMessage: a lower-case daemon clause is capitalised and stopped, words unchanged), so two daemon notes no longer run together into one unpunctuated line.

Copy: exportedMessage's three strings move into appsCopy (exportedTo, downloaded, exportCarries); buildChat's strings (already in appsCopy) take the registry's typographic apostrophe; the lint copy drops moreProblems. utils/openInBrowser.ts exports OPEN_IN_BROWSER_COPY ("Couldn’t open the app", the same sentence Built apps' Launch uses for the same failure, pinned by a test), and its three surfaces' tests (utils, MCPUIResourceRenderer, ArtifactViewer.openInBrowser) import it. WebPagePreview needed no change: its failure surface is this toast.

Tests: ExportAppDialog.test 9 -> 16 (the 9 #392 acceptance cases unchanged except the notes case, which now expects the daemon clause as a sentence; new: every finding in the warning Note with no inner scroller, advice behind the InfoTip, focus on the findings, the face stays while Export anyway runs, radios held while running, the neutral nothing-to-include Note, three notes as sentences unclamped); ApplicationsView.test broken-row case asserts two lines and the badge's description; new exportMessages.test (8); new builtAppsExport.browser.test (Chromium, real origin route-fulfilled, compiled main.css): rows equal height incl. the broken one, body scrolls and the last row is reachable at 1048x600 and 1440x900 light and dark with the footer rule, the lint Note is a wash, one scroller, findings at 12px, folds then expands, focus on the list, every text size on the 12/13/14/17/24 scale.
Mutations (impl/ws-views-a-tools/r392-restyle/mutate.py, each restored byte for byte): choose face without scrollBody, findings out of the Note, no focus move, the face falling back while running, raw note join, a third line on the broken row: each turns its jsdom and/or Chromium cases red.
Screenshots read (rows, full export at 1048 and 1440, lint face folded and expanded; light and dark).
Gates: applications + workflows + BioRouterHintsModal + the three openInBrowser surfaces + src/test copy guards + emphasis/raw-dialog guards + measures = 39 files / 490 tests green; tsc, eslint, prettier clean; no-goose-block clean. No main.css change.
Broccolito added a commit that referenced this pull request Oct 12, 2026
…s again after the #392 merge: its toasts stub gains toastWarning, which ApplicationsView now imports for the export notes toast (a423670, called in ec0c0f3). Stub only, no assertion changed; 11/11 pass, including the restored-disabled instrument cases; prettier, eslint, tsc clean [R-27]
Broccolito added a commit that referenced this pull request Oct 12, 2026
…ter/render.rs is in no workstream's ownership table; WS-BRAND owns the R-31 app-folder rule it now contradicts)): the exported-app launchers read the folder the daemon reads on Windows. run.ps1 and serve.mjs named the retired app folder (main's #392) and now apply rule 1 of app_folder (the older folder only while it is the only one), with the names filled from the app_folder constants; the README line and sdk-reference say the same; the launcher test gains the older-folder cases and the real presence check [R-31]
Broccolito added a commit that referenced this pull request Oct 12, 2026
…r React.StrictMode: its mounted ref started true and only the cleanup wrote it, so the development renderer's StrictMode remount left it false for good and run() dropped every answer. The dialog sat on Exporting… after the folder was written and never showed the app check's Export anyway face. The ref now starts false and the effect sets it true. ExportAppDialog.test.tsx renders three cases under StrictMode (closes after exported, the lint face takes focus and Export anyway closes it with allowLintErrors and the retry token, closes when onExport throws) and the four ApplicationsView export cases mount the view under StrictMode. Mutation: the old ref put back fails those 7, the other 12 pass; restored (cmp). Production builds were not affected (StrictMode double effects are dev only); the same code is on main (a423670). Checks: applications/ + 6 importers 12 files / 270 passed, src/test + src/styles guards 48 files / 902 passed; tsc, eslint, prettier clean
Broccolito added a commit that referenced this pull request Oct 12, 2026
…ys on the row at rest when the meta is a long description. The meta line was one truncate paragraph, so a long description pushed 'Build chat deleted' past the ellipsis (measured in the app: the note at x 1390 against a line ending at 1130). The line is now a flex row of two runs: the meta (min-w-0 truncate) gives way and the note (shrink-0, whitespace-pre keeps the ' · ' separator) never does; textContent is unchanged, the p carries data-testid app-row-meta. builtAppsExport.browser.test.tsx gains Renal Dosing Assistant (long description, deleted chat) and a case at 1048x600 and 1440x900, light and dark: the note is inside the line, unclipped and the element painted at its own centre, the description truncates with an ellipsis before it, the line reads 'description · Build chat deleted', the row keeps the other row's height; open() takes an apps list. Mutation: the one-line truncate p put back fails it (note right 1196 vs line right 787 at 1048); restored (cmp). Screenshots read: note visible light and dark at both widths. ApplicationsView.test.tsx's model and kb line case reads the line by its test id (the facts are now its first run). Checks: applications/ + 6 importers 12 files / 270 passed, src/test + src/styles guards 48 files / 902 passed; tsc, eslint, prettier clean
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