Skip to content

feat: bug report platform and multiple screenshots - #10064

Open
alejandro-jimenez-dcl wants to merge 4 commits into
devfrom
feat/bug-report-platform-and-multiple-screenshots
Open

feat: bug report platform and multiple screenshots#10064
alejandro-jimenez-dcl wants to merge 4 commits into
devfrom
feat/bug-report-platform-and-multiple-screenshots

Conversation

@alejandro-jimenez-dcl

@alejandro-jimenez-dcl alejandro-jimenez-dcl commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Pull Request Description

What does this PR change?

  • Platform. Every bug report ticket now fills the Bug Report type's Platform dropdown in Intercom. Explorer always reports Desktop; the proxy translates the code into Intercom's option.
  • Screenshot in the new proxy format. The attached screenshot travels in the evidence format the updated proxy expects. A screenshot over 3 MB is dropped from the ticket with a warning in the logs, and the ticket is still filed without it.
  • Submit label. The SUBMIT text now fades together with the button while the form is incomplete. Before, only the button background dimmed and the text stayed fully white.

Requires the proxy change to be merged and deployed first

https://dcl.tools/ops/ops-lambdas/-/merge_requests/423 teaches the Intercom proxy the Platform attribute and the new evidence format. Against the proxy currently deployed, every ticket from this branch fails with a validation error because Platform is an unknown attribute. Merge order: proxy first, then this PR.

Test Instructions

Prerequisites

  • The proxy MR above is merged and deployed to the environment you test against.
  • The bug report feature flag is enabled for your account.
  • Have a PNG or JPG image under 3 MB at hand, plus one bigger than 3 MB.

Test Steps

Report with a screenshot

  1. Sign in and open the bug report form from the sidebar button (it is also reachable from the sign-in screen and the loading screen).
  2. Pick an issue type and write a short description.
  3. Click the attach button and choose the small image. A thumbnail appears with a remove button next to it.
  4. Submit.
  5. Expected: the success screen shows. In Intercom, the new ticket has Platform set to Desktop and the screenshot inlined at the end of the description.

Report without a screenshot

  1. Open the form, fill the issue type and description, do not attach anything.
  2. Submit.
  3. Expected: the ticket is created with Platform set to Desktop and no image.

Oversized screenshot

  1. Open the form, fill it in and attach the image bigger than 3 MB.
  2. Submit.
  3. Expected: the ticket is created without the image. Nothing fails or hangs on the client side.

Submit button state

  1. Open the form with the description empty.
  2. Expected: both the SUBMIT button and its text look dimmed. Once the issue type and description are filled, both come back to full color.

Additional Testing Notes

  • Removing the screenshot, closing the form and reopening it must leave no stale thumbnail.
  • A ticket created with a broken image link would mean the proxy upload budget was exceeded and the client did not drop the image first.

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered
  • For SDK features: Test scene is included

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does. Especially useful for first-time contributors.

The submit button's color tint only affects its background, so the
SUBMIT text stayed fully white while the form was incomplete. The view
now toggles the label alpha together with the button's interactable
state.
… ticket

The Intercom proxy now takes a "Platform" list code (0 = Desktop; Explorer
has no mobile build) and an "evidence" array of up to three images instead
of a single object. Each image stays under the 3 MB per-image cap and the
set shares a 3 MB budget so the base64 body stays below the proxy's 4.5 MB
limit; images that would trip a cap are dropped from the ticket with a
warning, in attachment order.

The form keeps a list of attached screenshots rendered through
BugReportScreenshotSlot entries on the view; the attach button hides once
the slots are full. Sentry still receives the first image only, since a
feedback envelope carries a single attachment.

Requires the proxy branch feat/intercom-platform-and-multiple-screenshots
to be deployed first: the current proxy rejects the unknown "Platform"
attribute.
The proxy takes 1 for Desktop and 2 for Mobile and rejects 0, the value an
unassigned C# enum serializes to, so it never files as Desktop by accident.
The client enum was numbered 0/1 and every ticket would have been a 400.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

🚦 CI Status

Build

Windows and Mac built successfully in Unity Cloud.

Name Links & timing
Build ca4b884 · Logs · built 2026-09-14T08:29:56Z
Windows GitHub job · Unity Cloud #2 · Unity log · ⏱ 26m 24s build + 15m 6s queue · Download .zip · .zip via S3
Mac GitHub job · Unity Cloud #2 · Unity log · ⏱ 25m 40s build + 4m 2s queue · Download .zip · .zip via S3

Lint

Warnings not reduced: 12025 => 12027 — remove at least 3 warnings to merge.

No warnings in files changed by this PR — showing general ones you can remove to unblock (50 of 12027)
Assets/DCL/MarketplaceCredits/Purchase/UI/CreditPurchaseModalController.cs:685  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Rendering/GPUInstanceBatcher/ComputeShaders/DrawArgsInstanceCountTransfer.compute:1  CppUnusedIncludeDirective  Possibly unused #include directive
../render-features/AvatarOutline/RenderFeature_AvatarOutline.RenderPass_OutlineDraw.cs:75  RedundantArgumentDefaultValue  The parameter 'flags' has the same default value
Assets/DCL/Communities/CommunitiesCard/Events/EventListController.cs:156  RedundantArgumentDefaultValue  The parameter 'parcelToTeleport' has the same default value
Assets/DCL/Events/EventCardActionsController.cs:93  RedundantArgumentDefaultValue  The parameter 'parcelToTeleport' has the same default value
Assets/DCL/Friends/UI/FriendPanel/Sections/Friends/FriendListSectionUtilities.cs:57  RedundantArgumentDefaultValue  The parameter 'parcelToTeleport' has the same default value
Assets/DCL/Places/PlacesCardSocialActionsController.cs:183  RedundantArgumentDefaultValue  The parameter 'parcelToTeleport' has the same default value
../render-features/ObjectHighlight/RenderFeature_ObjectHighlight.RenderPass_DrawObjects.cs:226  RedundantArgumentDefaultValue  The parameter 'shaderPassId' has the same default value
../render-features/ObjectHighlight/RenderFeature_ObjectHighlight.RenderPass_DrawObjects.cs:236  RedundantArgumentDefaultValue  The parameter 'shaderPassId' has the same default value
Assets/DCL/Chat/_Refactor/ChatReactions/Tests/LocalPlayerWorldReactorShould.cs:141  RedundantArgumentDefaultValue  The parameter 'walletId' has the same default value
Assets/DCL/Chat/_Refactor/ChatReactions/Tests/LocalPlayerWorldReactorShould.cs:156  RedundantArgumentDefaultValue  The parameter 'walletId' has the same default value
Assets/DCL/Chat/_Refactor/ChatReactions/Tests/LocalPlayerWorldReactorShould.cs:171  RedundantArgumentDefaultValue  The parameter 'walletId' has the same default value
Assets/DCL/MapRenderer/MapLayers/HomeMarker/HomeMarkerController.cs:135  RedundantArgumentDefaultValue  The parameter 'worldName' has the same default value
Assets/DCL/Infrastructure/ECS/Unity/Materials/Tests/CreateBasicMaterialSystemShould.cs:76  RedundantAssignment  The value passed to the method is never used because it is overwritten in the method body before being read
Assets/DCL/Infrastructure/ECS/Unity/Materials/Tests/CreatePBRMaterialSystemShould.cs:85  RedundantAssignment  The value passed to the method is never used because it is overwritten in the method body before being read
Assets/DCL/RealmNavigation/RetrieveSceneFromFixedRealm.cs:46  RedundantAssignment  The value passed to the method is never used because it is overwritten in the method body before being read
Assets/DCL/AvatarRendering/Emotes/Editor/EmbeddedEmotesEditor.cs:37  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/AvatarRendering/Emotes/Editor/EmbeddedEmotesEditor.cs:38  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Backpack/AvatarSection/Outfits/OutfitsPresenter.cs:193  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Chat/History/ChatHistoryEncryptor.cs:25  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/ECS/Unity/Materials/Systems/StartMaterialsLoadingSystem.cs:167  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Global/Editor/DebugSettingsDrawer.cs:73  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Global/Editor/RealmLaunchSettingsDrawer.cs:248  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/BoxFactory.cs:64  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/BoxFactory.cs:100  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/BoxFactory.cs:136  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/BoxFactory.cs:172  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/BoxFactory.cs:219  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/Utility/Primitives/CylinderVariantsFactory.cs:137  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Landscape/Jobs/NoiseJob.cs:68  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/PluginSystem/Global/VoiceChatDebugContainer.cs:164  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/SDKComponents/AudioAnalysis/AudioAnalysisSystem.cs:75  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/SDKComponents/AvatarAttach/Systems/AvatarAttachHandlerSystem.cs:87  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Translation/Processors/AngleBracketSegmentationRule.cs:29  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Translation/Processors/AngleBracketSegmentationRule.cs:38  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs:52  RedundantBaseConstructorCall  Redundant base constructor call
../render-features/SkyboxEnvironmentProbe/SkyboxToCubemapRendererPass.cs:259  RedundantCast  Type cast is redundant
../render-features/SkyboxEnvironmentProbe/SkyboxToCubemapRendererPass.cs:310  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:154  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:155  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:156  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:157  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:158  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/FinishAvatarMatricesCalculationSystemShould.cs:159  RedundantCast  Type cast is redundant
Assets/DCL/AvatarRendering/Wearables/Helpers/WearablePolymorphicBehaviour.cs:135  RedundantCast  Type cast is redundant
Assets/DCL/MarketplaceCredits/Purchase/Tests/CreditsTopUpServiceShould.cs:36  RedundantCast  Type cast is redundant
Assets/DCL/SDKComponents/AvatarAttach/Tests/AvatarAttachHandlerSystemShould.cs:588  RedundantCast  Type cast is redundant
Assets/DCL/SDKComponents/Billboard/Tests/BillboardTest.cs:173  RedundantCast  Type cast is redundant
Assets/DCL/SDKComponents/MediaStream/YouTube/HlsManifestBuilder.cs:191  RedundantCast  Type cast is redundant
Assets/DCL/SpringBones/SpringBoneService.cs:185  RedundantCast  Type cast is redundant

Lint run · took 21m 30s

Tests

All Unity tests passed ✅

TESTS SUITE Result Passed Failed Skipped Tests time Job time
EditMode ✅ Passed 25864 0 13 4m 25s 15m 2s
PlayMode ✅ Passed 248 0 37 40s 9m 44s

Tests time sums the test cases; Job time is the job's wall clock including checkout, licensing and asset import.

Slowest tests
  • [editmode] 16.5s DCL.AuthenticationScreenFlow.Tests.ProfileFetchingAuthStateShould.CancelStalledFetchOnTimeout
  • [editmode] 15.3s DCL.Tests.Editor.ValidationTests.CheckForDebugUsage
  • [editmode] 11.0s DCL.Tests.Editor.ValidationTests.CheckUnityObjectsForMissingReferences
  • [editmode] 10.0s DCL.Notifications.Tests.NotificationsRequestControllerShould.ReuseSingleListInstanceAcrossPollIterations
  • [editmode] 5.0s DCL.Friends.Tests.FriendsConnectivityStatusTrackerShould.RaiseOnlineEventWhenSameStatusIsRebroadcastAfterReset
  • [editmode] 5.0s CrdtEcsBridge.WorldSynchronizer.Tests.CrdtWorldSynchronizerShould.ThrowIfSyncBufferIsAlreadyRented
  • [editmode] 4.8s DCL.Tests.Editor.ValidationTests.SettingsAreValid
  • [editmode] 4.3s SceneRunner.Tests.SceneFacadeShould.ContinueUpdateLoopOnBackgroundThread(180,4000)
  • [editmode] 4.1s SceneRunner.Tests.SceneFacadeShould.ContinueUpdateLoopOnBackgroundThread(90,4000)
  • [editmode] 4.1s SceneRunner.Tests.SceneFacadeShould.ContinueUpdateLoopOnBackgroundThread(10,4000)
  • [playmode] 4.2s Global.Tests.PlayMode.CubeWaveSceneShould.EmitECSComponents
  • [playmode] 2.6s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.ContinuousTweensRunIndefinitelyWhenDurationIsZero
  • [playmode] 2.3s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TextureMoveSequenceUpdatesMaterial
  • [playmode] 2.1s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.TextureMoveContinuousOffsetCompletesAndUpdatesMaterial
  • [playmode] 2.0s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.MoveContinuousMovesAndCompletesAfterDuration
  • [playmode] 1.9s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.RotateContinuousCompletesAfterDuration
  • [playmode] 1.9s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TweenSequenceWithoutLoopCompletesOnce
  • [playmode] 1.5s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TweenSequenceWithMoveRotateScaleWithOmittedScale_ResolvesScaleFromCurrentTransform
  • [playmode] 1.5s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.RotateContinuousPositiveAndNegativeYDirectionsAreOpposite
  • [playmode] 1.4s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TweenSequenceCompletesAllTweens

Full report: run summary · results + editor logs: editmode · playmode

Performance

🏁 Bare-metal benchmark finished — run #34823074175.

Full report

PR #10064, run #34823074175

Overall: ✅ no significant changes

Builds: Windows change, Windows baseline, macOS change, macOS baseline

How to read this table
  • Each build is measured 3 times, interleaved with the other build (change, baseline, change, baseline, ...) in the same session, so both see the same world content and machine state. The values are the median, and (min–max) is the lowest and highest of those runs.
  • Δ is Change minus Baseline (a negative Δ means Change is faster).
  • 🟢 faster / 🔴 slower — a difference that passed every check: the runs are fully separated (every run of one build faster than every run of the other), and the median difference is at least 3% and at least 0.5 ms.
  • ⚪ within noise — the builds' runs overlap, or the difference is tiny; it cannot be told apart from random variation. Treat it as no change.
  • — informational — the 0.1% worst metrics average only the few worst frames of a run, so a single OS hiccup swings them by a lot; they are shown for context and never earn a verdict.
  • ⚠️ no verdict — the two builds' sessions were not comparable (very different sample counts, or too few usable runs), so no conclusion is drawn from them.
  • Exceptions per run — the average number of exceptions in a run's log, not counting teardown ones logged while the app quits. Flagged only on a difference of at least 2 per run and 1.5× the other build; exception kinds the baseline never threw are called out under the table. The Exception breakdown groups all of them by the explorer's report category and exception type (as totals across the runs).
  • A run that logged unusually many exceptions (at least 10 and 5× the median of its build's runs — e.g. a service was down during it) is excluded from all numbers and called out under the table.
  • The Overall line at the top only reacts to a metric that moved on two or more machines, or by 10% or more on one — a single modest 🟢/🔴 cell can still be a statistical fluke.

Apple M1

Metric Baseline Change Δ Result
Samples 4144 (×3) 4209 (×3)
CPU average 21.6 ms (20.3–21.9) 21.2 ms (21.2–21.4) -0.4 ms ⚪ within noise
CPU 1% worst 192.5 ms (146.6–219.4) 168.6 ms (160.7–174.5) -23.8 ms ⚪ within noise
CPU 0.1% worst 223.1 ms (220.5–233.7) 221.1 ms (221.0–222.6) -1.9 ms — informational
GPU average 33.9 ms (32.7–34.5) 34.7 ms (33.5–35.3) 0.8 ms ⚪ within noise
GPU 1% worst 44.1 ms (42.7–44.5) 44.6 ms (43.5–44.7) 0.5 ms ⚪ within noise
GPU 0.1% worst 45.0 ms (44.0–45.2) 45.3 ms (44.5–45.6) 0.3 ms — informational
Exceptions per run 0 0 0 ⚪ no significant change

Intel Core i5

Metric Baseline Change Δ Result
Samples 2279 (×3) 2283 (×3)
CPU average 39.0 ms (33.5–39.3) 38.9 ms (38.5–41.8) -0.1 ms ⚪ within noise
CPU 1% worst 409.4 ms (75.1–448.6) 416.0 ms (410.1–477.9) 6.6 ms ⚪ within noise
CPU 0.1% worst 435.5 ms (199.7–481.9) 433.1 ms (430.5–505.0) -2.4 ms — informational
GPU average 17.0 ms (11.0–17.5) 17.5 ms (16.9–21.6) 0.6 ms ⚪ within noise
GPU 1% worst 389.1 ms (20.9–427.1) 392.9 ms (391.0–457.7) 3.8 ms ⚪ within noise
GPU 0.1% worst 414.1 ms (46.9–458.7) 412.4 ms (409.9–480.5) -1.7 ms — informational
Exceptions per run 0 0 0 ⚪ no significant change

Automation

On demand — comment /visual-tests on this PR to run the visual regression suite against its build.

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — PR #10064 feat: bug report platform and multiple screenshots

STEP 2 — Root-cause check: PASS

Feature enhancement: evolves the bug-reporting pipeline from a single screenshot to multiple screenshots and adds a Platform field to Intercom tickets. The changes address a genuine feature need; no symptom-masking.

STEP 3 — Design & integration: PASS

No new long-lived units are introduced. EvidenceImage, IntercomTicketPlatform, and BugReportScreenshotSlot are stateless data carriers.

  • EvidenceImage vs the pre-existing BugReportImage looks like near-duplication (both wrap Bytes/ContentType), but is justified layering: BugReportImage carries a Texture2D tied to the UI lifecycle, while EvidenceImage is a pure wire-payload type with no Unity display dependency. Keeping Texture2D out of BugReportService/IntercomTicketPayload is the right call.
  • IntercomTicketPlatform is a minimal addition — the proxy requires a non-zero enum code per its doc comment. Starting at 1 is intentional defensive design.
  • BugReportScreenshotSlot is a standard Unity inspector-wiring struct (not overriding readonly struct[Serializable] requires mutability for Unity serialization).

TEARDOWN / CONSUMPTION TRACE:

Opener Mirror Location
onClick.AddListener(() => OnRemoveScreenshotClicked(slotIndex)) Dies with the view (wired once in OnViewInstantiated) BugReportController.cs:119–123
attachedImages.Add(picked.Value) ClearAttachedImages()Object.Destroy(preview) + Clear() Called from Dispose(), OnBeforeViewShow(), OnViewClose()
operationsCts SafeRestart() on show/close, SafeCancelAndDispose() in Dispose() BugReportController.cs:73–74
submissionsCts SafeCancelAndDispose() in Dispose() BugReportController.cs:75
EvidenceImage bytes base64-encoded in BuildCreateTicketJson, sent to proxy IntercomTicketPayload.cs:103

No leaks or unconsumed buffers found. ✓

Texture lifecycle across async boundary: Verified that BugReportImage.Bytes is the raw file data captured independently of Preview in OsFileBrowserBugReportImageProvider.PickAsync. When ClearAttachedImages() destroys preview textures while SubmitDraftAsync is in flight, the EvidenceImage[] conversion (which only reads .Bytes/.ContentType) is unaffected. The BugReportDraft's ToArray() snapshot correctly decouples the submission from the controller's mutable list. ✓

STEP 4 — Member audit

Member Consumers Verdict
maxScreenshots (private property) 2 (AttachScreenshotAsync, RefreshScreenshots) Correctly derives limit from both proxy cap and prefab slot count. ✓
SetSubmitInteractable(bool) 1 (RefreshSubmitInteractable) Encapsulates two visual updates (button interactable + label alpha) that must stay in sync. Legitimate view method. ✓
SetScreenshots(...) 1 (RefreshScreenshots) Legitimate view method handling all slots. ✓
SelectEvidenceImages(...) 1 production + 5 tests Public static with comprehensive coverage. ✓

STEP 5 — Line-level review

No P0 or P1 issues found.

One P2 noted inline (see comment on MAX_EVIDENCE_TOTAL_BYTES).

Slot closure wiring (BugReportController.cs:119–123): for loop with a local int slotIndex copy, wired once in OnViewInstantiated (not per-show). Correct — no closure-over-loop-variable bug, no pooled-rebind anti-pattern. ✓

Evidence format change (single object → array): The payload's evidence key changes from a single {content_type, data} object to an array of them. Tests in IntercomTicketPayloadShould verify both single-image and multi-image serialization, including the array-of-one case. ✓

Validation layers are defense-in-depth:

  1. OsFileBrowserBugReportImageProvider caps at 10 MB and validates via Texture2D.LoadImage
  2. BugReportController limits to maxScreenshots (min of proxy cap and prefab slots)
  3. BugReportService.SelectEvidenceImages enforces per-image (3 MB), count (3), and total budget (3 MB) caps

Test coverage is comprehensive: 5 new/updated tests for SelectEvidenceImages edge cases (null, empty, zero-length, oversized, count cap, total budget overflow), 2 platform-code tests, updated controller and payload tests. ✓

STEP 6 — Complexity: COMPLEX

Changes span the bug-reporting pipeline across service, controller, view, payload, and prefab layers, plus the API contract with the Intercom proxy.

STEP 7 — QA: YES

Modifies runtime UI code in the bug report form (screenshot attachment, submit button, evidence serialization). User-facing.

STEP 8 — Non-blocking warnings

None. Main scene not modified.

Security review

  • Credentials: Clean. No secrets in diff. The proxy holds the workspace token.
  • Input validation: Well-layered (see Step 5 validation analysis). No crash paths — loops are bounds-checked, no unguarded indexing.
  • JSON construction: Built via Dictionary<string,object> + JsonConvert.SerializeObject (Newtonsoft). No string concatenation, no injection vector.
  • Resource limits: Total upload budget capped at 3 MB before base64. Max 3 images enforced in both UI and service layers.
  • Log exposure: ReportHub.LogWarning calls log only image index and byte thresholds — no image bytes, PII, or tokens.
  • Dependencies: No new packages added.
  • Prefab: No script/plugin/binary additions; only UI layout changes for screenshot slots.

DEPENDENCY_REVIEW: PASS


REVIEW_RESULT: PASS ✅
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: Changes the bug-reporting pipeline across service, controller, view, and payload layers plus the API contract with the Intercom proxy
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by decentraland-bot via GitHub

Comment on lines +60 to +65

/// <summary>
/// Bound for all the images together. The proxy caps the request body at 4.5 MB and base64 inflates
/// the bytes by a third, so the images share the budget that one image alone may fill.
/// </summary>
public const int MAX_EVIDENCE_TOTAL_BYTES = MAX_EVIDENCE_BYTES;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] The total budget constant aliases the per-image constant (MAX_EVIDENCE_TOTAL_BYTES = MAX_EVIDENCE_BYTES). While the comment explains the derivation (4.5 MB proxy body cap ÷ base64 inflation ≈ 3 MB raw budget), the identical value leaves a reader wondering whether the equality is coincidence or intentional coupling. Consider either inlining a reference to MAX_EVIDENCE_BYTES directly in the running-total check with a comment explaining the shared cap, or adding a note here that the equality is intentional and when each would diverge.

Suggested change
/// <summary>
/// Bound for all the images together. The proxy caps the request body at 4.5 MB and base64 inflates
/// the bytes by a third, so the images share the budget that one image alone may fill.
/// </summary>
public const int MAX_EVIDENCE_TOTAL_BYTES = MAX_EVIDENCE_BYTES;
/// <summary>
/// Bound for all the images together. Currently equals MAX_EVIDENCE_BYTES because the proxy
/// caps the request body at 4.5 MB and base64 inflates by a third — so the raw budget for all
/// images together is the same 3 MB that one image alone may fill.
/// </summary>
public const int MAX_EVIDENCE_TOTAL_BYTES = MAX_EVIDENCE_BYTES;

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