Skip to content

feat: support scene named spawn points#9369

Merged
popuz merged 41 commits into
devfrom
feat/support-scene-named-spawn-points
Jul 22, 2026
Merged

feat: support scene named spawn points#9369
popuz merged 41 commits into
devfrom
feat/support-scene-named-spawn-points

Conversation

@popuz

@popuz popuz commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Description

What does this PR change?

Adds named spawn point selection to the teleport pipeline (part of #6848). Scenes already declare named spawn points in scene.json; until now the explorer always ran the default selection (default-flagged → closest). This PR lets a teleport request a spawn point by name, end-to-end.

Core mechanism (single chokepoint, applies to every entry point below):

  • TeleportUtils.PickSpawnPoint gains string? spawnPointName. When set and matched (case-insensitive), that point is used directly, bypassing the default/closest logic.
  • Unmatched name → soft fallback to the default selection + ReportHub warning (Spawn point '<name>' not found in scene, falling back to default spawn point selection). The teleport itself never fails because of a bad name.
  • Duplicate names → first in declaration order wins (+ warning).
  • Everything after selection is unchanged: coordinate-range points ("x": [min, max]) still scatter arriving players randomly within the range; cameraTarget still applies.

Entry points wired in this PR:

Entry point Syntax
/goto (Genesis) /goto x,y/spawnPoint
/goto (worlds) /goto world/spawnPoint, /goto world/x,y/spawnPoint
/goto-local /goto-local x,y/spawnPoint, /goto-local spawnPoint (current scene)
Deep link (cold start + runtime) decentraland://?realm=...&spawnpoint=spawnPoint
Launch parameter (CLI / CI) Explorer.exe --realm ... --spawnpoint spawnPoint

Also included:

  • /goto parsing extracted into pure ChatParamUtils.ParseGotoTarget with full grammar-table unit tests (ChatParamUtilsShould); GoToChatCommand is now a thin dispatcher.
  • The chat command handler now trims trailing whitespace, so /goto 0,0 (trailing space) no longer fails validation — for all commands.
  • Already-in-realm named teleport: /goto world/name (and the runtime deep link with spawnpoint but no position) used while already inside the target realm now teleports to the named spawn point of the scene the player stands in, instead of stopping at 🟡 You are already in .... Without a name the old message is kept.
  • Unit tests: TeleportUtilsShould (named selection core), ChatParamUtilsShould (grammar), RealmLaunchSettingsShould (deep link / CLI arg parsing), ChatTeleporterShould (already-in-realm branching).

Out of scope: SDK entry points (teleportTo / changeRealm with spawnPoint) — they require protocol + js-sdk-toolchain changes first; nothing for them in this repo yet.

Test Instructions

Steps (standard run):

metaforge explorer run 9369

Prerequisites

  • A world with named spawn points. Test world: flutterecho.dcl.eth (4x4 parcels, spotlight cubes in each corner, physics playground near the origin corner), deployed with:
Name Position What it's for
spawn (default) 2, 0, 2 default landing (regression)
center 32, 0, 32 scene center
physics 15, 0, 12 inside the physics playground
far-light 60, 0, 60 far corner; has cameraTarget looking back at scene center
mid-corner 4, 0, 60 another corner
scatter x∈[26,38], z∈[26,38] coordinate-range scatter
twin (declared twice) 10, 0, 50 and 50, 0, 10 duplicate-name resolution
  • The "not found" / "duplicate" warnings go to logs, not chat:
metaforge explorer logs tail --filter "spawn point"

Test Cases

1. /goto — world forms

  1. From Genesis: /goto flutterecho/center → realm change, land at scene center (32, 32). No coordinates given: the world manifest picks the parcel, the name picks the point.
  2. From Genesis: /goto flutterecho.dcl.eth/0,0/far-light → land at the far corner and the camera faces back toward scene center (cameraTarget).
  3. Already inside the world: /goto flutterecho/0,0/physics → teleport within the world to the physics playground.
  4. Already inside the world: /goto flutterecho/centerin-world teleport to the scene center, no realm change (the name targets a spawn point of the scene you are standing in).
  5. Already inside the world: /goto flutterecho (no name) → 🟡 You are already in..., no teleport (pre-existing behavior, unchanged).
  6. Regression: /goto flutterecho from Genesis → land at the default spawn (2, 2), exactly as before.

2. /goto — Genesis form

  1. /goto x,y/name against any Genesis scene that declares named spawn points → lands at the named point.
  2. Fallback path works on any parcel: /goto 0,0/nosuch → normal teleport to 0,0 + warning in logs.
  3. Regression: /goto 0,0, /goto random, /goto crowd behave exactly as before.
  4. From inside a world: /goto 0,0/name → realm change to Genesis carries the name (it is not dropped).

3. /goto-local (inside the world / any realm)

  1. /goto-local 0,0/center → parcel + name form.
  2. /goto-local physicsbare-name form: teleports to the named point of the scene you are currently standing in.
  3. /goto-local far-light → also verify camera orientation.
  4. Reserved words are not names: /goto-local random / /goto-local crowd🔴 Invalid parameters + usage.
  5. /goto-local nosuch → teleport to your current parcel, default spawn selection + warning in logs (soft fallback).
  6. Regression: /goto-local 0,0 unchanged.

4. Deep link — cold start (client closed)

decentraland://?realm=flutterecho.dcl.eth&dclenv=org&spawnpoint=physics
  1. Client starts and the initial landing is the physics playground (not the default spawn).
  2. With a position: decentraland://?realm=flutterecho.dcl.eth&dclenv=org&position=0,0&spawnpoint=far-light.
  3. Bad name: ...&spawnpoint=nosuch → lands at default spawn + warning in logs.

5. Deep link — runtime (client already running)

  1. While standing in another realm (e.g. Genesis), open the same deep link from the OS → realm change, land at the named point.
  2. Already inside the world, deep link with position + spawnpoint → in-world teleport to the named point.
  3. Already inside the world, deep link with spawnpoint but without position (decentraland://?realm=flutterecho.dcl.eth&dclenv=org&spawnpoint=far-light) → in-world teleport to the named spawn point of the current scene (previously this silently did nothing).
  4. Already inside the world, deep link with realm only (no position, no spawnpoint) → nothing happens (pre-existing "already in realm" behavior, unchanged).

6. Launch parameters (cmd / CI)

Explorer.exe --realm flutterecho.dcl.eth --dclenv org --spawnpoint physics
  1. Same expectations as the cold-start deep link (CLI args and deep link query params share one parser).
  2. Genesis variant: --realm <genesis peer> --position 0,0 --spawnpoint <name>.

7. Cross-cutting edge cases (any entry point)

  1. Case-insensitivity: /goto-local PHYSICS, /goto flutterecho/Far-Light, ...&spawnpoint=CENTER → all match.
  2. Range scatter: /goto-local scatter several times in a row → each landing is a different spot within the 26..38 square (players must not stack on one point).
  3. Duplicate names: /goto-local twin repeatedly → always lands at (10, 50) (first declared), never (50, 10); warning about duplicates in logs.
  4. Unknown name: never breaks the teleport — always default selection + Spawn point '<name>' not found... in logs. Nothing red in chat.
  5. Trailing whitespace: /goto flutterecho/center (trailing spaces) → works (previously any trailing space failed validation of any command).
  6. Grammar junk stays world-names (unchanged behavior): /goto myworld//lobby, /goto myworld/0,0/ → treated as a (non-existent) world → 🔴 ... doesn't exist or not reachable.
  7. /goto flutterecho/randomrandom here is a spawn point name (soft fallback if the scene has none), not the random-parcel flag.
  8. Initial world entry without a name (jump-in, navmap, plain deep link) → default spawn, no behavior change.

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered (name matching runs only during teleport resolution; no per-frame cost, no allocations added to hot paths)
  • 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.

popuz added 6 commits July 13, 2026 18:33
…rget

  No behavior change. All /goto argument parsing moves out of GoToChatCommand
  into a pure static ChatParamUtils.ParseGotoTarget returning a GotoTarget
  value (world, parcel, spawn point, random/crowd flags). The command becomes
  a thin dispatcher. GotoTarget.SpawnPoint is always null in this slice — the
  field exists so the named-spawn-point grammar (#6848) lands additively.

  Covers the existing grammar with unit tests: x,y, random, crowd, world,
  world/x,y, plus junk/edge inputs.
  PickSpawnPoint selects a case-insensitively matched named point directly,
  bypassing the default/closest logic (unmatched name warns and falls back).
  The name is threaded as string? spawnPointName through the same-realm
  teleport chain, mirroring landOnParcel. ParseGotoTarget accepts x,y/name.
…x,y/name

    The spawn point name is threaded through the realm-change chain
    (TryChangeRealmAsync → TeleportParams → MoveToParcelInNewRealmTeleportOperation
    → TeleportToSpawnPointOperationBase) and through TryChangeToGenesisAsync,
    so /goto x,y/name also works from a world. For the coordinate-less form the
    manifest still picks the scene parcel; the name selects the point within it.
    ParseGotoTarget accepts world/name and world/x,y/name.
@popuz popuz self-assigned this Jul 14, 2026
@popuz popuz added the force-build Used to trigger a build on draft PR label Jul 14, 2026
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

badge

New build in progress, come back later!

@popuz popuz changed the title Feat/support scene named spawn points feat: support scene named spawn points Jul 14, 2026
@popuz
popuz marked this pull request as ready for review July 15, 2026 10:59
@popuz
popuz requested review from a team as code owners July 15, 2026 10:59
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Warnings count reduced: 14668 => 14644

Warnings/errors in files changed by this PR (13)
File Line Rule Message
Assets/DCL/Infrastructure/SceneLifeCycle/Tests/ControlSceneUpdateLoopSystemShould.cs 145 InconsistentNaming Name 'ChangeSceneFPS' does not match rule 'members_should_be_pascal_case'. Suggested name is 'ChangeSceneFps'.
Assets/DCL/Infrastructure/Global/Dynamic/RealmLaunchSettings.cs 30 InconsistentNaming Name 'EditorSceneStartPosition' does not match rule 'non_public_members_should_be_camel_case'. Suggested name is 'editorSceneStartPosition'.
Assets/DCL/Infrastructure/Global/Tests/EditMode/RealmLaunchSettingsShould.cs 189 InconsistentNaming Name 'IgnoreMacOSRealmInvalidation' does not match rule 'members_should_be_pascal_case'. Suggested name is 'IgnoreMacOsRealmInvalidation'.
Assets/DCL/Infrastructure/Global/Dynamic/Bootstraper.cs 342 InconsistentNaming Name 'OpenDefaultUI' does not match rule 'members_should_be_pascal_case'. Suggested name is 'OpenDefaultUi'.
Assets/DCL/Infrastructure/SceneLifeCycle/Tests/ControlSceneUpdateLoopSystemShould.cs 75 InconsistentNaming Name 'StartSceneWithCorrectFPS' does not match rule 'members_should_be_pascal_case'. Suggested name is 'StartSceneWithCorrectFps'.
Assets/DCL/Infrastructure/Global/Dynamic/RealmLaunchSettings.cs 24 InconsistentNaming Name 'enabled' does not match rule 'members_should_be_pascal_case'. Suggested name is 'Enabled'.
Assets/DCL/Infrastructure/Global/Dynamic/RealmLaunchSettings.cs 25 InconsistentNaming Name 'parcels' does not match rule 'members_should_be_pascal_case'. Suggested name is 'Parcels'.
Assets/DCL/Chat/Commands/ChatTeleporter.cs 48 InconsistentNaming Name 'realmURL' does not match rule 'non_public_members_should_be_camel_case'. Suggested name is 'realmUrl'.
Assets/DCL/Chat/Commands/ChatTeleporter.cs 88 InconsistentNaming Name 'realmURL' does not match rule 'non_public_members_should_be_camel_case'. Suggested name is 'realmUrl'.
Assets/DCL/Chat/Commands/ChatTeleporter.cs 115 InconsistentNaming Name 'realmURL' does not match rule 'parameters_should_be_camel_case'. Suggested name is 'realmUrl'.
Assets/DCL/Chat/Commands/ChatTeleporter.cs 129 InconsistentNaming Name 'realmURL' does not match rule 'parameters_should_be_camel_case'. Suggested name is 'realmUrl'.
Assets/DCL/Chat/Commands/ChatTeleporter.cs 96 RedundantArgumentDefaultValue The parameter 'allowsSpawnPointerOverride' has the same default value
Assets/DCL/Infrastructure/Global/Dynamic/RealmLaunchSettings.cs 41 UnusedMember.Local Field 'portableExperiencesEnsToLoadAtGameStart' is never used

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

PR Review — feat: support scene named spawn points

STEP 2 — Root-cause check ✅

This PR adds a new feature (named spawn point selection) to the teleport pipeline. The diff addresses a genuine gap: scene manifests already declare named spawn points, but the explorer had no way to target them. The implementation is a feature addition, not a symptom fix. PASS.

STEP 3 — Design & integration ✅

Threading approach: The spawn point name is threaded as string? spawnPointName = null through the existing teleport pipeline methods (ChatTeleporter → RealmNavigator → TeleportController → TeleportOperations → PlayerTeleportIntent → TeleportPositionCalculationSystem → TeleportUtils.PickSpawnPoint). This is the minimally invasive approach — it extends existing method signatures with an optional parameter rather than requiring a teleport-request-object refactoring. The parameter is never stored long-term; it flows through a single teleport invocation.

New units introduced:

  • GotoTarget — transient readonly struct for parsed /goto command results. Not a long-lived unit; no lifecycle management. Justified: it replaces ad-hoc parsing logic that was spread across GoToChatCommand, and the grammar-table test suite (ChatParamUtilsShould) validates every form.
  • DeepLinkExtensions — pure static extension methods extracted from DeepLinkHandleImplementation. The original methods were private static; the extraction is justified because the same deep-link parsing logic is now shared with the spawn point parameter, and the extension method pattern matches the DeepLink API surface cleanly.

MANDATORY OWNER SEARCH: No new systems, plugins, managers, controllers, or long-lived units that manage entity/scene/connection lifecycles are introduced. The spawn point name is a transient parameter flowing through a single teleport invocation. No persistent state, no per-frame reconciliation, no collections outside ECS. PASS — no owner-search violations.

TEARDOWN / CONSUMPTION TRACE: No new subscriptions, event hookups, connections, rooms, buffers, or measurements are added. The only new state is StartParcel.SpawnPointName (consumed once at startup alongside the parcel). PASS.

STEP 4 — Member audit ✅

Member Consumers Assessment
GotoTarget.{World,Parcel,SpawnPoint,IsRandom,IsCrowd} GoToChatCommand, GoToLocalChatCommand, tests Multi-consumer; well-scoped struct fields
ChatParamUtils.ParseGotoTarget() GoToChatCommand, GoToLocalChatCommand, tests Multi-consumer; replaces scattered parsing
ChatParamUtils.IsSpawnPointName() ParseGotoTarget, GoToLocalChatCommand.ValidateParameters Dual-use; validates spawn point segment
DeepLinkExtensions.{Realm,Position,SpawnPoint,Community}() DeepLinkHandleImplementation Single consumer, but justified extraction (shared parsing surface, replaces private static methods)
StartParcel.SpawnPointName TeleportStartupOperation, DeepLinkHandleImplementation.Assign Dual-use; consumed at startup
TeleportParams.SpawnPointName MoveToParcelIn{New,Same}RealmTeleportOperation Dual-consumer
PlayerTeleportIntent.SpawnPointName TeleportPositionCalculationSystem Single consumer; ECS component field used by the system that calculates teleport position — correct placement

No single-use intermediaries, no absent-≠-false predicates, no redundant derivations. PASS.

STEP 5 — Line-level review

[P2] GoToLocalChatCommand.ExecuteCommandAsync — semantic gap in bare-name form (see inline comment)

In the bare-name form (/goto-local physics), ParseGotoTarget returns the token in target.World (since it can't distinguish world names from spawn-point names at parse time). ExecuteCommandAsync then passes target.World as the spawnPointName parameter, which reads as passing a world name where a spawn point name is expected. A clarifying comment at the call site would make this self-documenting.

STEP 5 — Security review ✅

  • Input validation: Spawn point names are validated by IsSpawnPointName (non-empty, no commas, no slashes). The name is used only for case-insensitive string comparison against scene metadata — never in SQL, file I/O, URL construction, or any injection-prone context.
  • Deep link parameters: Extracted via DeepLink.ValueOf() which handles URL decoding. Validated with string.IsNullOrEmpty.
  • CLI arguments: Parsed via ApplicationParametersParser. Validated with !string.IsNullOrEmpty.
  • Log messages: Spawn point names appear in ReportHub.LogWarning (not user-facing chat). No injection risk.
  • No secrets, credentials, or sensitive data involved. No security issues found.

Positive observations

  • Core mechanism is clean: single chokepoint in TeleportUtils.PickSpawnPoint with soft fallback — teleports never fail because of a bad name.
  • Named lookup uses manual iteration (not LINQ), consistent with CLAUDE.md §4.
  • Excellent test coverage: TeleportUtilsShould (named selection core), ChatParamUtilsShould (grammar table — 13 test cases covering every form), RealmLaunchSettingsShould (deep link / CLI), ChatTeleporterShould (already-in-realm branching).
  • TrimEnd() in CommandsHandleChatMessageBus is a good cross-cutting fix — trailing whitespace previously broke all command validation, not just /goto.
  • DeepLinkExtensions extraction is a clean refactoring that eliminates duplicated parsing logic.

STEP 6 — Complexity

COMPLEX — Modifies shared interfaces (IRealmNavigator, ITeleportController) and the teleport pipeline across 6+ assemblies, introduces new parsing grammar, and changes cold-start and runtime teleport flow.

STEP 7 — QA assessment

QA_REQUIRED: YES — Changes affect user-facing teleport behavior across all entry points (chat commands, deep links, CLI arguments, cold start, runtime realm changes). The PR description includes a thorough test matrix.

STEP 8 — Non-blocking warnings

None. Main.unity is not modified.

CI Status

  • ✅ Playmode tests pass (23,542 passed)
  • ❌ Editmode tests: 1 failure — ControlSceneUpdateLoopSystemShould.StartWorldSceneWithoutReadinessReportRegardlessOfSceneRoom. This test is in ControlSceneUpdateLoopSystemShould.cs, a file not touched by this PR, and tests scene update loop behavior unrelated to spawn points. Likely a pre-existing flaky test.
  • ⏳ Builds (Windows, macOS) and Lint still in progress
  • ✅ PR title follows semantic commit format (feat: ...)

REVIEW_RESULT: PASS ✅
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: Modifies shared interfaces (IRealmNavigator, ITeleportController) and the teleport pipeline across 6+ assemblies, introduces new parsing grammar, and changes cold-start and runtime teleport flow
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by mikhail-dcl via GitHub

Comment thread Explorer/Assets/DCL/Chat/Commands/GoToLocalChatCommand.cs
@popuz popuz added clean-build Used to trigger clean build on PR and removed force-build Used to trigger a build on draft PR labels Jul 15, 2026
@popuz
popuz requested review from DafGreco and removed request for Ludmilafantaniella July 20, 2026 08:22
…ed-spawn-points

# Conflicts:
#	Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs
@popuz
popuz requested review from dalkia and removed request for mikhail-dcl July 20, 2026 09:28
@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@popuz popuz removed the clean-build Used to trigger clean build on PR label Jul 21, 2026

@DafGreco DafGreco left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✔️ PR reviewed and approved by QA on both platforms following instructions playing both happy and un-happy path

Regressions for this ticket had been performed in order to verify that the normal flow is working as expected:

  • [✔️ ] Backpack and wearables in world
  • [✔️ ] Emotes in world and in backpack
  • [✔️ ] Teleport with map/coordinates/Jump In
  • [✔️ ] Chat and multiplayer
  • [✔️ ] Profile card
  • [ ✔️ ] Skybox
  • [ ✔️ ] Settings

Evidence of all points

20260715-1617-22.4109911.mp4
20260715-1621-16.1543480.mp4
Image

Player (24).log

Image Image
20260716-0829-21.6603936.mp4
20260716-0825-13.4973618.mp4
20260716-0835-41.0890537.mp4
20260717-1033-43.7435144.mp4
20260716-0834-29.9240551.mp4

Comment thread Explorer/Assets/DCL/AssetsProvision/Tests/DuplicateAddressablesTest.cs Outdated

@pravusjif pravusjif left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@claude

This comment has been minimized.

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @popuz's task in 1m 8s —— View job


Security-focused review (re-run on sync) — workflow / dependency / CI files

  • Enumerate dependency / workflow / CI files changed vs origin/dev
  • Confirm dependency surface (manifest, lockfile, plugins, asmdefs)
  • Re-diff the three workflow files
  • Verify status of previously-flagged findings (.passed arithmetic, W.2 heredoc)
  • Classify remaining risk & post verdict

Scope

Changed review surface vs origin/dev:

.github/workflows/pr-comment-test-failures.yml   (ADDED    +119)
.github/workflows/pr-comment-warnings.yml         (MODIFIED  +9/-10)
.github/workflows/test.yml                         (MODIFIED  +61/-1)

Dependency side: PASS. Packages/manifest.json, packages-lock.json, and Explorer/Assets/Plugins/ are unchanged (empty diff). The only asm change is DCL.Chat.Commands.Tests.asmref — a test-only assembly reference ({"reference":"GUID:…"}), no precompiled assembly, no visibility change. No binaries added. Workflow-only security review; feature code is out of scope here (covered separately by Jarvis → REVIEW_RESULT: PASS).

The workflow diff is byte-identical to the version reviewed on the previous sync — nothing new to flag.


✅ MEDIUM .passed arithmetic injection — still FIXED

pr-comment-test-failures.yml:74-77 validates the artifact value before the $(( )) context:

# never let a non-numeric value reach the arithmetic context.
p=$(jq -r '.passed' "$file"); [[ "$p" =~ ^[0-9]+$ ]] || p=0
passed=$((passed + p))

Non-numeric coerces to 0; the command-substitution / array-subscript vector stays closed. .failed | length is always numeric. 👍

✅ W.2 heredoc injection — still FIXED

Both comment-composing workflows use a random heredoc delimiter (DELIM="EOF_$(uuidgen)") and strip newlines from rendered .failed[] / .pr_findings[] values with gsub("[\r\n]"; " "). The $GITHUB_OUTPUT early-close vector is closed.

test.yml extractor step — clean

  • "Extract failed test names" runs a single-quoted Python heredoc (<<'PY') reading only os.environ (ARTIFACTS_PATH, TEST_MODE) — no shell/expression interpolation into the script body. No injection channel.
  • Runs in the untrusted test job (no write scope); results cross to the trusted workflow_run companion only via an artifact. Correct trust split — least-privilege contents: read + pull-requests: write on the companion, PR number resolved via toJSON env + jq (no inline ${{ }} expression injection).
  • FINDINGS_TOTAL is routed through an env var (test.yml:447) instead of inline ${{ }} in the jq --argjson line — expression-injection hardening retained.
  • upload-artifact uses default retention (≤14 d) and the job runs no LLM — W.9 does not apply.
  • No pull_request_target, no PR-head checkout+execute, no LLM prompt files touched → W.1/W.3/W.4/W.8 not applicable.

🟡 LOW (pre-existing, carried over) — W.7: tag-pinned actions in write-capable jobs

Write-capable jobs still pin third-party actions to mutable tags rather than SHAs — actions/download-artifact@v4, peter-evans/find-comment@v2, peter-evans/create-or-update-comment@v3 (comment workflows), actions/upload-artifact@v4/@v6 (test.yml). A compromised upstream tag would inherit pull-requests: write. Recommend SHA-pinning (Dependabot for managed bumps). Pre-existing and non-blocking. (.github/workflows/ is App-protected — @popuz would apply this; I can't modify workflow files.)


Verdict

No HIGH, no open MEDIUM. Both previously-flagged MEDIUM findings (.passed arithmetic, W.2 heredoc) remain fixed, the test.yml extractor step is injection-clean, and the dependency surface is unchanged (only a test-only .asmref added). The single remaining item is the pre-existing tag-vs-SHA pinning (W.7, LOW).

DEPENDENCY_REVIEW: PASS

Scope: security review of the workflow / dependency / CI files only. Feature-code review is handled separately.

@popuz
popuz merged commit 346cba0 into dev Jul 22, 2026
21 of 22 checks passed
@popuz
popuz deleted the feat/support-scene-named-spawn-points branch July 22, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants