Skip to content

Add plugin navigation primitives - #2005

Merged
ymichael merged 18 commits into
mainfrom
bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd
Aug 20, 2026
Merged

Add plugin navigation primitives#2005
ymichael merged 18 commits into
mainfrom
bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd

Conversation

@ymichael

Copy link
Copy Markdown
Collaborator

What was wrong

Plugins had no host-owned semantic navigation boundary. URL links bypassed BB's configured URL router, file links depended on ambient routes or plugin-specific code, and callers could not select an owner-scoped fixed tab with a validated transient target. The existing URL, local-file, file-opener, and shared-panel controllers already implemented the product behavior, but the plugin app contract did not expose a common path to them.

What changed

  • Added experimental_UrlLink and useBbNavigate().experimental_openUrl(url) over the existing URL routing policy.
  • Added explicit workspace, host, and thread-storage live-file targets through experimental_FileLink, experimental_openFilePreview, and experimental_openFileExternally. Preview, file-opener, preferred-external-target, and host-resolution behavior reuses the existing controllers; first-party Markdown, Docs, and GitHub consumers now cross the same boundary. Existing APIs remain available through adapters.
  • Added the generic experimental_useAppPanel().openFixedTab({ surface, tab, target? }) controller, owner-defined experimental_target validation, and experimental_useFixedTabTarget(tab) transient delivery/consumption. The controller has no Diff/Changes branches. Core Changes file reveal and the GitHub Details tab both use the same owner-scoped path.
  • Wired the public surface through the SDK app contract and exports, real runtime, testing runtime and inspection harness, source-generated runtime export manifest and declarations, templates/guide, docs/api_to_audit.md, and the builtin bb-plugin-authoring skill. Link shells remain eager and external file resolution/heavy destinations remain dynamically imported.
  • No database migrations, daemon wire changes, or HOST_DAEMON_PROTOCOL_VERSION bump were required.

How you verified

  • pnpm exec turbo run typecheck across @bb/app, @bb/server, @bb/client-core, @get-bb/plugin-sdk, @bb/plugin-build, @bb/templates, @bb/server-contract, GitHub, Docs, and the thread-chat demo: 14/14 tasks passed.
  • Full Turbo test matrix for the affected app/server/SDK/build/template/contract/plugin packages: app 404 files / 3,065 tests passed, and the SDK, build, template, contract, client-core, GitHub, and Docs suites passed. One unrelated builtin-plugin hot-reload polling test flaked under the concurrent matrix; its exact file passed 23/23 immediately in isolation.
  • Focused navigation harness: 9 app files / 51 tests passed; public thread-tab contract test passed 2/2.
  • Turbo production builds for the app, server, SDK, GitHub, and Docs: 9/9 tasks passed.
  • Bundle budget: boot 427.7 KB brotli / 428.0 KB budget; lazy SplitWorkspaceRoute closure 654.1 KB / 655.0 KB budget.

Fixes # — no linked issue; implemented from the approved plugin navigation primitives plan.

AGENT GENERATED: by GPT-5

@ymichael
ymichael force-pushed the bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd branch 6 times, most recently from 6b307dd to a917ae3 Compare August 20, 2026 18:36
@ymichael
ymichael marked this pull request as ready for review August 20, 2026 18:40

@bb-slop-cop bb-slop-cop 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.

🚨 SLOP COP 🚨 · review

I am the Slop Cop. I am reviewing this pull request for security, code quality, architecture, duplicate code, performance, and real product behavior.

I will post one final review after the parallel checks finish.

Comment thread plugins/docs/app.tsx
? null
: {
kind: source.kind,
hostId: source.experimental_hostId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — The Docs opener drops the selected remote host.

The new live target keeps source.experimental_hostId, but openerSource omits it. The Docs RPC then resolves host files with hostId: null. A remote file can read or save the same absolute path on the primary host.

Please pass the host ID through the opener source schema, the resolver, and both read and save calls. Add a two-host regression test.

intent: ExperimentalFileOpenOptions;
onSettled: () => void;
}) {
const didSettleRef = useRef(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — The external-file queue stops after its first item.

React reuses this dispatcher when the host removes the first queue item. didSettleRef stays true, so the second accepted request never runs or leaves the queue.

Give each request an ID and use that ID as the dispatcher key. Add a test that submits two requests before the first settles.

[intent, navigation, onClick],
);
const anchor = (
<RouteAnchor {...anchorProps} href={target.path} onClick={handleClick} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — A valid file name can activate an external URL scheme.

The relative-path validator accepts names such as vscode:foo and mailto:test. This href makes modifier clicks activate those schemes instead of the file controller. Invalid targets also keep an active native link.

Do not use the file path as the anchor URL. Use a scheme-safe anchor for valid targets, and render invalid targets as inert content. Add tests for these file names.

(event: ReactMouseEvent<HTMLAnchorElement>) => {
onClick?.(event);
if (
!shouldHandleUrlClick(event) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Explicit URL targets do not stay native.

The public contract says that an explicit anchor target remains native. This check does not reject target="_blank", so BB can claim the click and override the requested browser behavior.

Return false from the host path when the anchor has any explicit target. Add a test for _blank with the in-app browser preference.

const environmentQuery = useEnvironment(environmentId, {
enabled: options.enabled && environmentId.length > 0,
});
const storageQuery = useThreadStoragePaths(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Thread-storage resolution scans the full storage tree.

This hook needs only storageRootPath. However, storagePaths makes the daemon scan the complete tree before it applies limit: 1. A context menu or external-open request can cause large filesystem work and remote latency.

Add a direct storage-location route that returns the root and host without a directory scan.

if (hostId === null || path === null) {
throw new Error("Host file preview target is incomplete");
}
const response = await sdk.files.read({ hostId, path, signal });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Host previews read and transform large files before they need the data.

The query reads the complete file and builds a base64 fallback before preview creation finishes. Media and HTML can then read the same file again through the preview lease. A 25 MiB file causes large duplicate work.

Create the fallback only after preview creation fails. Fetch file bytes only for preview types that need them.

)
) : activeHostFilePath !== null && activeHostFileHostId !== null ? (
renderFileOpenerReplacement(
<LazyHostScopedFilePreviewTabContent

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Closed panels keep host preview queries active.

The retained panel body stays mounted when the panel closes. This host-scoped preview receives no isPanelOpen value, so it can read and render a large remote file while hidden.

Pass isOpen to this component. Use it to disable the query, as the workspace and thread-storage preview paths do.

return buildFilePreview({ contentBytes, mimeType, name, path, url });
},
enabled,
staleTime: 30_000,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Large host preview payloads use the default cache period.

This query can hold full file bytes and a large data URL. Without HEAVY_PAYLOAD_QUERY_POLICY, inactive entries remain for five minutes and can use substantial browser memory.

Apply the shared heavy-payload policy. Consider a shorter period for absolute host files.

Comment thread plugins/github/app.tsx
>
{open ? "▾" : "▸"}
</button>
{environmentId === null ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Removed pull-request files link to missing live paths.

When file.status is removed, the worktree normally has no file at this path. The new live link opens a preview that must fail.

Render removed paths as plain text. Keep the separate diff expansion control available.

@bb-slop-cop bb-slop-cop 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.

🚨 SLOP COP 🚨 · review

Plain English summary

This pull request gives plugins shared controls for URLs, files, and right-panel tabs. It also moves several built-in plugins to these controls.

The design has a good goal. Plugins should use one BB navigation policy instead of separate custom code.

Review result

I found nine verified problems. Two can send file work to the wrong place or stop an accepted action.

High priority

Medium priority

Low priority

Architecture and duplicate code

The shared navigation host is a sound direction. I found no separate duplicate system that must block this pull request.

The performance fixes should reuse two existing patterns. Add a direct storage-root query, and apply the shared heavy-payload cache policy.

I rejected three weak concerns. A budget increase does not prove bundle growth. Small tab lists make the repeated lookup low risk. Stable context values prevent a proven render fault.

Verification

The security, quality, and performance reviews ran in parallel. A separate GPT-5.6 review checked the combined findings.

The focused app checks passed 35 tests. The focused SDK checks passed 41 tests. Current GitHub CI checks also pass.

A live browser opened the test plugin. The URL control opened GitHub, and the fixed-tab control selected the plugin tab.

Please fix the high and medium findings before merge. This review uses a comment only. It does not approve or request changes.

@ymichael
ymichael force-pushed the bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd branch 2 times, most recently from b08c45e to b494e78 Compare August 20, 2026 21:47
@ymichael
ymichael force-pushed the bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd branch from b494e78 to d68a893 Compare August 20, 2026 22:07
@ymichael
ymichael merged commit 9768d55 into main Aug 20, 2026
13 checks passed
@ymichael
ymichael deleted the bb/implement-plugin-navigation-primitives-thr_9rqwy7pmsd branch August 20, 2026 22:14
ymichael added a commit that referenced this pull request Aug 20, 2026
## What was wrong

The Docs file opener rebuilt the SDK's `PluginFileOpenerSource` for its
private RPC contract but dropped `experimental_hostId`. Its backend
schema could not accept that field, and host-file resolution hardcoded
`hostId: null`, so opening, previewing, or autosaving a file selected on
remote host A could read or overwrite the same absolute path on the
primary host.

## What changed

The Docs app now carries the existing optional `experimental_hostId`
through its opener source for both `openFile` and `saveOpenedFile`. The
Docs RPC schema and parser preserve that value, and host-file resolution
returns it to the existing Files SDK read, preview, and write calls.
When the field is omitted, resolution still returns `null`,
intentionally preserving primary-host behavior.

This is the smallest complete fix because it extends the direct data
flow for the existing SDK source identity; it does not add a parallel
host field or refactor unrelated routing. PR #2083 is now merged, and
this branch is rebased on top of it; that PR changes persisted app-tab
source routing, while this change fixes the separate Docs app/server RPC
hop.

There is no host-daemon wire change: the plugin RPC is server-owned and
the Files SDK already supports its host selector, so
`HOST_DAEMON_PROTOCOL_VERSION` is unchanged. There are no CLI, guide,
SDK, or user-facing configuration changes.

## How you verified

The regressions failed before the implementation with the exact routing
break: the app test received an opener source without
`experimental_hostId`, while the server test rejected it as an
unrecognized schema key (2 failed, 56 passed):

- `pnpm exec turbo run test --filter=bb-plugin-simple-notes --
app.test.tsx server.test.ts`

After the fix:

- The same focused command passed 58 tests. The app regression verifies
read and autosave RPC identities, and the two-host server regression
gives the primary and remote hosts different content, preview URLs, and
write results, then proves read, preview, and save all selected
`host_remote`.
- `pnpm exec turbo run test typecheck build
--filter=bb-plugin-simple-notes --force` passed after the final rebase:
65 tests passed; the Docs typecheck and build passed; 7 Turbo tasks
succeeded.
- `git diff --check origin/main...HEAD` passed.

Related to #2005 and #2083.

> AGENT GENERATED: by GPT-5.6-Sol
ymichael added a commit that referenced this pull request Aug 20, 2026
## What was wrong

PR #2005 queued normalized external-file intents but rendered one
unkeyed `AppFileExternalNavigationDispatcher`. After the first request
settled, React reused that component for the next queue item, so its
`didSettleRef` remained `true`; the second accepted request neither
dispatched nor left the queue, and every later request remained blocked
behind it. This is the verified [post-merge review
finding](#2005 (comment)).

## What changed

- Queue entries now pair each intent with a host-local monotonic request
ID.
- The lazy dispatcher is keyed by that ID, giving every accepted FIFO
item an independent settle lifecycle while preserving the
activation-only dynamic import.
- The host regression submits two requests in the same event, confirms
both were accepted, and verifies exactly-once dispatch in FIFO order.

This is intentionally limited to internal React state and identity. It
does not change the experimental plugin SDK signature, public plugin
API, server/daemon wire data, or host RPC contracts, so no API audit
entry or `HOST_DAEMON_PROTOCOL_VERSION` bump is needed.

## How you verified

Before the production change, the new focused regression failed with one
dispatch: `expected "vi.fn()" to be called 2 times, but got 1 times`.

After the fix and a clean rebase onto current `origin/main`
(`7f3d2ac66`):

- `pnpm exec turbo run test --filter=@bb/app --force --
src/components/plugin/AppFileExternalNavigationHost.test.tsx` — 2
passed.
- `pnpm exec turbo run test --filter=@bb/app --force` — 411 files
passed; 3,157 tests passed and 3 skipped.
- `pnpm exec turbo run typecheck --filter=@bb/app --force` — passed.
- `pnpm exec turbo run lint --filter=@bb/app --force` — passed with 0
errors (156 existing warnings).
- `pnpm exec turbo run build --filter=@bb/app --force` — passed.
- `node apps/app/scripts/check-bundle-budget.mjs` — passed (boot payload
428.7 KB Brotli vs. 467.8 KB budget).

> AGENT GENERATED: by GPT-5.6-Sol
ymichael added a commit that referenced this pull request Aug 20, 2026
## What was wrong

PR #2005 added an absolute-host preview query that treated heavy preview
work as eager and lightweight: it read the complete file and built a
base64 fallback before attempting the preview lease, kept the retained
panel query enabled after the panel closed, and used React Query's
default five-minute payload retention. A successful media or HTML lease
could therefore duplicate host I/O, while a hidden panel could start or
continue a large read and keep its payload observed indefinitely. This
addresses the connected findings in [the eager payload
review](#2005 (comment)),
[the hidden-panel
review](#2005 (comment)),
and [the retention
review](#2005 (comment)).

## What changed

- Pass the primitive plugin-panel `isOpen` state through the existing
lazy host-preview boundary and use it to gate `useHostFilePreview`.
- Move a disabled retained observer off the active host/path key so
closing the panel aborts the existing SDK request and starts cache GC
without unmounting or changing the lazy-loading boundary.
- Attempt the existing `sdk.files.createPreview` lease first. Successful
image/video leases now return a lightweight URL-backed preview without
reading or retaining file bytes. Text and HTML still read the source
bytes they render, but no longer build a base64 fallback after a lease
succeeds; lease failures build a data URL only for preview kinds that
need one.
- Apply `HEAVY_PAYLOAD_QUERY_POLICY`, giving inactive host-preview
payloads the shared one-minute retention period.
- Cover hidden/open/reopen gating, cancellation and GC, lease-first
media and fallback behavior, HTML source preservation, and the ambiguous
`.ts` source-preview case.

This is direct app query/component plumbing over the existing SDK APIs.
It adds no server or daemon contract fields, so
`HOST_DAEMON_PROTOCOL_VERSION` is unchanged. It also needs no CLI,
guide, or public SDK documentation change.

## How you verified

- Before the implementation, `pnpm exec turbo run test --filter=@bb/app
-- src/hooks/queries/host-file-preview-query.test.tsx
src/components/secondary-panel/ThreadSecondaryPanelTabContent.panelGate.test.tsx
src/components/plugin/PluginPanelRightPanelHost.test.tsx` reproduced the
findings: 5 failed and 13 passed. The failures showed the hidden initial
read, missing `isOpen` plumbing, media byte read, base64-before-lease
HTML work, and read-before-failed-lease ordering.
- After rebasing onto `origin/main` at `7f3d2ac66`, the same focused
command passed 3 files / 20 tests.
- `pnpm exec turbo run test --filter=@bb/app --force` passed 412 files /
3,162 tests, with 3 skipped.
- `pnpm exec turbo run typecheck build --filter=@bb/app` passed.
- `pnpm exec turbo run lint --filter=@bb/app` passed with 0 errors and
156 existing warnings outside the changed lines.

Post-merge follow-up to #2005; no matching issue.

> AGENT GENERATED: by GPT-5.6-Sol
ymichael added a commit that referenced this pull request Aug 20, 2026
## What was wrong

`FileDiffCard` rendered every pull-request file path as a live workspace
link whenever the thread had an environment, without considering
`file.status`. A removed file normally no longer exists in that
workspace, so its filename advertised a preview target guaranteed to
fail even though the separate diff control already exposed the deletion.
This confirms the [post-merge review
finding](#2005 (comment)).

## What changed

- Reused the existing non-interactive filename rendering when
`file.status` is `removed`, while retaining `experimental_FileLink` for
statuses whose path can exist.
- Added one focused thread-panel regression covering the complete
behavior: removed paths are not links, modified paths remain links, and
the removed file's accessible expand/collapse control still reveals its
diff.
- Kept the fix to the existing render condition; no abstraction, wire
change, protocol bump, CLI, guide, or documentation change was needed.

## How you verified

- Before the implementation change, `pnpm exec turbo run test
--filter=bb-plugin-github --force -- app.test.tsx` failed the new
regression with `removed.ts` rendered as `<a href="removed.ts">` (1
failed, 2 passed).
- After the change, that focused command passed all 3 tests.
- After rebasing onto current `origin/main`, `pnpm exec turbo run test
--filter=bb-plugin-github --force` passed 6 files / 25 tests.
- `pnpm exec turbo run typecheck --filter=bb-plugin-github` passed.
- `pnpm exec turbo run build --filter=bb-plugin-github` passed and built
the GitHub plugin's server and app artifacts.
- `pnpm exec prettier --check plugins/github/app.test.tsx` and `git diff
--check origin/main...HEAD` passed.

Fixes: no matching issue.

> AGENT GENERATED: by GPT-5.6-Sol
ymichael added a commit that referenced this pull request Aug 21, 2026
## What was wrong

PR #2005 made live thread-storage files resolve through
`useThreadStoragePaths({ limit: 1 })` even though the caller needs only
the authoritative storage root and host. The server therefore sent
`host.list_paths`, whose daemon handler recursively walks the complete
storage tree before applying the response limit. Merely opening a file
context menu or accepting an external-open request could perform large
filesystem work and add remote-host latency. This is the root cause
identified in [the post-merge
review](#2005 (comment));
#2086 lazy-loads the storage browser but does not change this lookup.

The first version of this PR added a direct location route, but review
found three incomplete consumers: the Docs file opener still recovered
the same metadata through `threads.get()` plus `storageFiles({ limit:
"1" })`, reconnect catch-up omitted the new location query-key prefix,
and the builtin plugin-authoring skill's exact SDK method map omitted
`storageLocation`.

## What changed

- Add `GET /api/v1/threads/:id/thread-storage/location` and
`sdk.threads.storageLocation()`. The server reuses its existing
authoritative `requireThreadStorageTarget` result and returns `{ hostId,
storageRootPath }` without issuing a host filesystem RPC.
- Resolve app live-file targets and Docs thread-storage open/save
operations from that direct location. Both preserve loading/error
behavior, absolute-path construction, validated relative paths,
`rootPath` confinement, and local-versus-remote host context without
listing storage.
- Include the location query in thread/environment cache ownership and
reconnect invalidation so an active failed lookup recovers when the
server socket reconnects.
- Add `storageLocation` to the builtin bb-plugin-authoring skill's exact
`threads` SDK method map.
- Add focused route, SDK/contract, query, cache-owner,
reconnect-recovery, Docs confinement, and resolver coverage. This is
internal transport for existing file-open behavior, not a new end-user
command or configuration surface, so no CLI/guide parity change is
needed.
- No host-daemon message changed, so `HOST_DAEMON_PROTOCOL_VERSION` is
unchanged.

This is the smallest complete fix because the server already knows the
host and derives the root from the active host session; exposing and
consistently consuming that existing result deletes the broad listing
dependency instead of optimizing or special-casing the recursive listing
API.

## How you verified

- Before the route existed, its new public-route regression failed with
`expected 404 to be 200`. Afterward, the focused server route passed
without reporting any host RPC response, proving the route does not
enumerate the filesystem.
- Before the Docs update, the storage-location-only regression failed
with `bb.sdk.threads.get is not stubbed`; afterward, the focused Docs
server file passed 32/32 while asserting the returned remote host and
confined root on read, preview, and CAS write.
- Before reconnect invalidation included the prefix, the active
failed-location regression remained `undefined` instead of recovering to
`loaded`; afterward, the focused cache-effect and cache-owner tests
passed 11/11.
- After rebasing onto `origin/main` at `e171ceba4`: `pnpm exec turbo run
build typecheck --filter=@bb/app --filter=@bb/server
--filter=bb-plugin-simple-notes` passed 10/10 tasks. A final post-commit
Turbo typecheck also passed.
- Forced full affected test matrix: app 414 files / 3,178 tests passed
(3 skipped); server 195 files / 1,817 tests passed; Docs 3 files / 66
tests passed.
- Earlier focused SDK and server-contract coverage passed 8/8 and 33/33
respectively; their full suites passed 96 and 58 tests.
- `pnpm exec turbo run lint --filter=@bb/app` passed with 0 errors and
155 existing warnings outside the changed lines.
- `git diff --check` passed.

Fixes: N/A — no matching issue; post-merge follow-up to #2005.

> AGENT GENERATED: by GPT-5.6-Sol
SawyerHood added a commit that referenced this pull request Aug 21, 2026
…still sync (#2144)

## What was wrong

Image links in the mobile app failed with a "Couldn't sync tabs" toast.
The links and the image routes were fine. A tap opens a
`host-file-preview` panel tab, and the app then syncs the tab strip with
`GET`/`PUT /threads/:id/tabs`. #2005 added `hostId` to
`host-file-preview` tabs with `.default(null)`, so the server now emits
`hostId: null` in every tabs response. Installed mobile builds bundle a
`.strict()` copy of `threadTabsResponseSchema` that predates the field,
so their SDK rejects the whole response (`Unrecognized key: hostId`),
the mutation errors, and the tab never settles. Phones cannot update in
step with the server, so the server must stay readable by the shipped
schema.

## What changed

- `apps/server/src/routes/threads/tabs.ts`: the GET and PUT responses
omit `hostId` when it is `null`, on `host-file-preview` tabs and on
`host-file-preview` file-opener owners of plugin-panel tabs. A non-null
`hostId` still travels. Current clients parse the omission back to
`null` through the schema default.
- `packages/server-contract`: added `ThreadTabsWireResponse` (`z.input`
of the response schema) and typed both tabs routes with it.

No daemon wire change, so no `HOST_DAEMON_PROTOCOL_VERSION` bump.

## How you verified

- New test in `apps/server/test/public/public-thread-tabs.test.ts` that
fails before the route change (`expected … to not have property
"hostId"`) and passes after it.
- `pnpm exec turbo run test --filter=@bb/server --
test/public/public-thread-tabs.test.ts`: 3/3 pass. `@bb/server-contract`
tests: 58/58 pass.
- `pnpm exec turbo run typecheck` for `@bb/server`, `@bb/sdk`,
`@bb/app`, `@bb/mobile`, `@bb/demo-server`: pass.
- Manual: paired the iOS simulator with the live `bee.getbb.app` server
on a current Metro bundle; storage and host-file image links open the
panel tab and the lightbox.

Fixes #

> AGENT GENERATED: by Claude Opus 5

Co-authored-by: Claude <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