Skip to content

fix(mattermost): truncate inbound preview on code-point boundary#101630

Merged
steipete merged 1 commit into
openclaw:mainfrom
hugenshen:fix/mattermost-monitor-utf16-truncation
Jul 9, 2026
Merged

fix(mattermost): truncate inbound preview on code-point boundary#101630
steipete merged 1 commit into
openclaw:mainfrom
hugenshen:fix/mattermost-monitor-utf16-truncation

Conversation

@hugenshen

@hugenshen hugenshen commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes garbled verbose inbound previews in Mattermost when message bodies contain emoji or other astral characters near the 200-character preview limit. The verbose debug path used raw bodyText.slice(0, 200), which can split a UTF-16 surrogate pair and leave a lone high surrogate in gateway logs.

Concrete example: A 205-code-unit message ${"a".repeat(199)}😀tail hits the preview cap inside the emoji. slice(0, 200) keeps the emoji's high surrogate (0xD83D) but drops its low surrogate, producing mojibake in mattermost inbound: … preview="…" debug lines.

Why This Change Was Made

Replaces bodyText.slice(0, 200) with truncateUtf16Safe(bodyText, 200) from openclaw/plugin-sdk/text-utility-runtime in the verbose inbound preview path (extensions/mattermost/src/mattermost/monitor.ts:1681).

User Impact

Before: Verbose Mattermost inbound logging can show broken previews for emoji-heavy messages.

After: Previews truncate on complete UTF-16 boundaries; emoji at the cap boundary are excluded rather than split.

Evidence

  • extensions/mattermost/src/mattermost/monitor.ts:27,1681–1683truncateUtf16Safe import and verbose preview wiring
  • packages/normalization-core/src/utf16-slice.ts — shared truncateUtf16Safe implementation
  • Diff: +52 / −3 in monitor.ts and monitor.inbound-system-event.test.ts

Behavior proof

1) Node runtime (truncateUtf16Safe vs raw slice, production helper)

Uses the exact test payload from monitor.inbound-system-event.test.ts:

=== #101630 Mattermost verbose preview UTF-16 proof ===
Node: v22.22.0
body length (code units): 205
raw slice(0,200) length: 200
raw slice ends on high surrogate: true
truncateUtf16Safe(200) length: 199
safe preview matches test expectation: true
safe preview excludes split emoji: true

2) Call-site regression through production monitor path

$ pnpm test extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts -t "keeps verbose inbound previews" --reporter=verbose

 ✓ mattermost inbound user posts > keeps verbose inbound previews on complete UTF-16 boundaries 62ms

 Test Files  1 passed (1)
      Tests  1 passed | 10 skipped (11)
   Duration  4.65s

[test] passed 1 Vitest shard in 8.37s

Regression flow (monitorMattermostProvider websocket posted event → verbose debug):

  1. Inbound message body = ${"a".repeat(199)}😀tail (len 205 code units).
  2. Verbose logger receives preview ${"a".repeat(199)} — emoji excluded, not split.
  3. Assertion: verboseDebug called with preview="${"a".repeat(199)}" (no lone surrogate).

Full touched file still green:

$ pnpm test extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts --reporter=verbose
 Tests  11 passed (11)

Proof gap: No redacted live Mattermost websocket capture with verbose logging enabled in this environment.

@openclaw-barnacle openclaw-barnacle Bot added channel: mattermost Channel integration: mattermost size: XS triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jul 7, 2026
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 8, 2026, 12:47 AM ET / 04:47 UTC.

Summary
The PR replaces Mattermost inbound verbose-preview raw slicing with truncateUtf16Safe and adds a monitor-path regression for an emoji boundary.

PR surface: Source +1, Tests +48. Total +49 across 2 files.

Reproducibility: yes. Current main and v2026.6.11 raw-slice bodyText at 200 UTF-16 code units, so a 199-ASCII prefix followed by an emoji reaches the lone-surrogate cut point.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦞 diamond lobster
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted live Mattermost verbose-log output or terminal transcript from a real setup, or ask a maintainer to apply a proof override for the latest head.
  • After adding proof, update the PR body so ClawSweeper re-reviews automatically; if it does not, ask a maintainer to comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides Node/helper output and a Vitest FakeWebSocket monitor regression, but no live Mattermost verbose-log output or proof: override label is present; live proof should be redacted before posting. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The external-PR proof gate remains unresolved unless a maintainer explicitly treats the monitor-path regression as a proof override or the contributor adds redacted live Mattermost verbose-log or terminal output.

Maintainer options:

  1. Decide the mitigation before merge
    Land the helper-based Mattermost logging fix after a maintainer proof override or redacted live Mattermost verbose-log proof confirms the safe preview on the latest head.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] No automated code repair is needed; the remaining action is a maintainer proof-sufficiency decision or contributor live proof.

Maintainer decision needed

  • Question: Should the existing maintainer-endorsed FakeWebSocket monitor regression count as a proof override for this external Mattermost verbose-log fix?
  • Rationale: ClawSweeper normally requires real setup proof for external PRs, but the patch is narrow and a maintainer already stated that the regression exercises the production logging path.
  • Likely owner: steipete — This handle already commented directly on proof sufficiency for this PR.
  • Options:
    • Accept the regression as proof override (recommended): Treat the maintainer-endorsed monitor-path regression as sufficient for this verbose-log-only fix and proceed through normal exact-head landing gates.
    • Require live Mattermost proof: Keep the proof gate active until the contributor adds redacted Mattermost verbose logs, terminal output, or another real runtime transcript showing the safe preview.
    • Pause until policy is settled: Do not merge until maintainers decide whether production-path test proof is enough for this logging-only edge case.

Security
Cleared: The diff imports an existing SDK text helper and updates a colocated Mattermost test, with no dependency, lockfile, workflow, package, secret, auth, permission, or executable supply-chain change.

Review details

Best possible solution:

Land the helper-based Mattermost logging fix after a maintainer proof override or redacted live Mattermost verbose-log proof confirms the safe preview on the latest head.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main and v2026.6.11 raw-slice bodyText at 200 UTF-16 code units, so a 199-ASCII prefix followed by an emoji reaches the lone-surrogate cut point.

Is this the best way to solve the issue?

Yes for the code shape. Using the existing SDK UTF-16-safe truncation helper at the Mattermost verbose-preview site is the narrowest maintainable fix; the remaining question is proof sufficiency, not patch correctness.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against bdc1ce8dede0.

Label changes

Label justifications:

  • P3: This is a low-blast-radius Mattermost verbose-log formatting fix with no delivery, config, auth, migration, or persisted-state change.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦞 diamond lobster.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides Node/helper output and a Vitest FakeWebSocket monitor regression, but no live Mattermost verbose-log output or proof: override label is present; live proof should be redacted before posting. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +1, Tests +48. Total +49 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 2 1 +1
Tests 1 50 2 +48
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 52 3 +49

What I checked:

Likely related people:

  • steipete: Already commented that the websocket monitor regression exercises the real production logging path and pushed a maintainer fixup during this PR's review history. (role: proof-sufficiency commenter; confidence: high; commits: 14e788345cd2; files: extensions/mattermost/src/mattermost/monitor.ts, extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts)
  • damoahdominic: The raw-slice verbose preview appears in the Mattermost extension extraction associated with feat: add Mattermost channel support #1428. (role: introduced Mattermost monitor path; confidence: medium; commits: 495a39b5a989; files: extensions/mattermost/src/mattermost/monitor.ts)
  • joshavant: Current-main blame for the monitor file points to a recent full-file Mattermost monitor carry-forward commit, so this is a useful routing signal but not root-cause proof. (role: recent area contributor; confidence: medium; commits: 75e3aa293adc; files: extensions/mattermost/src/mattermost/monitor.ts)
  • vincentkoc: Current main is at a plugin-localization PR by this handle and the latest release commit also carried the Mattermost monitor raw-slice behavior. (role: recent plugin-area contributor; confidence: medium; commits: bdc1ce8dede0, e085fa1a3ffd; files: extensions/mattermost/src/mattermost/monitor.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (9 earlier review cycles; latest 8 shown)
  • reviewed 2026-07-07T14:28:34.942Z sha dee2062 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-07T14:53:21.011Z sha 72d2857 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-07T15:07:54.537Z sha e7889fe :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-07T15:23:14.644Z sha 42edba3 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-07T15:54:13.524Z sha b1f504f :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T02:26:47.166Z sha 569e61e :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T04:00:08.730Z sha 569e61e :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T04:07:38.758Z sha 569e61e :: needs real behavior proof before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. labels Jul 7, 2026
@hugenshen hugenshen changed the title ## fix(fal): bound music-generation success response JSON reads fix(mattermost): truncate inbound preview on code-point boundary Jul 7, 2026
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jul 7, 2026
@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from a9d040b to dee2062 Compare July 7, 2026 14:18
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: S and removed size: XS labels Jul 7, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 7, 2026
@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from dee2062 to 72d2857 Compare July 7, 2026 14:33
@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label Jul 7, 2026
@hugenshen

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Squashed to one commit, removed scripts/proof-mattermost-inbound-preview-bound.mjs from the branch, and refreshed PR body evidence to use focused Vitest output only.

@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 7, 2026
@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from e7889fe to 42edba3 Compare July 7, 2026 15:15
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts and removed scripts Repository scripts labels Jul 7, 2026
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Maintainer fixup pushed at 14e788345cd248a898c5b0beef305f72f68782ee.

The websocket monitor regression already exercises the real production logging path, so I removed the later standalone proof script, which only duplicated the truncation expression and helper import. The runtime fix and production-path regression are unchanged.

I am running the native exact-head landing sequence now. Please hold further head updates until it completes so GitHub does not cancel the reviewed CI run.

@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from 14e7883 to b1f504f Compare July 7, 2026 15:43
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from b1f504f to 569e61e Compare July 8, 2026 02:15
@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@hugenshen
hugenshen force-pushed the fix/mattermost-monitor-utf16-truncation branch from 569e61e to 1fb6e28 Compare July 8, 2026 04:34
@hugenshen

Copy link
Copy Markdown
Contributor Author

Maintainer fixup pushed at 14e788345cd248a898c5b0beef305f72f68782ee.

The websocket monitor regression already exercises the real production logging path, so I removed the later standalone proof script, which only duplicated the truncation expression and helper import. The runtime fix and production-path regression are unchanged.

I am running the native exact-head landing sequence now. Please hold further head updates until it completes so GitHub does not cancel the reviewed CI run.

Hi @steipete — branch is now rebased on latest main (head: 1fb6e28, one commit). I won't push again until you decide.

The proof gate is still showing needs proof. You already confirmed the posted-event regression exercises the real production logging path. Could you either apply the proof: override label or complete the landing sequence from this head? I'll hold here.

@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Land-ready at exact head 1fb6e289021726c61cec6db3616f406b24d97af8.

  • The contributor's final force-updated head is byte-identical on both changed files to the previously reviewed Mattermost fixup.
  • Regression coverage drives the real websocket posted event through monitorMattermostProvider with verbose logging enabled and asserts the exact 199-character preview prefix.
  • Fresh exact-head structured autoreview: clean, confidence 0.99.
  • git diff --check: pass.
  • Exact-head CI 28917750123, OpenGrep 28917750139, and CodeQL 28917750140: success.
  • Native review artifacts validated and OPENCLAW_TESTBOX=1 scripts/pr prepare-run 101630 passed the hosted exact-head gate.

Known proof gap: no live Mattermost server capture; the production monitor/websocket path is covered in the focused regression test. No ingress, auth, routing, config, or message-body behavior changes.

@steipete
steipete merged commit f77c2aa into openclaw:main Jul 9, 2026
94 checks passed
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

steipete added a commit that referenced this pull request Jul 9, 2026
* fix(matrix): truncate inbound preview on UTF-16 code-point boundary

The matrix inbound verbose preview used bodyText.slice(0, 200) before logging.
When a supplementary-plane character (emoji, extended CJK) straddles the 200th
code unit, the slice splits its surrogate pair and emits a lone surrogate into
the verbose log line. Lone surrogates corrupt JSON log serialization, break
terminal rendering, and crash UTF-8-validating log shippers.

Use the shared truncateUtf16Safe helper (already used by the mattermost sibling
monitor after #101630) to keep complete surrogate pairs intact. No behavior
change for ASCII input; the preview is still capped at 200 code units.

Co-Authored-By: Claude <[email protected]>

* test(matrix): fix inbound-preview test for lint and lib target

- Avoid String.prototype.isWellFormed (ES2024 lib): use the encodeURIComponent
  well-formedness probe instead, which works on the project's TS lib target.
- Replace literal string concatenation with template strings to satisfy the
  no-useless-concat lint rule. No behavior change in the assertions.

Co-Authored-By: Claude <[email protected]>

* fix(channels): keep inbound log previews UTF-16 safe

* test(line): type verbose preview fixtures

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 9, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 9, 2026
* fix(matrix): truncate inbound preview on UTF-16 code-point boundary

The matrix inbound verbose preview used bodyText.slice(0, 200) before logging.
When a supplementary-plane character (emoji, extended CJK) straddles the 200th
code unit, the slice splits its surrogate pair and emits a lone surrogate into
the verbose log line. Lone surrogates corrupt JSON log serialization, break
terminal rendering, and crash UTF-8-validating log shippers.

Use the shared truncateUtf16Safe helper (already used by the mattermost sibling
monitor after openclaw#101630) to keep complete surrogate pairs intact. No behavior
change for ASCII input; the preview is still capped at 200 code units.

Co-Authored-By: Claude <[email protected]>

* test(matrix): fix inbound-preview test for lint and lib target

- Avoid String.prototype.isWellFormed (ES2024 lib): use the encodeURIComponent
  well-formedness probe instead, which works on the project's TS lib target.
- Replace literal string concatenation with template strings to satisfy the
  no-useless-concat lint rule. No behavior change in the assertions.

Co-Authored-By: Claude <[email protected]>

* fix(channels): keep inbound log previews UTF-16 safe

* test(line): type verbose preview fixtures

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
ceckert added a commit to ceckert/openclaw that referenced this pull request Jul 14, 2026
…hreadDirectMessages

The 7.2 cherry-pick resolved extensions/mattermost/src/mattermost/monitor.ts by
taking our side wholesale, silently reverting six upstream commits landed between
7.1 and 7.2. Adopt them instead of clobbering:

  f77c2aa  truncate the inbound preview on a code-point boundary (openclaw#101630)
  b7ab62c  preserve text-block boundaries in the draft preview (openclaw#87449)
  da6a24d  prevent hung pre-delivery hooks from blocking lanes (openclaw#104256)
  218dcd8  noUncheckedIndexedAccess in the extensions lane (openclaw#105132)
  9cdf166  trim the monitor gating facade (openclaw#106021)

70a7e6e (opt-in DM threading by chat type) was already in the impl; three 7.1-era
assertions in monitor.test.ts that upstream had superseded move to upstream's
semantics (the implementations are byte-identical to upstream).

b7ab62c's 173-line monitor hunk (block-preview boundary machinery) was hand-ported
onto our activity/warm-fallback interleaving; the block-preview code is now
byte-identical to pristine 7.2.

threadDirectMessages kept as a thin compat feeding upstream's replyToModeByChatType,
because Dockerfile.base's shared config probe still writes it and the probe spans
matrix bases older than 2026.7.2. Retire both together later.

Mattermost suite 630/632. The 2 remaining timeouts are localized to
deliverMattermostReplyWithDraftPreview: with a mocked-rejecting updateMattermostPost,
our patched buildFinalEdit attempts the in-place edit (pristine skips it), the edit
rejects, and the deliverNormally fallback awaits the unmocked deliverMattermostReplyPayload
which never resolves under the test's bare sendMessageMattermost stub. Behavioral
divergence in our activity/warm-fallback finalize path, not the upstream port. TODO.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: mattermost Channel integration: mattermost P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants