Skip to content

feat(next): add Labs Artifacts pane - #27264

Closed
teamleaderleo wants to merge 2 commits into
feat-cmux-nextfrom
lane/artifacts-pane-mvp-clean
Closed

teamleaderleo wants to merge 2 commits into
feat-cmux-nextfrom
lane/artifacts-pane-mvp-clean

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

HOLD: Implementation, remote validation, before/after captures, and exact-head independent review are unfinished.

Adds a read-only Artifacts pane behind Labs preview features, which are off by default. The native host reads the account's /v1/artifacts tree, file, and history endpoints with its existing cmux bearer. HTML previews are being moved onto the existing shared artifact host; Markdown and JSON stay text. History selection compares the selected revision with its parent and shows session, machine, and harness trailers.

Leo: “as for the syncing memories and stuff, i think it'd be useful for customers too... we can look at how these guys do artifacts? https://github.com/pierrecomputer/pierre or anything else interesting from them / also this https://www.cloudflare.com/products/artifacts/”

Earlier: “like the claude.ai artifacts but imagine you didnt need to only have it on claude.ai”. On the MVP: “go ahead”.

Reuse and overlap review:

  • Read #25190, superseded by #26216, plus the CEF host #26224 and WebKit host #26286. The intended reuse is their existing ArtifactFrame Preview/Run flow, artifactSandbox policy, one-shot cmux-render protocol, and PageArtifactFrame / CEF artifact endpoint. This draft currently still contains a separate scripted srcdoc implementation; replacing it is unfinished and required before SHIP. No claim of completed reuse or inherited runtime validation is made.
  • The existing host permits only the exact cmux.agent parent/endpoint. The new cmux.artifacts page needs a coordinated, narrowly scoped extension of the shared host and its CEF/WK navigation/resource tests, not a second renderer or a broad frame allowlist. Passive preview must remain script-disabled; explicit Run uses the existing opaque sandbox.
  • Read Austin Wang's project-local Artifacts #8610: session-rooted .cmux artifacts/notes, marker rediscovery, LocalArtifactRepository, CLI and filesystem sidebar. This cloud-repository pane does not extend or copy that storage implementation. The related design is acknowledged here; its branch and PR remain untouched.

Sandbox and threat notes:

  • Shared host reuse must retain sandbox="allow-scripts" without allow-same-origin, response CSP with connect-src 'none', one-shot initialization, and refusal of self-navigation, nested frames, network/file/page resources and bridge calls. Before Run, HTML has no script capability.
  • The client never chooses a repository or receives an Artifacts/git token. The native host performs only authenticated reads.
  • The native endpoint must be corrected to the existing development platform route before live acceptance; feed.apiBaseURL is not that endpoint.
  • The fixture backend models immediate-child tree responses and distinct commit versions. Three gallery layouts are included; stacked preview/history is the default. These are synthetic fixtures, not real integration evidence.
  • @pierre/trees and @pierre/diffs use their existing exact package pins. The upstream trees NOTICE is preserved.

Validation remains incomplete. The first remote check failed in “Prepare full webview tests” before test execution. No compile, screenshot, or sandbox-test receipt is claimed. The initial broad typecheck ignored the new directory and does not validate the pane. The local corrections add the .gitignore exception; a subsequent local web check passed, but no fleet/native/browser receipt is claimed.

The only HOLD here is this PR's unfinished implementation and validation. No blanket Sidebar or Swift hold applies; remove this WIP HOLD once exact-head SHIP and required evidence are complete.

Before/after screenshots: pending Linux gallery capture. This PR is not ready to land.

Agent session: agent session

@cursor

cursor Bot commented Oct 29, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 287f62c: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr27264 -f ref=lane/artifacts-pane-mvp-clean -f platform=macos -f backend_mode=local

Unclear: web / react-apps-check failed: no known signature; failed step: Check webview test shards.

CI failed on ad0fb02f2d (run 40969403461 attempt 1): 4 code, 1 unknown.

Job Verdict Why
web / react-apps-check (4) code a web test failed
web / react-apps-check (3) code a web test failed
web / react-apps-check (2) code a web test failed
web / react-apps-check (1) code a Python test failed
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Failures:

  • seen on other PRs model-menu-submenu-up.test.ts chromium: at the window's foot, Effort opens up from its row: failed (job)
  • seen on other PRs model-menu-submenu-up.test.ts chromium: at the window's top, Effort opens down from its row: failed (job)
  • seen on other PRs model-menu-submenu-up.test.ts webkit: at the window's foot, Effort opens up from its row: failed (job)
  • seen on other PRs model-menu-submenu-up.test.ts webkit: at the window's top, Effort opens down from its row: failed (job)
  • seen on other PRs reasoning-menu-check.test.ts chromium: the check trails the chosen row; labels start at the row's inset: failed (job)
  • seen on other PRs reasoning-menu-check.test.ts webkit: the check trails the chosen row; labels start at the row's inset: failed (job)
  • seen on other PRs reasoning-own-control.test.ts New Tab at 760 px: the model button opens its menu, whose Effort row lists live levels: failed (job)
  • seen on other PRs reasoning-own-control.test.ts New Tab at 400 px: the model button opens its menu, whose Effort row lists live levels: failed (job)
  • seen on other PRs MarkdownMath.test.tsx keeps shell command dollar signs as plain text without dropping content: failed (job)
  • seen on other PRs queuedMessages.test.ts each agent message is a received-card row, in the queue's order, with its promptId: failed (job)
  • ... 14 more in the job logs
Matched log lines
web / react-apps-check (4): (fail) chromium: at the window's foot, Effort opens up from its row [31074.17ms]
web / react-apps-check (3): (fail) keeps shell command dollar signs as plain text without dropping content [9.09ms]
web / react-apps-check (2): (fail) chromium: agent-pane.new-tab reasoning-chip at 760 px: picking a mode or a level hands the keyboard back to the prompt [31562.11ms]
web / react-apps-check (1): FAIL: test_coverage_base_gap_warns_on_a_pr_changing_the_gallery_format (__main__.ScopedChecksTests.test_coverage_base_gap_warns_on_a_pr_changing_the_gallery_format)

Not re-run automatically: web / react-apps-check (4), web / react-apps-check (3), web / react-apps-check (2), web / react-apps-check (1), web / react-apps-check are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

red: cmux-next crash ratchet; NEW: green on the base b9bb6bb; do not merge until green; see lane-rules

Gallery before/after for ad0fb02: the capture produced no images (run).

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Default changes

This pull request changes 4 defaults at ad0fb02f2db7 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextApp/AppActions.swift:bind: "9760ef56c3a4aa1c335bdeafcd9602897769726ec3636742c0b75877d47f367a" -> "23c3201e86533b9139c251910ef3f6df095e829ccd1875be95130e115e8a29ca"
  • tunable-source.CmuxNextApp/Pages/ArtifactsPage.swift:artifacts: (none) -> "a0f91a33afd0a69d1b8486cb3ec354516c8a7e8e2f0addccf81d9b7b2be3cfbf"
  • tunable-source.CmuxNextApp/Pages/ArtifactsPage.swift:artifactsBaseURL: (none) -> "bb0078ed221125f1e5c817bdfbfe561bc7ec06553d82906aa65c67d2279ca6bc"
  • tunable-source.CmuxNextApp/Pages/ArtifactsPage.swift:bind: (none) -> "4e6835db335c703e1c8dd6740831b031da68865dc06c8eccf2374b597e6e1f35"

Needs a review. It passes with an independent review receipt on this commit: a comment or review whose first line is Review (<reviewer>): SHIP at ad0fb02f2db7119056e9d0130900626a248c08e7, from a member, not a self-review (the newest verdict naming the head decides; a later BLOCK beats an earlier SHIP), or with an approving review on this commit from @lawrencecchen or @austinywang (the call owners in .github/defaults-call-owners.json, never the author). A receipt posted as a review re-runs this check; after a plain comment, re-run it. The needs a call label alone does not pass, and a new push needs a new receipt.

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-oasuj6 created for this PR by the cmux coordinator.

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Found 44 test failures on Blacksmith runners:

Failures

Test View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
/tmp/tmp4xev96zt/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmpoy97j0ed/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs

...and 34 more test failures. View all on Blacksmith: run 40969403431, run 40969403461, run 40969403446

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Orphaned: no activity for 16 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged feat-cmux-next in at ad0fb02. A later pass queues it for the merge hub if it is reviewed, small and red only on base reds.

@teamleaderleo

teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Carried on #27823 (this commit with its authorship, rebased onto feat-cmux-next, plus the fixes that make it build and match the platform reads). Review and landing happen there. This PR stays open for its owner to close.

teamleaderleo added a commit that referenced this pull request Oct 30, 2026
Carries #27264's Artifacts pane and repairs it so it builds and matches
cmux-platform's /v1/artifacts reads:

- CmuxNextPages/ArtifactsPageProvider: the page's three ops (tree, file,
  history) become one checked GET each with the app's bearer; params are
  allowlisted (ref and path strings, limit 1-100), a refusal becomes the
  app's own words (signed out, nothing saved, unreachable), a file over
  4 MiB comes back without content, bytes that are not UTF-8 as base64.
  The provider asks labs.previewFeatures on every read, so a restored tab
  reads nothing once Labs is off. Unit tests cover each of these.
- The App wiring sits in StatsPage.swift beside the other preview page
  (no new App file or type); artifacts.show is palette-only and GUI-only.
- The web page imported a module that does not exist and assumed a
  recursive tree; it now reads the tree a folder at a time (capped at 200
  folders), shows it in the shared keyboard tree (ui/Tree), previews HTML
  in the agent pane's scriptless srcdoc (sandbox=""), and shows a chosen
  revision's change against its parent with the agent pane's line diff.
  A parent where the file did not exist reads as added. A reply for a
  file the person already left is dropped; a refresh keeps the tree.
- Strings are localized in all 21 locales (page, native, palette).

Tests: webviews/test/artifacts-page.test.ts (8 pass on this head);
Tests/CmuxNextPagesTests/ArtifactsPageProviderTests.swift (run on the
fleet, not here).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-1144e1f1f6e83544a4cb
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high fast, review-router; run by a backlog lander, relayed): BLOCK at ad0fb02

  • P1: the artifacts API override/default can send credentials to an arbitrary or dev origin.
  • P2: the provider is registered unconditionally despite its Labs gate.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Superseded by its carry #27823 (this commit rebased onto feat-cmux-next with the build fixes). Closing this copy.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Conflict wave: not resolved. This PR is superseded by its carry #27823 (the author's comment above). I pushed no merge. I did not close the PR; a person can close it.

teamleaderleo added a commit that referenced this pull request Nov 5, 2026
…iff (#27823)

* feat(artifacts): add Labs read-only artifacts pane

Agent-Session: https://cmux.com/deeplink/agent?id=aid-1144e1f1f6e83544a4cb

* feat(next): finish the Labs Artifacts page on the platform reads

Carries #27264's Artifacts pane and repairs it so it builds and matches
cmux-platform's /v1/artifacts reads:

- CmuxNextPages/ArtifactsPageProvider: the page's three ops (tree, file,
  history) become one checked GET each with the app's bearer; params are
  allowlisted (ref and path strings, limit 1-100), a refusal becomes the
  app's own words (signed out, nothing saved, unreachable), a file over
  4 MiB comes back without content, bytes that are not UTF-8 as base64.
  The provider asks labs.previewFeatures on every read, so a restored tab
  reads nothing once Labs is off. Unit tests cover each of these.
- The App wiring sits in StatsPage.swift beside the other preview page
  (no new App file or type); artifacts.show is palette-only and GUI-only.
- The web page imported a module that does not exist and assumed a
  recursive tree; it now reads the tree a folder at a time (capped at 200
  folders), shows it in the shared keyboard tree (ui/Tree), previews HTML
  in the agent pane's scriptless srcdoc (sandbox=""), and shows a chosen
  revision's change against its parent with the agent pane's line diff.
  A parent where the file did not exist reads as added. A reply for a
  file the person already left is dropped; a refresh keeps the tree.
- Strings are localized in all 21 locales (page, native, palette).

Tests: webviews/test/artifacts-page.test.ts (8 pass on this head);
Tests/CmuxNextPagesTests/ArtifactsPageProviderTests.swift (run on the
fleet, not here).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-1144e1f1f6e83544a4cb

* fix(next): give the Artifacts page its own line diff so its bundle needs no shiki

The agent pane's diff.ts imports its whole model, which reaches
@pierre/diffs and shiki; the pages bundle has no shiki, so
build-pages-web.sh failed on the artifacts page. lineDiff.ts is a
small LCS line diff with three lines of context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-1144e1f1f6e83544a4cb

* fix(next): Artifacts page review fixes: passive frames, refresh, API origin

- PageDescriptor.passiveFrames: the page's own document may set a frame's
  srcdoc (WebKit otherwise cancels that load on a non-agent page, leaving
  the preview blank), and a srcdoc document can neither navigate itself
  (a clicked link, a meta refresh) nor be replaced by another URL. The
  Artifacts page opts in. PagePassiveFrameNavigationTests.
- A refresh whose tree head moved reads the chosen file and its history
  again, keeping them on screen meanwhile; an unchanged head reads only
  the tree.
- CMUX_NEXT_ARTIFACTS_API_URL applies only to a platform Worker
  (https *.debussy.workers.dev) or wrangler dev on loopback, since the
  bearer goes there; anything else falls back to development.
- Query values are percent-encoded whole: URLComponents.queryItems kept
  & and = as is, so a path like x&ref=y added a param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Artifacts page review fixes: CEF passive frames, consistent reads

- CEF: the shim's srcdoc lockdown (ArtifactContextRestricted) covers the
  Artifacts page's frames too (IsPassiveFramePageURL), so on Chromium a
  saved document cannot navigate or load anything but inline media, as
  on WebKit. cmux_page_path_test.cpp covers it (158 cases pass). The
  preview frame is keyed by path and commit: a passive srcdoc frame
  refuses to be replaced, so each document gets a new frame.
- The store reads folders, the chosen file and its history at the root
  tree's commit, so a save landing meanwhile cannot mix two trees.
- A failed reload is retried: the next refresh reads the file again
  until a read at that head succeeds, and choosing the file again
  retries too.
- artifacts.show takes focus only when the run allows a view change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Artifacts page: retry a failed change, parallel folder reads, tree follows selection

- A refresh at the same head reads the chosen file again until its
  newest change has shown too, and clicking the chosen revision retries
  a change that failed to read.
- Folders under the root are read up to four at once (the old loop
  drained the queue one at a time).
- The tree's highlighted row follows the store's choice when a refresh
  picks another file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Artifacts history shows a change to the file's final newline

A last line with no newline after it was the same line as one with, so
adding or dropping the final newline read as "did not change the
file's text". lineDiff marks such a line while diffing and the page
says "No newline at end of file" after it, as git does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Artifacts line diff keeps the no-newline mark out of the text

The last line's missing newline was an in-band suffix, so a file whose
text ended in that suffix read as having no final newline. Lines are
now {text, noNewline} and compare on both. The hunk walk reads each
line's next change from one backward pass instead of a scan per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Artifacts provider tests type-check fast

action exports (Mac) failed to emit the test module:
ArtifactsPageProviderTests.swift:80 "the compiler is unable to type-check
this expression in reasonable time". That was the @test(arguments:) list
of (String, JSONValue) tuples built from mixed literals. It is now a typed
static [BadParams] of explicit JSONValue cases, and the other literal-heavy
arguments and expectations in the file (the expected reads and file, the
refusal codes, the override lists) are typed locals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Artifacts: diff line colors as page tokens

The design-token ratchet refuses raw colors outside custom property
definitions; the added and removed line fills move into two page tokens
with the same values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-627034dbf10a04318c5c

* fix(artifacts): a failed history read keeps the file preview; binary or too-large revisions show no diff

Review findings at 4e5ba0f (router-review BLOCK F1/F2): the file and history reads no
longer share one Promise.all, so a history failure shows beside the preview and a refresh
retries it; a revision whose side has no text says so instead of diffing it as empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0b517f2a10c3ba1c8f62

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — ad0fb02f Deployed Oct 29, 2026 by teamleaderleo via wait for same-key tree publisher #78746
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants