Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
47b4eb1
[feat] 51 scatter set format: params + seed + overrides, a determinis…
AlexZ005 Oct 22, 2026
512623e
[refactor] 51-surface: the Inspector Scatter section moves into its o…
AlexZ005 Oct 22, 2026
fb2bcfa
[feat] 51 E4: edit scatter instances - select + gizmo, box ops, rotat…
AlexZ005 Oct 22, 2026
f470049
[feat] 51-surface phase 1: scatterSurfaceCore - fill, masks, spacing …
AlexZ005 Oct 22, 2026
e6f1bfe
[feat] 51 E1 scatter sources: pack items, prefabs + objects as their …
AlexZ005 Oct 22, 2026
15a9ccf
[fix] 51-surface: the scatterSurfaceCore fixture test takes @ts-noche…
AlexZ005 Oct 22, 2026
96daad1
Merge remote-tracking branch 'origin/feat/51-sources' into feat/51-su…
AlexZ005 Oct 22, 2026
fb6d161
[feat] 51-surface phase 2: Add scatter on any mesh, fill + masks + sp…
AlexZ005 Oct 22, 2026
39de914
[feat] 51-surface phase 3: a group of meshes (a placed model) is a sc…
AlexZ005 Oct 22, 2026
561bf51
[fix] 51 E4: a selection survives its own revision; e2e for the edit …
AlexZ005 Oct 22, 2026
f4af84a
[feat] 51 E5: convert scatter instances to objects and copies to a sc…
AlexZ005 Oct 22, 2026
9199cbf
[feat] 51 device thinning: far scatter fades out to stay inside the d…
AlexZ005 Oct 22, 2026
31ae5ac
[fix] 51-surface: a group host's surface takes its children in a peer…
AlexZ005 Oct 22, 2026
94e508a
Merge origin/feat/51-sources: sources, tint, sink, thinning into the …
AlexZ005 Oct 22, 2026
e29151d
Merge remote-tracking branch 'origin/feat/51-sources' into feat/51-union
AlexZ005 Oct 23, 2026
caeb2b9
Merge remote-tracking branch 'origin/feat/51-surface' into feat/51-union
AlexZ005 Oct 23, 2026
54ce6ac
Merge origin/feat/51-edit (#416): E4 edit instances + E5 convert, ont…
AlexZ005 Oct 23, 2026
1f3b9dd
[test] 51 union: scatterEditAdapter writes edits on the set format (o…
AlexZ005 Oct 23, 2026
660f7f1
[deps] three add-ons: three-mesh-bvh 0.9.16, @threlte/extras 9.22.3
AlexZ005 Oct 23, 2026
95dcc88
[deps] svelte 5.57.2, svelte-check 4.7.7, @lucide/svelte 1.61.0
AlexZ005 Oct 23, 2026
864a6e6
[deps] vite 8.3.5, vitest 5.0.5 (closes both @vitest/mocker advisories)
AlexZ005 Oct 23, 2026
e78cb84
[deps] prettier 3.9.10, iwer + @iwer/devui 2.7.0, @axe-core/playwrigh…
AlexZ005 Oct 23, 2026
342bba6
[deps] tools/agent: @roamhq/wrtc 0.8.0 -> 0.10.0
AlexZ005 Oct 23, 2026
f50bd27
[deps] playwright 1.63.0 -> 1.64.1 (Chromium 156.0.8078.4, chromium-1…
AlexZ005 Oct 23, 2026
e4ea26c
[feat] deps-auto 3: pc-battery — the GPU half of the dependency gate,…
AlexZ005 Oct 23, 2026
7a9f5c8
[fix] deps-auto 3b: pc-battery needs no jq; a pending status older th…
AlexZ005 Oct 23, 2026
209225a
[fix] deps-auto 2b: the shard runner tells the suites where the app i…
AlexZ005 Oct 23, 2026
5c55f9e
Merge tag 'v1.42.0' into feat/51-int
AlexZ005 Oct 23, 2026
8d05db7
Merge remote-tracking branch 'origin/feat/deps-1' into feat/51-int
AlexZ005 Oct 23, 2026
e8742a0
Merge remote-tracking branch 'origin/deps' into feat/51-int
AlexZ005 Oct 23, 2026
8c88200
[fix] 51 union E5: To objects brings a logic prefab back with its log…
AlexZ005 Oct 23, 2026
9275fb2
[test] 51 union: suite union-51 (prefab/pack To objects, determinism …
AlexZ005 Oct 23, 2026
21cab9e
[fix] deps-auto 2c: the shard runner brings the local PeerJS signalin…
AlexZ005 Oct 24, 2026
a914391
[test] union-51: U1 presses the row's To objects once it holds still …
AlexZ005 Oct 24, 2026
6dab729
[fix] deps-auto 2d: shards run two suites at a time, report as they g…
AlexZ005 Oct 24, 2026
913ee7d
probe: classification run (20 shards, par 1, no retry) — reverted after
AlexZ005 Oct 24, 2026
eff900a
[fix] 51 union E5: objects made from scatter keep the model's name on…
AlexZ005 Oct 24, 2026
24994f5
[test] lock-search follows 51: the Distant scatter settings row (7 qu…
AlexZ005 Oct 24, 2026
e7cfcb0
[feat] deps-auto 2e: the battery splits into a required set that gate…
AlexZ005 Oct 24, 2026
4c0f266
[fix] deps-auto 3c: pc-battery verdicts tuned on a first desktop run …
AlexZ005 Oct 24, 2026
f4b128f
[fix] deps-auto 2f: the required set is what a runner passes reliably…
AlexZ005 Oct 24, 2026
2d562c5
[fix] 51 union: Use as scatter source moves under Physics & effects ▸…
AlexZ005 Oct 24, 2026
03fc009
Scripts, behaviours and custom HUD elements run in a sandbox
AlexZ005 Oct 24, 2026
3169216
[feat] deps-auto 2g: a required red gets two more tries on a fresh ru…
AlexZ005 Oct 24, 2026
a413d7d
Script calls take a compact path; the engine is pre-bundled in dev
AlexZ005 Oct 24, 2026
26dd3e1
Keys stay out of the diagnostics bundle; SECURITY.md describes the sc…
AlexZ005 Oct 24, 2026
f0b7601
Builds ship a Content-Security-Policy
AlexZ005 Oct 24, 2026
ca3a26b
An optional play origin (VITE_PLAY_ORIGIN)
AlexZ005 Oct 24, 2026
c4aab77
An invite that names another signaling server asks before switching
AlexZ005 Oct 24, 2026
a724228
e2e: what scene code can see from inside the sandbox
AlexZ005 Oct 24, 2026
8647bf3
[fix] deps-auto 2h+3d: the required set drops what went red in runs 7…
AlexZ005 Oct 24, 2026
e3f4a5e
[test] keyboard-walk: the wrap check skips a toast's ✕ (a Scene saved…
AlexZ005 Oct 24, 2026
40132fc
The script engine starts loading with a scene that carries code
AlexZ005 Oct 24, 2026
ad12f25
CHANGELOG 1.42.1
AlexZ005 Oct 24, 2026
14408fa
[fix] deps-auto 2i+3e: throw-velocity to optional (timing-dependent o…
AlexZ005 Oct 24, 2026
1bb1a79
[fix] deps-auto 3f: pc-battery --poll — the jq-removal edit left a st…
AlexZ005 Oct 24, 2026
04ce9a3
[fix] 51 union E5: Library and pack instances become objects one by o…
AlexZ005 Oct 24, 2026
5fe47e8
[docs] deps-auto 5a: RELEASING 'Dependency updates' describes the bui…
AlexZ005 Oct 24, 2026
631eca1
Merge remote-tracking branch 'origin/deps' into feat/51-int
AlexZ005 Oct 24, 2026
4661b6c
Merge the 1.43.0 union (feat/51) into the 1.43.1 security patch
AlexZ005 Oct 24, 2026
b2bc9a7
e2e behaviours: the first load on a page includes the sandbox engine
AlexZ005 Oct 24, 2026
bedf172
Merge pull request #444 from theprototype-app/feat/51
AlexZ005 Oct 25, 2026
5811f4f
1.43.0
AlexZ005 Oct 25, 2026
fd40425
Merge v1.43.0 into the 1.43.1 security patch
AlexZ005 Oct 25, 2026
97b2be7
1.43.1
AlexZ005 Oct 25, 2026
e0d7ef1
[fix] body-parser 1.20.9 (root, dev) + 2.4.0 (tools/agent) for GHSA-2…
AlexZ005 Oct 25, 2026
c07e36b
Merge pull request #467 from theprototype-app/fix/body-parser
AlexZ005 Oct 25, 2026
b14c701
[docs] CHANGELOG 1.43.2: the body-parser fix (GHSA-27r3-jxmh-p828)
AlexZ005 Oct 25, 2026
de657a4
1.43.2
AlexZ005 Oct 25, 2026
52fecfb
e2e: E2E_CPU_THROTTLE runs any suite with CDP CPU throttling (GitHub-…
AlexZ005 Oct 25, 2026
5ebdac8
peer sync: room edits made while an approved joiner's dial-back opens…
AlexZ005 Oct 25, 2026
e612886
e2e: connect() waits for both ends' conn to open (the settle is a min…
AlexZ005 Oct 25, 2026
5259d4d
e2e: the scatter suites' cross-peer checks start after a wire barrier…
AlexZ005 Oct 25, 2026
4e9e522
CLAUDE.md: golden rule 2 — broadcast skips an opening conn; the dial-…
AlexZ005 Oct 25, 2026
f78c238
e2e: patient real clicks in the scatter suites; scatter-colliders bac…
AlexZ005 Oct 25, 2026
015fb37
e2e: scatter-surface's 'Add scatter' wait is patient too (scroll + at…
AlexZ005 Oct 25, 2026
c6e278b
e2e: scatter-colliders waits on simulated time; scatter-sources' loca…
AlexZ005 Oct 25, 2026
f9227df
battery.json: scatter-colliders estimate 600 s (1126-1141 s on GitHub…
AlexZ005 Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,19 @@ VITE_SKIP_WELCOME=false
# `<package version>-preview`; set VITE_RELEASE_LABEL (≤ 24 chars) to name it explicitly.
# VITE_ERROR_REPORTS_URL=
# VITE_RELEASE_LABEL=

# 69 T3: Content-Security-Policy for BUILDS (`npm run dev` sends none). The policy is written into
# every page as a <meta> (see csp.config.js) and `frame-ancestors` into build/_headers (Cloudflare
# Pages reads it; another server should send that header line itself). All optional:
# VITE_CSP_SCRIPT_SRC= # extra script origins, e.g. https://static.cloudflareinsights.com
# VITE_CSP_CONNECT_SRC= # REPLACES the default connect-src ('self' https: wss: + localhost)
# VITE_CSP_EXTRA_CONNECT= # added to the default connect-src
# VITE_CSP_IMG_SRC= # REPLACES the default img-src / media-src ('self' data: blob: https:)
# VITE_CSP_FRAME_ANCESTORS= # who may frame the app (default * — see VITE_PLAY_ORIGIN)
# VITE_CSP=off # ship no policy (a deployment that sets its own at the edge)

# 69 T4: an optional second origin for PLAYING (same build, served twice). Play links and embeds point
# there; storage is per origin, so a played scene's page holds none of the editor's (login, AI keys,
# autosaves). On that origin the page is player-only. Unset = one origin for everything (as before).
# VITE_PLAY_ORIGIN=https://play.example.com
# VITE_APP_ORIGIN=https://app.example.com # where the player's "Open in the app" link goes
110 changes: 110 additions & 0 deletions .github/workflows/battery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# deps-auto — ONE SHARD SET OF THE E2E BATTERY (called by deps.yml: the required set, then the optional set).
# scripts/ci/battery.cjs decides which suites a shard runs (tests/e2e/battery.json); this file only builds the
# machine: the lockfile's Playwright container, core + the sibling content at the refs battery.json pins, the
# module zips, then the shard (its own dev server + local signaling server) and the artifact.
name: battery

on:
workflow_call:
inputs:
set:
description: required | optional | confirm (re-runs the required set's reds on fresh runners)
type: string
required: true
matrix:
description: the plan job's matrix JSON for this set
type: string
required: true
playwright:
type: string
required: true
refs:
description: battery.json's refs (JSON)
type: string
required: true
deadline:
description: minutes after which a shard starts no new suite
type: number
required: true

permissions:
contents: read

jobs:
shard:
name: ${{ inputs.set }} ${{ matrix.shard }}/${{ matrix.of }}
runs-on: ubuntu-latest
# a hard cap far past any deadline: the deadline is what ends a shard normally
timeout-minutes: 110
# the browsers and their OS libraries at exactly the lockfile's Playwright: no 5-minute apt step per shard
container:
image: mcr.microsoft.com/playwright:v${{ inputs.playwright }}-noble
options: --ipc=host
strategy:
fail-fast: false
matrix: ${{ fromJSON(inputs.matrix) }}
# the sibling checkouts sit beside core, where the suites' own defaults look for them. Inside a
# container the workspace is NOT ${{ github.workspace }} (that is the host path): use the env vars.
env:
E2E_NO_PERF_OFFER: '1'
MODE: quick
steps:
- uses: actions/checkout@v4
with:
path: core
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(inputs.refs).scenes.repo }}
ref: ${{ fromJSON(inputs.refs).scenes.ref }}
path: scenes
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(inputs.refs).modules.repo }}
ref: ${{ fromJSON(inputs.refs).modules.ref }}
path: modules
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(inputs.refs).packs.repo }}
ref: ${{ fromJSON(inputs.refs).packs.ref }}
path: packs
- uses: actions/setup-node@v4
with:
node-version-file: core/.nvmrc
cache: npm
cache-dependency-path: |
core/package-lock.json
modules/package-lock.json
- name: install
run: |
command -v openssl > /dev/null || (apt-get update -qq && apt-get install -y -qq --no-install-recommends openssl > /dev/null)
(cd core && npm ci --no-audit --no-fund && npm run certs)
# the module zips the game suites install (MODULES_REPO)
(cd modules && npm ci --no-audit --no-fund && node scripts/pack.cjs --all --force)
# confirm: what the required shards left red (or unrun), from their artifacts
- uses: actions/download-artifact@v4
if: inputs.set == 'confirm'
with:
pattern: battery-required-*
path: prev
- name: battery shard ${{ matrix.shard }}/${{ matrix.of }} (~${{ matrix.est }} min, ${{ matrix.suites }} suites)
working-directory: core
run: |
W=$GITHUB_WORKSPACE; S=$W/scenes; G=$S/games
export SCENES_DIR=$S SCENES_REPO=$S SHELL_SCENES_DIR=$G GAMES_DIR=$G FBW_SCENES=$S/examples
export MODULES_REPO=$W/modules MODULES_DIR=$W/modules PACKS_DIR=$W/packs
for g in dungeon-realms:DUNGEON_REALMS escape-room:ESCAPE football:FOOTBALL jam-room:JAM_ROOM marble-maze:MARBLE \
mini-golf:MINIGOLF sky-run:SKY_RUN stars-room:STARS_ROOM target-toss:TARGET_TOSS towers:TOWERS \
untangle:UNTANGLE waves:WAVES race:RACE; do export "${g#*:}_TPSCENE=$G/${g%%:*}/scene.tpscene"; done
export JELLY_TPSCENE=$S/examples/jelly-room/scene.tpscene TANK_TPSCENE=$S/examples/fluid-tank-toy/scene.tpscene \
POOL_TPSCENE=$S/examples/pool-party/scene.tpscene
node scripts/ci/battery.cjs run --set ${{ inputs.set }} ${{ inputs.set == 'confirm' && '--from "$W/prev"' || '' }} --shard ${{ matrix.shard }} --of ${{ matrix.of }} --serve --port 5173 \
--deadline ${{ inputs.deadline }} --out "$W/battery-out"
- uses: actions/upload-artifact@v4
if: always()
with:
name: battery-${{ inputs.set }}-${{ matrix.shard }}
# every suite's log; screenshots and evidence only for the suites that failed (the runner deletes the
# rest). Beside core, not in it: an artefact inside the served tree reloads the app mid-test.
path: battery-out
retention-days: 14
if-no-files-found: ignore
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ on:
push:
branches: [main, release/next]
workflow_call:
inputs:
# deps.yml: the e2e battery runs these suites itself, so the two browser jobs below stand down
battery:
type: boolean
default: false

jobs:
build:
Expand Down Expand Up @@ -149,6 +154,7 @@ jobs:
- run: npm run test:unit

e2e-smoke:
if: ${{ !inputs.battery }}
runs-on: ubuntu-latest
# NOT a gate yet — see the header. Flip this to false once it has been green on a few
# PRs in a row; until then a red here is a signal to read, not a block.
Expand Down Expand Up @@ -195,6 +201,7 @@ jobs:
# screen-reader basics (live regions, selection announce, the object-list tree). One page at a
# time and DOM-only assertions, so — unlike e2e-smoke — it gates.
a11y:
if: ${{ !inputs.battery }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
132 changes: 65 additions & 67 deletions .github/workflows/deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# target-branch). Every PR into `deps` runs the whole release gate here: ci.yml (build, the
# svelte-check ratchet, vitest, scene-lint, kit-diff, a11y, supply chain) and perf-budget.yml, both
# called as reusable workflows, then the integrators' e2e battery split into shards on
# GitHub-hosted runners (SwiftShader: no GPU). `deps-gate` is the one required check; the ruleset on
# GitHub-hosted runners (SwiftShader: no GPU, battery.yml): the REQUIRED set gates; the OPTIONAL set (red on
# SwiftShader with unchanged code, each with its reason in tests/e2e/battery.json) runs after it and is only
# reported. `deps-gate` is the one required check; the ruleset on
# `deps` also requires the `pc-battery` commit status, which the GPU check on a desktop posts for a
# PR that changes what the app ships (scripts/ci/pc-battery.sh — pulled from there, no runner of ours
# in this public repo) and this workflow posts as "not needed" for every other PR.
Expand Down Expand Up @@ -82,17 +84,29 @@ jobs:
needs: scope
if: needs.scope.outputs.gate_ci == 'true'
uses: ./.github/workflows/ci.yml
with:
battery: true

# the budget gate in three parallel parts (~20 min each instead of ~60 in one)
perf:
needs: scope
if: needs.scope.outputs.gate_ci == 'true'
strategy:
fail-fast: false
matrix:
part: [1, 2, 3]
uses: ./.github/workflows/perf-budget.yml
with:
part: ${{ matrix.part }}
parts: 3

plan:
needs: scope
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
optional_matrix: ${{ steps.plan.outputs.optional_matrix }}
optional_shards: ${{ steps.plan.outputs.optional_shards }}
playwright: ${{ steps.pw.outputs.version }}
refs: ${{ steps.refs.outputs.refs }}
steps:
Expand All @@ -104,81 +118,60 @@ jobs:
- id: refs
run: echo "refs=$(node -p "JSON.stringify(require('./tests/e2e/battery.json').refs)")" >> "$GITHUB_OUTPUT"

# the suites that gate (sized by battery.json to finish inside the hour)
e2e:
name: e2e ${{ matrix.shard }}/${{ matrix.of }}
needs: plan
uses: ./.github/workflows/battery.yml
with:
set: required
matrix: ${{ needs.plan.outputs.matrix }}
playwright: ${{ needs.plan.outputs.playwright }}
refs: ${{ needs.plan.outputs.refs }}
deadline: 75

# SwiftShader makes any one suite fail now and then, twice on one loaded runner too: a required red (or a suite
# the deadline left unrun) gets two more tries on a FRESH runner before it counts
confirm:
needs: [plan, e2e]
if: always() && needs.e2e.result == 'failure'
uses: ./.github/workflows/battery.yml
with:
set: confirm
matrix: '{"include":[{"shard":1,"of":3,"suites":0,"est":0},{"shard":2,"of":3,"suites":0,"est":0},{"shard":3,"of":3,"suites":0,"est":0}]}'
playwright: ${{ needs.plan.outputs.playwright }}
refs: ${{ needs.plan.outputs.refs }}
deadline: 20

# red or flaky on SwiftShader with the same red on unchanged code: run, reported, never waited for. They start
# AFTER the required shards and the confirm step (the free plan runs 20 jobs at once); deps-gate does not need them.
e2e-optional:
needs: [plan, e2e, confirm]
if: always() && needs.plan.outputs.optional_shards != '0'
uses: ./.github/workflows/battery.yml
with:
set: optional
matrix: ${{ needs.plan.outputs.optional_matrix }}
playwright: ${{ needs.plan.outputs.playwright }}
refs: ${{ needs.plan.outputs.refs }}
deadline: 40

optional-report:
needs: e2e-optional
if: always() && needs.e2e-optional.result != 'skipped'
runs-on: ubuntu-latest
timeout-minutes: 100
# the browsers and their OS libraries at exactly the lockfile's Playwright: no 5-minute apt step per shard
container:
image: mcr.microsoft.com/playwright:v${{ needs.plan.outputs.playwright }}-noble
options: --ipc=host
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
# the sibling checkouts sit beside core, where the suites' own defaults look for them. Inside a
# container the workspace is NOT ${{ github.workspace }} (that is the host path): use the env vars.
env:
E2E_NO_PERF_OFFER: '1'
MODE: quick
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: core
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(needs.plan.outputs.refs).scenes.repo }}
ref: ${{ fromJSON(needs.plan.outputs.refs).scenes.ref }}
path: scenes
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(needs.plan.outputs.refs).modules.repo }}
ref: ${{ fromJSON(needs.plan.outputs.refs).modules.ref }}
path: modules
- uses: actions/checkout@v4
with:
repository: ${{ fromJSON(needs.plan.outputs.refs).packs.repo }}
ref: ${{ fromJSON(needs.plan.outputs.refs).packs.ref }}
path: packs
- uses: actions/setup-node@v4
with:
node-version-file: core/.nvmrc
cache: npm
cache-dependency-path: |
core/package-lock.json
modules/package-lock.json
- name: install
run: |
command -v openssl > /dev/null || (apt-get update -qq && apt-get install -y -qq --no-install-recommends openssl > /dev/null)
(cd core && npm ci --no-audit --no-fund && npm run certs)
# the module zips the game suites install (MODULES_REPO)
(cd modules && npm ci --no-audit --no-fund && node scripts/pack.cjs --all --force)
- name: battery shard ${{ matrix.shard }}/${{ matrix.of }} (~${{ matrix.est }} min, ${{ matrix.suites }} suites)
working-directory: core
run: |
W=$GITHUB_WORKSPACE; S=$W/scenes; G=$S/games
export SCENES_DIR=$S SCENES_REPO=$S SHELL_SCENES_DIR=$G GAMES_DIR=$G FBW_SCENES=$S/examples
export MODULES_REPO=$W/modules MODULES_DIR=$W/modules PACKS_DIR=$W/packs
for g in dungeon-realms:DUNGEON_REALMS escape-room:ESCAPE football:FOOTBALL jam-room:JAM_ROOM marble-maze:MARBLE \
mini-golf:MINIGOLF sky-run:SKY_RUN stars-room:STARS_ROOM target-toss:TARGET_TOSS towers:TOWERS \
untangle:UNTANGLE waves:WAVES race:RACE; do export "${g#*:}_TPSCENE=$G/${g%%:*}/scene.tpscene"; done
export JELLY_TPSCENE=$S/examples/jelly-room/scene.tpscene TANK_TPSCENE=$S/examples/fluid-tank-toy/scene.tpscene \
POOL_TPSCENE=$S/examples/pool-party/scene.tpscene
node scripts/ci/battery.cjs run --shard ${{ matrix.shard }} --of ${{ matrix.of }} --serve --port 5173 --out "$W/battery-out"
- uses: actions/upload-artifact@v4
if: always()
with:
name: battery-${{ matrix.shard }}
# every suite's log; screenshots and evidence only for the suites that failed (the runner deletes the
# rest). Beside core, not in it: an artefact inside the served tree reloads the app mid-test.
path: battery-out
retention-days: 14
if-no-files-found: ignore
pattern: battery-optional-*
path: battery
- name: optional suites (report only)
run: node scripts/ci/battery.cjs report battery || true

# THE required check (the ruleset on `deps` names it)
gate:
name: deps-gate
needs: [scope, ci, perf, plan, e2e]
needs: [scope, ci, perf, plan, e2e, confirm]
if: always() && needs.scope.result == 'success'
runs-on: ubuntu-latest
permissions:
Expand All @@ -190,7 +183,12 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: battery-*
pattern: battery-required-*
path: battery
- uses: actions/download-artifact@v4
if: needs.confirm.result != 'skipped'
with:
pattern: battery-confirm-*
path: battery
- name: battery report
id: report
Expand Down
17 changes: 13 additions & 4 deletions .github/workflows/perf-budget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@ on:
push:
branches: [main, release/next]
workflow_call:
inputs:
# deps-auto: deps.yml runs the gate as `parts` parallel jobs, this one = `part` (scripts/perf-games.cjs --part)
part:
type: number
default: 0
parts:
type: number
default: 1
workflow_dispatch:

permissions:
contents: read

concurrency:
group: perf-budget-${{ github.ref }}
group: perf-budget-${{ github.ref }}-${{ inputs.part }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
budget:
name: perf-budget
name: perf-budget${{ inputs.part && format(' {0}/{1}', inputs.part, inputs.parts) || '' }}
runs-on: ubuntu-latest
timeout-minutes: 75
steps:
Expand Down Expand Up @@ -91,13 +99,14 @@ jobs:
APP_URL: https://localhost:5173/
PERF_SCENES_DIR: ${{ runner.temp }}/perf-deps/scenes
PERF_MODULES_DIR: ${{ runner.temp }}/perf-deps/modules
run: node scripts/perf-games.cjs --check --label ci --out perf/out
PART: ${{ inputs.part && format('{0}/{1}', inputs.part, inputs.parts) || '' }}
run: node scripts/perf-games.cjs --check --label ci${PART:+-${PART%/*}} --out perf/out ${PART:+--part $PART}

- name: the table
if: always()
uses: actions/upload-artifact@v7
with:
name: perf-budget
name: perf-budget${{ inputs.part && format('-{0}', inputs.part) || '' }}
path: perf/out/
if-no-files-found: ignore

Expand Down
Loading