Skip to content

Keep recordings dropped from the macOS capture thumbnail - #24473

Merged
teamleaderleo merged 17 commits into
feat-cmux-nextfrom
codex/recording-preview-retention
Oct 30, 2026
Merged

teamleaderleo merged 17 commits into
feat-cmux-nextfrom
codex/recording-preview-retention

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Dragging a recording from macOS's floating capture thumbnail could leave chat with a link to a file that macOS immediately deleted. Pin the dropped recording until the existing chat upload owner saves it, then return that saved path. The pane grants access to that exact saved file so its existing open, save, and drag actions continue to work.

Pending saves and retained grants belong to that chat. A real chat switch invalidates in-flight work; a new chat receiving its first session ID keeps its attachments.

Testing

  • Managed native step 86c0f7358bb1b5c47fbfb1b5 passed all 26 tests in AgentPaneRecordingDropTests, AgentPaneAttachDroppedTests, AgentPaneMediaDragTests, and AgentChatUploadsTests on exact head e6541632699d852a1a2402eb44590dc069fbfb30.
  • The tests-only commit ad697b8d66da0fb1b797c1d65ba0d56e2a8228ff compiled and reproduced the deleted-path failures in managed step 7feb0909fcde93461dd553ae.
  • The corrected tests-only head 7a590a7929dc6767d2d84a85581cbaee1d8ca45e compiled and reproduced both the late-save cross-chat leak and first-session attachment loss in step 2c010db370b582de11e02c57.
  • Swift syntax, localization defaults, package groups, feature flags, and the NEXT prepush gate passed.

Observed / Not run

Observed deletion of the original recording before the request, retained bytes after deletion, subsequent media drag, exact-file access without sibling access, expiry, mismatched names, oversized/missing previews, refused saves, and session adoption during an awaited save in native tests. Independent and primary source reviews passed; required CI is still pending. No live floating-thumbnail drag was performed in an installed app. Visual recording previews are a separate change.

Changelog

Fixed: Recordings dragged from macOS's floating capture thumbnail remain available after the thumbnail closes.

Proof

No capture: this change preserves dropped file bytes and exact-file access without changing the attachment's appearance. Native lifecycle and file-boundary regression tests cover the behavior.

Agent session: cosmic-gray

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

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

Review c88db7f: 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=pr24473 -f ref=codex/recording-preview-retention -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on cd1ce12177; ci-status is green without them.

CI passes on cd1ce12177 (run 40979183695 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

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.

web and size checks: green

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

blacksmith-sh Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / nightly-guards / workflow-guard-tests / release-notary is red on 14 of 19 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@cursor

cursor Bot commented Oct 23, 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.

@cursor

cursor Bot commented Oct 23, 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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Checks job structure is red on 13 of 13 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No new SwiftUI (macOS UI is AppKit) is red on 13 of 13 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Script tests (pin-cmux-tui, embed-cef, ensure-cef store) is red on 13 of 13 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh, next-review): BLOCK at 5835c1c

  • Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneDroppedPaths.swift:62 (also AgentPaneModel+Attach.swift:40) references AgentPaneUpload.maximumFiles, but AgentPaneUpload defines no such member. The target and added tests cannot compile.

teamleaderleo and others added 2 commits October 29, 2026 15:22
…ng drops

Base removed the per-ask file count cap (#24652); the 100-name cap on a drop
still bounds how many recordings one drop pins.

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

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

teamleaderleo and others added 2 commits October 29, 2026 16:08
…h scope

Base's replyPathScope() (#24360's link.preview rework) builds its roots
from roots() + addedRoots, no longer from fileOpenRoots(), which is where
this PR adds the retained recording copies. After a chat adopted its first
session, a drag of a retained recording was refused
(firstSessionAdoptionPreservesPendingAndRetainedRecordings failed at
c88db7f). The scope now carries the retained exact files too.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): SHIP at cd1ce12

SHIP

  1. No correctness issues found.

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

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves cd1ce12: an independent review passed this exact head (review box review-24473-cd1ce121 receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@teamleaderleo
teamleaderleo merged commit e346d50 into feat-cmux-next Oct 30, 2026
200 of 206 checks passed
@teamleaderleo
teamleaderleo deleted the codex/recording-preview-retention branch October 30, 2026 00:12
@cmux-bot cmux-bot Bot removed the stack-blocker label Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/40978245548 at e346d50 failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at f101108. This pull request is one of 4 merged since: #25420, #25942, #24009, #24473.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

teamleaderleo added a commit that referenced this pull request Nov 1, 2026
…size (#28695)

* NEXT: tests for Cloud and peer chat uploads of any size (red)

Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS
... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE".

These fail on base, which caps Cloud uploads at 32 MiB a file and 64 MiB
a batch and peer uploads at 1 GiB:
- CloudUploadsTests: a staged Cloud begin past 32 MiB, and a batch past
  the 64 MiB total, are taken; no language of cloud.upload.failed or
  cloud.upload.refused carries a placeholder or a number.
- AgentPeerUploadsTests: an inline peer upload past 1 GiB is sent; a
  staged peer begin past 1 GiB, alone or as a batch's first file, and a
  batch past the 1 GiB total, are taken.
- AgentPaneFilePutTests: a chunked put past 1 GiB sends its begin.
Sizes are only declared (Data(count:) or a declared size); no bytes are
written.

To test the Cloud begin without a live Cloud machine, its admission
check moves, unchanged, into CloudUploads.beginRefusal(name:size:manifest:).
The old test that the refusal names the limits is dropped.

Split out of #27016 (Cloud and peer stager caps); the peer test drop
and the "refusal names no limit" idea come from #27016.

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

* NEXT: Cloud and peer chat uploads take files of any size

Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS
... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE".

Cloud (CloudUploads): drop maximumFileBytes (32 MiB) and
maximumTotalBytes (64 MiB), the guard in upload(_:to:services:), and in
a staged begin the per-file size check, the manifest's per-file and
total checks and the per-session staged total. A begin still needs a
non-negative size and a manifest that starts with the file begun.

Peer (acpmux file_put): drop AgentPaneUpload.maximumFileBytes,
maximumTotalBytes and maximumPeerFileBytes (1 GiB), the four guards in
AgentPaneFilePut, the begin and manifest checks in AgentPeerUploads'
stager and the guard in AgentPeerUploads.upload, and the base64 and
total limits in AgentPaneUpload.files. A peer that refuses a file
(filePut.tooLarge) still says so.

The chunk bound in both stagers is written size - offset, so a huge
declared size cannot overflow it.

Localization: cloud.upload.refused named the limits ("up to %1$@ each,
%2$@ in all") in every locale. There are no limits to name, so the key
and CloudUploadStrings.refused are gone; a refusal from the machine
shows cloud.upload.failed ("Couldn't send the file to the Cloud
machine."), already translated in every locale, and keeps its
upload_refused code for the page.

Split out of #27016 (Cloud and peer stager caps).

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

* NEXT: tests for page-decoded uploads of any size reaching Cloud and peer (red)

Review of #27073: the request decoder (AgentPaneRequest+Decoding) still
refuses a staged begin, or a manifest entry, over 1 GiB, and a manifest
of more than 64 files, before CloudUploads or AgentPeerUploads see it.
The stager tests called the stagers directly and missed it.

These fail on this head:
- AgentPeerUploadsTests.aDecodedBeginOfAnySizeReachesThePeerStager and
  CloudUploadsTests.aDecodedBeginOfAnySizeReachesTheCloudStager (2 GiB
  and 8 GiB): a chat.upload.begin decoded through AgentPaneRequest(body:),
  the bridge's own decoding, alone, as a batch's first file, and as a
  small first file whose manifest names a file over 1 GiB, must decode
  into the staged begin and be taken by the peer stager and the Cloud
  stager's begin check (DecodedStagedBegins builds the requests).
- AgentPaneUploadTests.stagedUploadAcceptsAnyNonnegativeFileSize and
  stagedUploadAccepts65LargeFiles, with
  stagedUploadRejectsNegativeFileAndBatchSizes, taken verbatim from
  #26409 so the two PRs' test hunks are identical.

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

* NEXT: the request decoder takes a staged upload of any size

Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS
... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE".

AgentPaneRequest+Decoding: drop maximumUploadStageFile (1 GiB) from a
chat.upload.begin's size and each manifest entry's size, and the
64-entry manifest limit. A begin still needs a file name and a
non-negative size; a manifest still needs one or more well-formed
entries. A chunk stays at most 512 KiB.

This is #26409's change to the same file, line for line (its
3813876 and 951315e), so whichever of #26409
and #27073 lands second merges it cleanly. Cloud and peer begins do not
pass through AgentChatUploads (CloudHandlers+Move tries Cloud, then
peer, then the local stager), so the local stager's caps stay #26409's.

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

* cmux link access: test a sparse upload over 1 GiB in bounded parts (red)

`cmux link access upload` (the Cloud chat's finish step) still refuses a
file over 1 GiB, so a file the stager took whole fails at the end with
upload_refused. The test pastes a sparse 1 GiB + 1 byte file and uploads
it to a fake fs-v1 machine: it must be accepted and arrive whole, in
parts of at most 8 MiB (overwrite, then appends).

Leo: "Attachments take arbitrarily large files of any type; there is no
size cap".

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

* cmux link access: an upload takes a file of any size

`cmux link access upload` refused any file over 1 GiB (MAX_FILE_BYTES),
so a Cloud chat upload the stager took whole still failed at finish with
upload_refused. The cap is gone. The file was already streamed: copy_file
hands an open file to Remote::write, which reads and sends it in 8 MiB
fs.write parts (overwrite, then appends to a temporary name, then a
rename), so nothing reads the whole file into memory.

The uploads.rs change is taken byte-identical from #27016 (launch-polish),
so the two merge cleanly; credit to that PR.

Leo: "Attachments take arbitrarily large files of any type; there is no
size cap".

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

* Keep staged uploads cancellable through transfer

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Keep peer cancellation test on main actor

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Allow staged cancellation test to overlap finish

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Keep peer transfer task with its stage

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Cancel active staged uploads through terminal cleanup

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Retain batch authorization through upload cleanup

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Stop stage expiry while terminal cleanup owns it

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Keep active uploads bounded by idle progress

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Bound upload reply and diagnostic capture

Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5

* Fix upload regression test isolation and macro mutation

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

* Attachment cap guard: an exemption names only a cap its file still declares (red)

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

* Attachment cap guard: drop the exemptions of caps that are gone

Cloud and peer uploads no longer declare their file and batch caps, nor the
CLI its MAX_FILE_BYTES, so their exemptions go; AgentPaneDraftAttachments'
maximumBytes was already gone on base.

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

* NEXT: a pinned recording keeps its own in-memory budget; stages name every field

Base's #24473 read AgentPaneUpload.maximumFileBytes and maximumTotalBytes for
the macOS capture-thumbnail recording snapshot, which this branch removes, so
the merged tree did not compile. The snapshot reads the recording into memory
before saving it, so it keeps the same 1 GiB as its own named budget
(AgentPaneRecordingSnapshot.maximumPinnedBytes / maximumPinnedTotalBytes).
The Cloud and peer Stage initializers name expiry and cleanup explicitly.

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

* NEXT: Cloud and peer staged uploads share one store in CmuxNextAgentPane

The two App stagers each kept their own stages, batches, expiry deadlines and
cleanup tasks (+131 CmuxNextApp lines, which the arch ratchet refuses).
AgentPaneStagedUploads<Target> now owns that once: a stage of any size, a
batch authorized while its stages live, one DemandTimer for abandoned stages
and batches (the peer stager no longer sleeps a task per stage), a transfer
that owns its file until it returns, and a cancel that stops the transfer
before the staged file is removed. Each App stager keeps only its own target
checks, file layout and transfer. AgentPaneStagedUploadsTests covers begin of
any size, batch order, chunk ownership, and cancel during the transfer.

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

* NEXT: Cloud uploads go one file per call; a late cancel takes a delivered file back

Review of the carry (Astra, BLOCK at 6ff0fa8):
- The older one-frame upload sent every file in one CLI call, under no upload
  id: more than 64 files were refused, and a failed or cancelled call left its
  partial file and the files already sent on the machine. CloudAccessCLI.upload
  now sends each file in its own call under an id of the app's, and a failure or
  a cancel removes every file of the upload (removeUploads) before it answers.
- A cancel that came after the transfer delivered the file (the chip's x while
  the answer was on its way) found no stage and left the file on the machine.
  AgentPaneStagedUploads keeps a finished stage's undo for its lifetime; the
  Cloud stager's undo removes that upload id. Peers have no remote delete yet.
- CloudBringBackTests expects the upload id on the CLI call (the original
  already sent it for one file).

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

* NEXT: a Cloud upload's cleanup tries every file; the undo test compares before and after

Review of the carry (Astra, BLOCK at f6edd0e): removeUploads stopped at the
first failed removal, leaving later files on the machine; it now tries every
id and throws after. The late-cancel test expected no undo at all on its
second file, though the first had rightly been undone.

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

* NEXT: removeUploads' doc says once that every id is tried

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

* NEXT: intake's batch upload call keeps one formatter-stable line

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

* NEXT: the staged-upload undo test logs on a main-actor class; package-only imports

The fleet test compile refused the test's undo closure that appended to a
captured var (converting non-Sendable function value). AgentPaneStagedUploads
is package, so its imports need not be public.

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

* fix(attachments): drop the stale MAX_FILE_BYTES cap exemption

Base removed MAX_FILE_BYTES from access_cli/uploads.rs, so the guard's
exemption named a cap that no longer exists and
tests/test_attachment_caps_guard.py failed (1 of 19).
python3 -I tests/test_attachment_caps_guard.py: 19 OK.

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

* test(upload): a part in flight reports progress before its answer (red)

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

* fix(upload): report progress while a part is in flight; idle outlasts the socket deadline

Codex BLOCK on #28695: the 30 s upload idle only heard a heartbeat after a
whole 8 MiB fs.write returned, so a healthy slow link was killed. The Rust
Remote now writes a request line in 256 KiB slices and reports progress after
each slice and on each answer; the app's upload idle is 90 s, longer than the
CLI's 60 s per-read/write socket deadline (IO_DEADLINE). No size cap.

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

---------

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

This branch was previously deployed

1 inactive deployment
ci-routing-agent — cd1ce121 Deployed Oct 29, 2026 by teamleaderleo via wait for same-key tree publisher #78810
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.

1 participant