Skip to content

feat(desktop): add team sharing to community catalog - #3995

Open
wpfleger96 wants to merge 1 commit into
duncan/team-catalog-backendfrom
duncan/team-catalog-desktop
Open

feat(desktop): add team sharing to community catalog#3995
wpfleger96 wants to merge 1 commit into
duncan/team-catalog-backendfrom
duncan/team-catalog-desktop

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 31, 2026

Copy link
Copy Markdown
Member

Stack: #5112 → this PR

Stacks on the team catalog backend PR (#5112). Contains all desktop/src/**, desktop/tests/**, and desktop/playwright.config.ts changes — 30 files, +4,276/−985 lines.

TS parse layer

teamCatalogRelay.ts decodes kind 30178 projection events into TeamCatalogEntry records. catalogRelay.ts generalizes the persona catalog relay into a shared base that both persona and team catalog hooks extend, eliminating the per-kind duplication in the previous persona-only implementation.

Hooks

useTeamCatalogRelay.ts subscribes to the team catalog kind, applies freshness ordering, and exposes the decoded entries. useAgentsDataRefresh.ts gains team catalog invalidation alongside the existing persona refresh path.

CommunityCatalogDialog

Single unified surface replacing the two former separate dialogs. Agents and Teams appear as labeled sections; the selection helper (communityCatalogSelection.ts) handles preferred-loading, vanished-item repair (explicit selection cleared when the encoded key leaves live data), and cross-section auto-init. All selection transitions are covered by 15 deterministic unit tests in communityCatalogSelection.test.mjs.

TeamShareDialog

Publishes and unshares catalog entries via the backend set_team_shared Tauri command (both share and unshare transitions call set_team_shared).

e2e + screenshots

team-catalog.spec.ts covers the full browse + adopt flow. team-catalog-screenshots.spec.ts produces the 8-image pixel-regression set (comment 5208042093).

Deletion

PersonaCatalogDialog.tsx is removed; persona browsing now lives inside the unified CommunityCatalogDialog.

Follow-ups

A post-merge PR will extend the shared agent-definition text-safety policy (isSafeAgentDefinitionText — Unicode-control/bidi/zero-width rejection, length bounds) to the team surface: team create/update/adopt/projection parse and member display_name/system_prompt fields, backend plus the frontend memberPassesV1 path. Persona definitions already carry this via #4220; teams currently validate only byte bounds, so this closes the parity gap.

@wpfleger96
wpfleger96 requested a review from a team as a code owner July 31, 2026 19:36
wpfleger96 pushed a commit that referenced this pull request Jul 31, 2026
wpfleger96 pushed a commit that referenced this pull request Jul 31, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 5c11456 to 8bd3c8e Compare July 31, 2026 20:18
wpfleger96 pushed a commit that referenced this pull request Jul 31, 2026
wpfleger96 pushed a commit that referenced this pull request Jul 31, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 28c614f to a4c603a Compare July 31, 2026 21:32
wpfleger96 pushed a commit that referenced this pull request Jul 31, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch 6 times, most recently from 8ed79f3 to f573bdc Compare August 4, 2026 18:53

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewing on Wes's behalf. Requesting changes for two security blockers in the catalog review surface:

  1. Verify relay events before they can affect paging, coordinate selection, attribution, or displayed content. fetchCatalogEvents currently inserts every raw RelayEvent returned by the socket into byId and derives oldestCreatedAt from it; sharedCatalogHeads then trusts the raw id, pubkey, created_at, tags, and content. RelayClient.fetchEvents is only a transport fetch and does not verify signatures. A forged/unsigned 30178 event can therefore appear under another publisher, claim or retract their coordinate ahead of a valid signed head, and control the pagination cursor. The backend re-fetch and verification correctly prevents adoption, but it does not protect browsing, publisher attribution, remote image loads, or denial/shadowing of legitimate entries. Please verify ID + signature on a fresh wire-shaped object before adding an event to the paging map or deriving a cursor/head from it, and add regressions for a forged newer head, forged author/content, malformed signature, and an invalid event attempting to control until. The equivalent hardening and tests already exist in PR #4220 for kind 30175; this shared helper should preserve that invariant for both 30175 and 30178 rather than regress it.

  2. Render executable team/member instructions byte-for-byte, not as Markdown, and reject non-reviewable controls at the adoption boundary. TeamCatalogDialog.tsx passes both team.instructions and member.systemPrompt through the chat Markdown renderer. Markdown can conceal spoiler bodies, link destinations, and image sources, so the instructions reviewed in the dialog can differ materially from the plaintext later executed by the adopted agents. The current team catalog validators only enforce schema/size bounds, so bidi/default-ignorable controls can also make reviewed text differ from execution. Please use the literal instruction-review component/invariant from PR #4220 and apply its executable-text validation to team instructions and every member prompt before publication/adoption. Add coverage proving Markdown syntax stays literal, hidden controls fail closed, and accepted bytes are preserved exactly through adoption.

The feature has unusually substantial coverage otherwise: this head adds/changes 62 test/spec/fixture files, including the Rust adoption/pending/sharing/reconcile suites, TS relay/parser tests, and Playwright catalog/screenshot specs. CI is green at e64aa20ff81e236ca068aab3b9ad93142fb839e3, but the existing fixtures use placeholder sig: "sig" values and therefore do not exercise the first trust boundary.

@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from e64aa20 to 131a906 Compare August 6, 2026 16:39
wpfleger96 pushed a commit that referenced this pull request Aug 6, 2026
@wpfleger96

Copy link
Copy Markdown
Member Author

Community Catalog — browse (teams section)

Team list with name, member count, description, and "Add team" footer.
catalog-browse

Community Catalog — member expanded

Per-member row expanded showing Type, Preferred model/runtime/provider, and agent instruction.
catalog-member-expanded

Community Catalog — team instructions

Team instructions section collapsed above member rows.
catalog-team-instructions

Community Catalog — already added

Footer button reads "Added to my teams" (disabled) after the team is adopted.
catalog-added

Community Catalog — empty state

Both agents and teams empty: "Nothing shared yet" placeholder.
catalog-empty

Community Catalog — both sections

Agents and Teams section headers both visible with one shared entry each.
catalog-both-sections

Share dialog — not yet shared

Share dialog before enabling catalog access.
share-not-shared

Share dialog — published

Share dialog with catalog toggle on and published toast.
share-published

wpfleger96 pushed a commit that referenced this pull request Aug 6, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 3bcca7e to f869c80 Compare August 6, 2026 21:10
@wpfleger96
wpfleger96 changed the base branch from main to duncan/team-catalog-backend August 6, 2026 21:10
@wpfleger96 wpfleger96 changed the title feat(desktop): implement 30178 team catalog sharing for the community catalog feat(desktop): add community catalog UI, team share dialog, and e2e tests Aug 6, 2026
@wpfleger96 wpfleger96 changed the title feat(desktop): add community catalog UI, team share dialog, and e2e tests feat(desktop): add team sharing to community catalog Aug 6, 2026
wpfleger96 added a commit that referenced this pull request Aug 12, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- Rust tests: team_catalog/tests.rs (999 lines), adopt/tests.rs (999),
  pending/tests.rs (679), event_sync_team_catalog_tests.rs (436)
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from 0acacf4 to 16a1ff3 Compare August 12, 2026 14:50
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from f869c80 to 72c1491 Compare August 12, 2026 14:52
wpfleger96 added a commit that referenced this pull request Aug 12, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- Rust tests: team_catalog/tests.rs (999 lines), adopt/tests.rs (999),
  pending/tests.rs (679), event_sync_team_catalog_tests.rs (436)
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from 16a1ff3 to e5e5c2a Compare August 12, 2026 15:48
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 72c1491 to dc6f777 Compare August 12, 2026 15:48
wpfleger96 added a commit that referenced this pull request Aug 14, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- Rust tests: team_catalog/tests.rs (999 lines), adopt/tests.rs (999),
  pending/tests.rs (679), event_sync_team_catalog_tests.rs (436)
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from e5e5c2a to 5cce7c6 Compare August 14, 2026 15:20
wpfleger96 added a commit that referenced this pull request Aug 14, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- Rust tests: team_catalog/tests.rs (999 lines), adopt/tests.rs (999),
  pending/tests.rs (679), event_sync_team_catalog_tests.rs (436)
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from 5cce7c6 to 728ae20 Compare August 14, 2026 19:38
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch 2 times, most recently from 5362227 to 96335ec Compare August 14, 2026 20:41
wpfleger96 added a commit that referenced this pull request Aug 18, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- Rust tests: team_catalog/tests.rs (999 lines), adopt/tests.rs (999),
  pending/tests.rs (679), event_sync_team_catalog_tests.rs (436)
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from 728ae20 to 9a086df Compare August 18, 2026 16:05
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 96335ec to 7629469 Compare August 18, 2026 16:17
wpfleger96 added a commit that referenced this pull request Aug 18, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- persona_events.rs: domination-aware flush for kind:5 tombstones. A
  tombstone is signed strictly past the future-dated head it retracts,
  so its retained created_at is the domination floor. The relay ingest
  gate rejects events beyond ±900s of server time, so a byte-frozen
  future-dated replay can age out of the acceptance window and strand
  the head live forever. Flush now re-dates to now when the floor has
  passed, publishes at the floor when it is within the window, and
  leaves the tombstone pending to converge when the floor is further
  ahead than the window — never emitting an event the relay rejects.
- Rust tests: team_catalog/tests.rs, adopt/tests.rs, pending/tests.rs
  (incl. gate tests driving both the 30176 and 30178 tombstones through
  a stub relay that enforces the real ±900s ingest gate, covering the
  delayed/offline-retry case), event_sync_team_catalog_tests.rs
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from 9a086df to a66f7fa Compare August 18, 2026 23:55
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 7629469 to 58d52a5 Compare August 19, 2026 00:04
wpfleger96 added a commit that referenced this pull request Aug 19, 2026
Implements the backend for 30178 team catalog sharing on the community
catalog. No UI callers yet — this PR is the backend half of a two-PR
split; desktop PR #3995 (stacked here) adds the TS parse layer, hooks,
CommunityCatalogDialog, and e2e.

Changes:
- team_catalog.rs: 30178 projection builder + size contracts (32 KiB
  ceiling, per-field bounds, avatar downscaling for raster data URLs);
  build_team_catalog_event/content, team_catalog_content_from_event,
  tombstone_team_catalog_coordinate
- commands/teams/pending.rs: publish/unshare/tombstone commands with
  relay-scoped share state; refresh_or_retract_shared_head_at for
  immediate retraction on member edits that exceed the size contract;
  persona-edit refresh guard so unrelated personas are never embedded
- commands/teams/adopt/: add_team_from_catalog with backend head
  verification + byte-level rollback across both stores; plan_add with
  full member provenance (owner, d-tag, member-key, projection-hash);
  builtin reuse via hint matching; commit_stores atomic writer
- event_sync.rs: reconcile_team_catalog_heads_at startup reconcile;
  republish changed heads, tombstone unrebuildable ones, skip unshared;
  multi-head continuation so a single pass handles all shared teams
- persona_events.rs: domination-aware flush for kind:5 tombstones. A
  tombstone is signed strictly past the future-dated head it retracts,
  so its retained created_at is the domination floor. The relay ingest
  gate rejects events beyond ±900s of server time, so a byte-frozen
  future-dated replay can age out of the acceptance window and strand
  the head live forever. Flush now re-dates to now when the floor has
  passed, publishes at the floor when it is within the window, and
  leaves the tombstone pending to converge when the floor is further
  ahead than the window — never emitting an event the relay rejects.
- Rust tests: team_catalog/tests.rs, adopt/tests.rs, pending/tests.rs
  (incl. gate tests driving both the 30176 and 30178 tombstones through
  a stub relay that enforces the real ±900s ingest gate, covering the
  delayed/offline-retry case), event_sync_team_catalog_tests.rs
- 27 shared JSON parity fixtures (team_catalog_content/)
- Existing TeamRecord + AgentDefinition types extended with sharing
  fields (team_catalog_source, catalog_source, shared flag, etc.)
- All commands registered in lib.rs; dormant until UI PR merges

Stack: this PR -> #3995
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-backend branch from a66f7fa to 6a57da7 Compare August 19, 2026 00:44
Extend the unified add-agent dialog (#5015) into a single Community Catalog
surface that browses both shared agents and shared teams. The dialog keeps
sections for personas and teams with type-tagged selection and a
teams-preferred launch. TeamsSection's discover entry and the new-agent card
both open this one dialog.

Teams are decoded from kind 30178 projection events by teamCatalogRelay.ts.
Signature verification, shared-tag gating, and coordinate collapse live in the
shared catalogRelay.ts seam, so both the persona and team paths reject
unsigned or forged heads before projecting. Instruction review renders verbatim
in a <pre> on all three surfaces (persona, team, and member instructions) so
the text a user reviews is the text sent to the agent. TeamShareDialog
publishes and unshares team catalog entries.

Playwright e2e covers the unified create/catalog/import navigation, the teams
catalog flow, and the screenshot regression set.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/team-catalog-desktop branch from 58d52a5 to 76da912 Compare August 19, 2026 00:54
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.

2 participants