Skip to content

feat: open Explorer UI from SDK scene#9472

Open
popuz wants to merge 11 commits into
devfrom
feat/scene-triggered-explorer-ui
Open

feat: open Explorer UI from SDK scene#9472
popuz wants to merge 11 commits into
devfrom
feat/scene-triggered-explorer-ui

Conversation

@popuz

@popuz popuz commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

SDK7 scenes open a native Explorer fullscreen panel via ~system/RestrictedActions and receive a typed OpenExplorerUiResult verdict (OPENED / WAS_ALREADY_OPEN / REJECTED_NOT_CURRENT_SCENE / REJECTED_FEATURE_DISABLED / REJECTED_NO_USER_GESTURE).

  • Protocol bindings regenerated (OpenExplorerUi RPC, ExplorerUi & OpenExplorerUiResult enums); also picks up experimental's additive AvatarEmoteCommand.Mask field.
  • Impl gates: current-scene, feature-flag, user-gesture window, already-open.
  • Gesture signal: LastUserInputTick on ISceneStateProvider, stamped by WritePointerEventResultsSystem, consumed by TryOpenExplorerUi.
  • IExplorerUiActions returns the open verdict and tracks panel open-state via MVC events; disposal wired through the restricted-actions API.
  • Communities is gated through CommunitiesFeatureAccess (identity-dependent flag + wallets allowlist — the same source the sidebar uses), not through FeaturesRegistry, where FeatureId.COMMUNITIES is never populated.
  • Host RestrictedActions.js exposes the runtime enums; unit tests cover the gesture, already-open, unknown-value, feature-disabled and communities-rejection-propagation paths.

Pull Request Description

What does this PR change?

Adds the openExplorerUi restricted action so SDK7 scenes can open native Explorer panels from a user gesture:

ExplorerUi value Panel Gate
EU_SETTINGS (0) Settings
EU_MAP (1) Map
EU_BACKPACK (2) Backpack
EU_CAMERA_REEL (3) Camera Reel camera-reel feature flag
EU_COMMUNITIES (4) Communities Communities feature flag + wallets allowlist (same as sidebar)
EU_PLACES (5) Places discover feature flag
EU_EVENTS (6) Events discover feature flag

Every call returns a verdict to the scene: OPENED (1), WAS_ALREADY_OPEN (2), REJECTED_NOT_CURRENT_SCENE (3), REJECTED_FEATURE_DISABLED (4), REJECTED_NO_USER_GESTURE (5).

Test Instructions

The world flutterecho.dcl.eth hosts a test scene with two rows of labeled clickable cubes in front of spawn:

  • Row 1 (closer) — 7 cubes, one per panel: each click calls openExplorerUi for that panel.
  • Row 2 (further) — gate-provoking cubes: DELAYED 5s (calls after a countdown, outside the user-gesture window), DOUBLE CALL (two calls from one click), INVALID (99) (unknown ui value). On scene load the scene also fires one automatic ON-START(MAP) call with no gesture at all.

The player spawns right in front of the cube rows. Every verdict is shown in an on-screen HUD (top-right under the scene-name badge, newest first with a scene-side timestamp: green OPENED, yellow WAS_ALREADY_OPEN, red rejections) — log tailing is optional. The scene console additionally logs each step with the [openExplorerUi] prefix and a timestamp: click <NAME>, request <NAME> (ui=N) and verdict <NAME> -> <RESULT> (<code>), so the click→verdict chain is fully traceable from Explorer logs.

Steps (standard run):

metaforge explorer run 9472 -- --realm flutterecho.dcl.eth
metaforge explorer logs tail --filter "openExplorerUi"   # in a second terminal

(Alternatively join any realm and use the chat command /goto flutterecho.dcl.eth.)

Expected result: see the test cases below — every cube opens its panel with OPENED (1) in the logs.

Steps (fresh account):

metaforge account create --clear
metaforge explorer run 9472 -- --realm flutterecho.dcl.eth

Expected result: same as the standard run — in particular the COMMUNITIES cube must behave exactly like the sidebar does for that account.

Prerequisites

  • Explorer build from this PR (metaforge explorer run 9472)
  • Account able to open Communities from the sidebar (for TC2)

Test Steps

TC1 — happy path, all 7 panels

  1. Stand at spawn, face the cube row, click a cube (e.g. MAP).
  2. The matching panel opens fullscreen; the log shows OPENED (1).
  3. Close the panel (Esc) and repeat for each of the 7 cubes.
  4. Each opened panel must look and behave identically to opening the same section manually from the sidebar.

TC2 — Communities parity (bug fixed in this PR)

  1. Verify Communities opens from the sidebar for this account.
  2. Click the COMMUNITIES cube.
  3. Communities opens with OPENED (1). Before the fix this always returned REJECTED_FEATURE_DISABLED (4) even for allowed users.

TC3 — feature-flag rejection

  1. Relaunch: metaforge explorer run 9472 -- --realm flutterecho.dcl.eth --debug --camera-reel false
  2. Click the CAMERA_REEL cube: no panel opens, log shows REJECTED_FEATURE_DISABLED (4).
  3. Relaunch with --debug --discover false: the PLACES and EVENTS cubes are rejected the same way; MAP / SETTINGS / BACKPACK still open.

TC4 — manual UI regression

  1. Open and close every section manually (sidebar, map hotkey) before and after cube-triggered opens — no stuck panels, no double-opens.
  2. Sidebar visibility of Communities is unchanged vs dev (the gating source did not change, only the restricted action now consults it).

TC5 — user-gesture gate

  1. On scene load, before clicking anything, the HUD already shows ON-START(MAP) -> REJECTED_NO_USER_GESTURE (5) — a call with no gesture ever recorded is rejected.
  2. Click the DELAYED 5s cube (row 2) and don't click anything else during the countdown shown above it: no panel opens, the HUD shows DELAYED(MAP) -> REJECTED_NO_USER_GESTURE (5).
  3. (Any click during the countdown refreshes the gesture window and can legitimately turn the verdict into OPENED — that is not a failure, just retry without clicking.)

TC6 — rapid clicks / double call

  1. Click the DOUBLE CALL cube (fires openExplorerUi twice from one click): exactly one Map panel opens — MVCManager ignores a show command for a controller that is already showing.
  2. The HUD shows two verdicts: DOUBLE#1(MAP) -> OPENED (1) and, for DOUBLE#2(MAP), either OPENED (1) or WAS_ALREADY_OPEN (2) depending on whether the panel registered as shown between the two calls — both are acceptable; the failure mode would be a second panel or a stuck UI.

TC7 — unknown ui value

  1. Click the INVALID (99) cube: no panel opens, the HUD shows INVALID(99) -> REJECTED_FEATURE_DISABLED (4).

Additional Testing Notes

  • REJECTED_NOT_CURRENT_SCENE is the only verdict not drivable in the world (a world confines the player to its own scene). It is unit-tested; in a Genesis City deployment of the same scene it can be reproduced by clicking DELAYED 5s and walking to a neighbouring parcel during the countdown.
  • The scene must be built with the branch SDK from CI run ≥ 30037787217: earlier builds used the pre-reorder ExplorerUi numbering and open the wrong (off-by-one) panel. The scene deployed to flutterecho.dcl.eth is up to date; if cubes open mismatched panels, suspect a stale scene deployment, not the client.
  • Scene logs are also visible in the in-app scene console (--scene-console).
  • The same showcase exists in sdk7-test-scenes at parcel 80,-5 (scenes/80,-5-open-explorer-ui) for local-scene-development runs.

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered — the gesture stamp is a single uint write per input event; no per-frame work added
  • For SDK features: Test scene is included — flutterecho.dcl.eth world + sdk7-test-scenes/scenes/80,-5-open-explorer-ui

🤖 Generated with Claude Code

SDK7 scenes open a native Explorer fullscreen panel via ~system/RestrictedActions
and receive a typed OpenExplorerUiResult verdict (Opened / RejectedNotCurrentScene
/ RejectedAlreadyOpen / RejectedFeatureDisabled / RejectedNoUserGesture).

- Protocol bindings regenerated (OpenExplorerUi RPC, ExplorerUi &
  OpenExplorerUiResult enums); also picks up experimental's additive
  AvatarEmoteCommand.Mask field.
- Impl gates: current-scene, feature-flag, user-gesture window, already-open.
- Gesture signal: LastUserInputTick on ISceneStateProvider, stamped by
  WritePointerEventResultsSystem, consumed by TryOpenExplorerUi.
- IExplorerUiActions returns OpenSectionResult and tracks panel open-state via
  MVC events; disposal wired through the restricted-actions API.
- Host RestrictedActions.js exposes the runtime enums; tests cover the gesture
  and already-open rejections. CONTEXT.md documents the open-verdict domain language.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@popuz popuz self-assigned this Jul 23, 2026
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

popuz and others added 10 commits July 23, 2026 19:42
SDK7 scenes open a native Explorer fullscreen panel via ~system/RestrictedActions
and receive a typed OpenExplorerUiResult verdict (Opened / RejectedNotCurrentScene
/ RejectedAlreadyOpen / RejectedFeatureDisabled / RejectedNoUserGesture).

- Protocol bindings regenerated (OpenExplorerUi RPC, ExplorerUi &
  OpenExplorerUiResult enums); also picks up experimental's additive
  AvatarEmoteCommand.Mask field.
- Impl gates: current-scene, feature-flag, user-gesture window, already-open.
- Gesture signal: LastUserInputTick on ISceneStateProvider, stamped by
  WritePointerEventResultsSystem, consumed by TryOpenExplorerUi.
- IExplorerUiActions returns OpenSectionResult and tracks panel open-state via
  MVC events; disposal wired through the restricted-actions API.
- Host RestrictedActions.js exposes the runtime enums; tests cover the gesture
  and already-open rejections.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerate RestrictedActions.gen.cs from protocol commit 0e7de00: ExplorerUi loses
EU_UNSPECIFIED and renumbers contiguously with EU_SETTINGS as the zero value, and
OpenExplorerUiResult renames REJECTED_ALREADY_OPEN to WAS_ALREADY_OPEN (value 2,
no longer a rejection; rejected values shift to 3-5). Mirror both enums in the
host JS module. Delete the OpenSectionResult enum and return the protobuf
OpenExplorerUiResult from IExplorerUiActions.OpenSection, collapsing the
TryOpenExplorerUi tail to a direct cast of the OpenSection verdict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
  FeatureId.COMMUNITIES is never populated in FeaturesRegistry (its state
  is identity-dependent), so the synchronous registry gate in
  TryOpenExplorerUi rejected the section unconditionally, while the
  sidebar and ExplorePanel opened it fine via CommunitiesFeatureAccess.

  Move the Communities gate into ExplorerUiActions, backed by a new
  synchronous CommunitiesFeatureAccess.IsUserAllowedCached() snapshot of
  the async allowlist check (warmed at login; true in the editor,
  mirroring the async path). CAMERA_REEL and DISCOVER stay on the
  registry gate, which is populated correctly for them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@popuz
popuz marked this pull request as ready for review July 24, 2026 11:44
@popuz
popuz requested review from a team as code owners July 24, 2026 11:44
@decentraland-bot
decentraland-bot self-requested a review July 24, 2026 11:44
@popuz popuz changed the title feat: add openExplorerUi restricted action feat: open Explorer UI by the SDK scene Jul 24, 2026
@popuz popuz changed the title feat: open Explorer UI by the SDK scene feat: open Explorer UI from SDK scene Jul 24, 2026
Comment thread scripts/package.json
},
"dependencies": {
"@dcl/protocol": "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-29092627614.commit-d2f272f.tgz",
"@dcl/protocol": "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-30037355888.commit-0e7de00.tgz",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

change it back when approved

@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.

STEP 2 — Root-cause check

Problem: SDK7 scenes have no way to open native Explorer panels (Settings, Map, Backpack, etc.) from user gestures.

Fix vs symptom: The diff adds a new openExplorerUi restricted action — this is the right approach. It extends the existing restricted-actions infrastructure with proper gating (current-scene, gesture window, feature flags, communities identity check). This addresses the cause directly.

Verdict: PASS ✅


STEP 3 — Design & integration

Owner search

New unit: ExplorerUiActions — manages whether the explore panel is open (via MVC events) and opens sections.

  1. Entity/resource managed: The explore panel's open/close state and the act of opening a section.
  2. Existing owners searched:
    • ExplorePanelController — the MVC controller that owns the panel lifecycle. Lives in DCL.Social.
    • SidebarController — opens explore panel sections from the sidebar. Lives in DCL.Social.
    • ExplorePanelPlugin — constructs the ExplorePanelController and wires its dependencies.
  3. Can the logic live at those points? No. The restricted-actions API lives in SceneRuntime, which cannot reference DCL.Social (the dependency runs the other way). The asmref that compiles ExplorerUiActions into DCL.Social while keeping the file in the RestrictedActions/ folder is the correct cross-assembly seam.

Teardown trace

Opener Mirror Location
mvcManager.OnViewShowed += OnViewShowed mvcManager.OnViewShowed -= OnViewShowed ExplorerUiActions ctor → Dispose()
mvcManager.OnViewClosed += OnViewClosed mvcManager.OnViewClosed -= OnViewClosed ExplorerUiActions ctor → Dispose()

Disposal chain: SceneRuntimeImpl.Dispose()RestrictedActionsAPIWrapper.Dispose() (line 118) → RestrictedActionsAPIImplementation.Dispose() (line 242) → explorerUiActions.Dispose() (line 244) → unsubscribes MVC events. ✅

Verdict: PASS ✅


STEP 4 — Member audit

New member Consumers Assessment
CommunitiesFeatureAccess.IsUserAllowedCached() ExplorerUiActions.OpenSection (1) Legitimate accessor centralizing storedResult with editor override. Not a single-use derivation.
ISceneStateProvider.LastUserInputTick WritePointerEventResultsSystem (writer), RestrictedActionsAPIImplementation.TryOpenExplorerUi (reader) Simple stamp property, 2 consumers.
IExplorerUiActions.OpenSection() RestrictedActionsAPIImplementation.TryOpenExplorerUi (1) Interface boundary for cross-assembly seam. Mocked in tests. Justified.
IRestrictedActionsAPI.TryOpenExplorerUi() RestrictedActionsAPIWrapper.OpenExplorerUi (1) Standard interface method following existing restricted-actions pattern.

STEP 5 — Line-level review

3 findings (all P2). See inline comments.

Additional P2 — Unused constructor parameter (not on a changed line):
ExplorePanelController.cs:89 — The creditsPanelController field was removed but the constructor still accepts the parameter. It is no longer stored or used. Consider removing the parameter from the signature and its call site in ExplorePanelPlugin.cs (~line 620).


STEP 6 — Complexity assessment

COMPLEX — Adds a new restricted action with protocol bindings, cross-assembly asmref, MVC event subscriptions, gesture-window logic, and async patterns (UniTask + .Forget()).


STEP 7 — QA assessment

QA_REQUIRED: YES — Modifies runtime code that affects user-facing behavior: opening fullscreen panels from scene interactions, gesture-based gating, and feature-flag rejection.


STEP 8 — Non-blocking warnings

⚠️ Unrelated cleanup included: The PR removes three members from ExplorePanelController (creditsPanelController field, communitiesBrowserController private property, CanBeClosedByEscape override). The CanBeClosedByEscape removal is a behavioral change: previously, the panel could not be closed by Escape during the ViewShowing transition state; now the IController default applies (always closable for FULLSCREEN layer). This is likely fine (other fullscreen panels use the default), but confirm this is intentional — TC4 should validate no regression.


STEP 9 — Verdict

REVIEW_RESULT: PASS ✅
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: New restricted action with protocol bindings, cross-assembly asmref, MVC event subscriptions, gesture-window logic, and async patterns.
QA_REQUIRED: YES


Security review

No security issues found. The gesture gate prevents scenes from opening panels without user interaction (1-tick window, underflow-safe). LastUserInputTick is only written by internal C# systems — scene JS cannot spoof it. The TryMapExplorerUi switch rejects unknown enum values. IsUserAllowedCached() defaults to false (fail-closed). uint overflow wraps to 0, which also fails closed.


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

Comment on lines +47 to +51
if (isExplorePanelOpen)
return OpenExplorerUiResult.WasAlreadyOpen;

OpenSectionAsync(section).Forget();
return OpenExplorerUiResult.Opened;

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] Race in isExplorePanelOpen guard. Between the isExplorePanelOpen check (line 47) and OnViewShowed firing, a rapid second call passes the guard and queues a duplicate ShowAsync. Both return Opened to the scene. The MVC manager handles double-show gracefully (TC6 documents both verdicts as acceptable), so this isn't a functional bug, but setting the flag eagerly would make the WasAlreadyOpen verdict more accurate.

Suggested change
if (isExplorePanelOpen)
return OpenExplorerUiResult.WasAlreadyOpen;
OpenSectionAsync(section).Forget();
return OpenExplorerUiResult.Opened;
if (isExplorePanelOpen)
return OpenExplorerUiResult.WasAlreadyOpen;
isExplorePanelOpen = true;
OpenSectionAsync(section).Forget();
return OpenExplorerUiResult.Opened;

If ShowAsync fails before OnViewShowed fires, reset the flag in the catch blocks of OpenSectionAsync:

catch (OperationCanceledException) { isExplorePanelOpen = false; }
catch (Exception e)
{
    isExplorePanelOpen = false;
    ReportHub.LogException(e, ReportCategory.RESTRICTED_ACTIONS);
}


bool TryOpenNftDialog(string urn);

int TryOpenExplorerUi(int ui);

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] Naming: Try prefix implies bool return. By C# convention, TryX methods return bool (e.g. TryOpenExternalUrl on line 11). TryOpenExplorerUi returns int (a result code), breaking the convention. Since the caller always receives a verdict rather than success/failure, consider renaming to OpenExplorerUi (matching the JS bridge name and the protocol RPC name).

Suggested change
int TryOpenExplorerUi(int ui);
int OpenExplorerUi(int ui);

(Would also require renaming in RestrictedActionsAPIImplementation.cs:212 and RestrictedActionsAPIWrapper.cs:99.)

@github-actions

Copy link
Copy Markdown
Contributor

Warnings not reduced: 14591 => 14592 — remove at least one warning to merge.

Warnings/errors in files changed by this PR (81)
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:42  CSharpWarnings::CS0162  Code is unreachable
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:42  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:71  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:92  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:92  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:114  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:115  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:123  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:130  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs:136  CSharpWarnings::CS8618  Non-nullable field 'engineFactory' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:34  CSharpWarnings::CS8618  Non-nullable field 'explorerUiActions' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:31  CSharpWarnings::CS8618  Non-nullable field 'globalWorldActions' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:29  CSharpWarnings::CS8618  Non-nullable field 'mvcManager' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs:148  CSharpWarnings::CS8618  Non-nullable field 'path' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:87  CSharpWarnings::CS8618  Non-nullable field 'permissionsProvider' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:28  CSharpWarnings::CS8618  Non-nullable field 'restrictedActionsAPIImplementation' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:32  CSharpWarnings::CS8618  Non-nullable field 'sceneData' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:30  CSharpWarnings::CS8618  Non-nullable field 'sceneStateProvider' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:35  CSharpWarnings::CS8618  Non-nullable field 'sceneWorld' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:33  CSharpWarnings::CS8618  Non-nullable field 'systemClipboard' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:26  CSharpWarnings::CS8618  Non-nullable fields 'resultCallback', 'cts' must contain non-null values when exiting constructor. Consider adding the 'required' modifiers or declaring the fields as nullable.
Assets/DCL/ExplorePanel/ExplorePanelController.cs:75  CSharpWarnings::CS8618  Non-nullable fields 'tabsBySections', 'exploreSections', 'sectionSelectorController', 'setupExploreSectionsCts', 'checkForLiveEventsCts' must contain non-null values when exiting constructor. Consider adding the 'required' modifiers or declaring the fields as nullable.
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:52  ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract  Expression is always true according to nullable reference types' annotations
Assets/DCL/ExplorePanel/ExplorePanelController.cs:205  ConditionalAccessQualifierIsNonNullableAccordingToAPIContract  Conditional access qualifier expression is never null according to nullable reference types' annotations
Assets/DCL/ExplorePanel/ExplorePanelController.cs:205  ConditionalAccessQualifierIsNonNullableAccordingToAPIContract  Conditional access qualifier expression is never null according to nullable reference types' annotations
Assets/DCL/ExplorePanel/ExplorePanelController.cs:205  ConditionalAccessQualifierIsNonNullableAccordingToAPIContract  Conditional access qualifier expression is never null according to nullable reference types' annotations
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:83  ConditionalAccessQualifierIsNonNullableAccordingToAPIContract  Conditional access qualifier expression is never null according to nullable reference types' annotations
Assets/DCL/TeleportPrompt/TeleportPromptController.cs:86  ConditionalAccessQualifierIsNonNullableAccordingToAPIContract  Conditional access qualifier expression is never null according to nullable reference types' annotations
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:42  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:43  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:46  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:51  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:52  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:56  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:58  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Communities/CommunitiesFeatureAccess.cs:59  HeuristicUnreachableCode  Code is heuristically unreachable
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:196  InconsistentNaming  Name 'CommunicationsControllerAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'CommunicationsControllerApi'.
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:201  InconsistentNaming  Name 'EngineAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'EngineApi'.
Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/RestrictedActionsApi/IRestrictedActionsAPI.cs:9  InconsistentNaming  Name 'IRestrictedActionsAPI' does not match rule 'interfaces_should_be_pascal_case_with_i_prefix'. Suggested name is 'IRestrictedActionsApi'.
Assets/DCL/Infrastructure/SceneRuntime/ISceneRuntime.cs:149  InconsistentNaming  Name 'RegisterEngineAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RegisterEngineApi'.
Assets/DCL/Infrastructure/SceneRuntime/ISceneRuntime.cs:156  InconsistentNaming  Name 'RegisterEngineAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RegisterEngineApi'.
Assets/DCL/Infrastructure/SceneRuntime/ISceneRuntime.cs:61  InconsistentNaming  Name 'RegisterEngineAPIWrapper' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RegisterEngineApiWrapper'.
Assets/DCL/Infrastructure/SceneRuntime/ISceneRuntime.cs:230  InconsistentNaming  Name 'RegisterSDKMessageBusCommsApi' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RegisterSdkMessageBusCommsApi'.
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:192  InconsistentNaming  Name 'RestrictedActionsAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RestrictedActionsApi'.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/RestrictedActionsAPIImplementation.cs:27  InconsistentNaming  Name 'RestrictedActionsAPIImplementation' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RestrictedActionsApiImplementation'.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RestrictedActions/Tests/RestrictedActionsAPIImplementationShould.cs:26  InconsistentNaming  Name 'RestrictedActionsAPIImplementationShould' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RestrictedActionsApiImplementationShould'.
Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/RestrictedActionsApi/RestrictedActionsAPIWrapper.cs:12  InconsistentNaming  Name 'RestrictedActionsAPIWrapper' does not match rule 'members_should_be_pascal_case'. Suggested name is 'RestrictedActionsApiWrapper'.
Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs:400  InconsistentNaming  Name 'TestAPIWrapper' does not match rule 'members_should_be_pascal_case'. Suggested name is 'TestApiWrapper'.
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:270  InconsistentNaming  Name 'WithRuntimeAndJsAPI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'WithRuntimeAndJsApi'.
Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs:190  InconsistentNaming  Name 'WithRuntimeAndJsAPIBase' does not match rule 'members_should_be_pascal_case'. Suggested name is 'WithRuntimeAndJsApiBase'.

…and 31 more (see the csharp-lint-reports artifact).

@github-actions

Copy link
Copy Markdown
Contributor

Tests: 24288 passed, 1 failed

Failed tests (1)
  • [editmode] SceneRunner.Tests.SceneFacadeShould.DisposeEverythingOnce

@balducciv

Copy link
Copy Markdown

PR reviewed and approved by QA on both platforms following the PR test instructions. ✅
Smoke test performed on Windows and Mac (standard account + fresh account) to verify the normal flow is working as expected. ✅

Build: feat/scene-triggered-explorer-ui @ 8d16d00e (PR #9472 HEAD)
OS: Windows 11 (Katana 15 B12VFK) / macOS — Apple M3 Pro (Mac15,6)

Test results:

  • TC1 — All 7 panels open with OPENED (1) from the cube row — confirmed on both platforms, both accounts
  • TC2 — Communities parity: COMMUNITIES cube returns OPENED (1) on all runs — the previous REJECTED_FEATURE_DISABLED (4) bug does not reproduce
  • TC3 — Feature-flag rejection, confirmed on both platforms:
    • --camera-reel false: CAMERA_REELREJECTED_FEATURE_DISABLED (4) (Mac + Windows)
    • --discover false: PLACES / EVENTSREJECTED_FEATURE_DISABLED (4), other panels unaffected (Mac + Windows)
  • TC4 — Manual UI regression: all 7 panels open cleanly with no stuck state on a standard relaunch, on both platforms
  • TC5 — Gesture gate: ON-START(MAP) -> REJECTED_NO_USER_GESTURE (5) on scene load with no prior click, on all runs. DELAYED cube correctly rejects when no click occurs during the countdown (5 separate confirmations on Mac + Windows); one case where the actual MAP cube was clicked mid-countdown produced a legitimate OPENED (1) on the subsequent MAP click — consistent with the documented gesture-refresh behavior
  • TC6 — Double call: DOUBLE#1(MAP) / DOUBLE#2(MAP) both return OPENED (1) on every run (never WAS_ALREADY_OPEN), with only one Map panel visibly opening — consistent with the eager-flag race already flagged as a non-blocking P2 in code review, not a new issue
  • TC7 — INVALID (99) cube returns REJECTED_FEATURE_DISABLED (4) on every occurrence, across all runs (multiple repeats on Mac + Windows)

Unrelated errors noted (do not affect verdict):

  • Curl error 42 / Curl error 23 — scene-transition callback aborts, pre-existing noise
  • EventsApiException: Error fetching events (one Windows run) — from SidebarController.FillLiveEventsAsync, unrelated to the openExplorerUi action
  • livekit::rtc_engine reconnection errors (one Mac run) — Pulse/multiplayer transport noise around scene transitions, unrelated to this fix
  • DisposableUtils.SafeDispose / DynamicWorldContainer disposal exceptions — all occur after [ExitUtils] Exit requested, standard shutdown-sequence noise
  • No new runtime errors in any of the TC3–TC7 logs on either platform

Verdict: PASS ✅

All 7 test cases (TC1–TC7) pass on both Windows and Mac, across standard, fresh, flag-disabled, and gesture-edge-case runs, with no regressions or related errors found. Full click→verdict chain traced end-to-end from scene console + Player.log evidence.

Scene console + Player.logs for windows and mac

TC 1
log 9472 windows new user.txt
log 9472 windows old user.txt
Player 9472 new user windows.log
Player 9472 old user windows.log
Scene console - 9472 Mac.rtf
scene console new acc 9472 Mac.rtf

24.07.2026_15.10.14_REC.9472.new.user.mp4
24.07.2026_15.03.48_REC.9472.old.user.mp4

TC 3
log 9472 windows step 3-3.txt
log 9472 windows step 3.txt
Player 9472 TC3-1 Mac.log
Player 9473 step 3-1.log
Player 9473 step 3-3 windows.log
TC-3-1 mac.rtf
TC3 - Mac - Camera Reel step 1.rtf

24.07.2026_15.51.15_REC.TC.3-1.mp4
24.07.2026_15.31.06_REC.TC3.step.1.Mac.camera.reel.mp4

TC 4
Player 9472 TC 4 Mac.log
Player TC4 Windows.log
TC-4 OK Mac.rtf

24.07.2026_15.53.10_REC.TC.4.Mac.OK.mp4

TC 5-6-7
log 9472 windows step 5-6-7.txt
Player 5-6-7 windows.log
Player TC-5-6-7 Mac.log
TC-5 Mac.rtf
TC-5-6-7 Mac.rtf

24.07.2026_15.56.19_REC.TC.5.mp4
24.07.2026_15.58.05_REC.TC.6.mp4

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.

3 participants