Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
838 commits
Select commit Hold shift + click to select a range
7a67342
feat(native-engine): InstancedBufferGeometry, per-instance custom att…
jonit-dev Oct 9, 2026
3ed5dbe
docs(census): regenerate the native runtime census after instanced ge…
jonit-dev Oct 9, 2026
052bb3e
feat(tools): profile:wasm-page --gpu-calls counts WebGPU calls per fr…
jonit-dev Oct 9, 2026
5f23557
perf(native-engine): a post render target is one pass, however often …
jonit-dev Oct 9, 2026
383e4c8
Merge branch 'feat/native-engine' into lane/ne-midway
jonit-dev Oct 9, 2026
1a70b18
chore(three-native): regenerate the native registry snapshot after th…
jonit-dev Oct 9, 2026
7fa88da
docs(prds): PRD-540 the core game boots on Wasm and attribute.array s…
jonit-dev Oct 9, 2026
c40da4d
feat(wasm): the minimal template's post chain runs on Wasm through pa…
jonit-dev Oct 9, 2026
cb8b507
fix(three-native): registry fields are JSON arrays, not tuples
jonit-dev Oct 9, 2026
2f5bcd1
feat(wasm): a wrapper keeps its JS state while the engine references it
jonit-dev Oct 9, 2026
77b1ee7
feat(native-engine): MeshStandardMaterial.bumpMap and bumpScale on V8…
jonit-dev Oct 9, 2026
522f960
docs(prds): file PRD-551, render targets and fullscreen quads on the …
jonit-dev Oct 9, 2026
73dbb83
feat(native-engine): TSL normalLocal, tangentLocal and positionPrevio…
jonit-dev Oct 9, 2026
af35fc1
feat(native-engine): a game's RenderTarget is drawn, sampled and read…
jonit-dev Oct 9, 2026
2213811
feat(native-engine): TSL storage() over an engine BufferAttribute, an…
jonit-dev Oct 9, 2026
dc7475a
feat(native-engine): render targets and QuadMesh on V8 and Wasm (PRD-…
jonit-dev Oct 9, 2026
260c550
style(three-native): QuadMesh's engine class record passes the naming…
jonit-dev Oct 9, 2026
52e10f6
Merge feat/native-engine (2f5bcd172) into lane/ne-wasm-templates
jonit-dev Oct 9, 2026
67cb9c3
docs(census): regenerate the native runtime census after the feat/nat…
jonit-dev Oct 9, 2026
0c7826f
fix(v8): the adapter frees the data its installed functions use
jonit-dev Oct 9, 2026
cd8805c
test(three-native): check browser TSL on the real Wasm module
jonit-dev Oct 9, 2026
8c9a212
Merge lane/ne-wasm-templates into feat/native-engine
jonit-dev Oct 9, 2026
3cae36c
fix(native-engine): bind Material.dispose so environment sampling fin…
jonit-dev Oct 9, 2026
a484b58
Merge branch 'lane/ne-browser-tsl' into feat/native-engine
jonit-dev Oct 9, 2026
6b7b253
fix(three-native): release TSL scope handles on the browser back end
jonit-dev Oct 9, 2026
db13409
test(native-engine): an instanceColor assigned after a drawn frame co…
jonit-dev Oct 9, 2026
7d7a843
test(wasm): instanced geometry and a late instanceColor read back on …
jonit-dev Oct 9, 2026
7757217
docs(prds): PRD-545 instanceColor and InstancedBufferGeometry boxes p…
jonit-dev Oct 9, 2026
5c2ec64
docs(prds): PRD-546 polygonOffset box proved by its r185 fixture
jonit-dev Oct 9, 2026
e70c927
feat(native-engine): Data3DTexture and TSL texture3D on V8 and Wasm
jonit-dev Oct 9, 2026
3629a2e
docs(prds): PRD-546 done, Data3DTexture read back on the Wasm rendere…
jonit-dev Oct 9, 2026
0e2f742
test(native-engine): texture(object) with and without level displaces…
jonit-dev Oct 9, 2026
f55a9da
docs(prds): PRD-547 vertex-stage texture box proved by its r185 fixture
jonit-dev Oct 9, 2026
1698096
feat(native-engine): TSL normalWorld reads the material's normal, fli…
jonit-dev Oct 9, 2026
c63a331
fix(three-native): uniform and constant values keep r185's semantics …
jonit-dev Oct 9, 2026
e0aa8e2
docs(prds): PRD-547 uniform and constant value box proved on V8 and Wasm
jonit-dev Oct 9, 2026
01942ef
test(native-engine): viewportLinearDepth, linearDepth, cameraNear and…
jonit-dev Oct 9, 2026
c953da0
docs(prds): PRD-548 viewport depth and shared texture boxes proved by…
jonit-dev Oct 9, 2026
3f8cac8
test(native-engine): a SkeletonUtils.clone of a SkinnedMesh bends on …
jonit-dev Oct 9, 2026
dd5e46c
docs(prds): PRD-548 SkeletonUtils.clone box proved by its r185 fixture
jonit-dev Oct 9, 2026
8a1b20d
feat(three-native): BufferGeometryUtils.mergeVertices over engine geo…
jonit-dev Oct 9, 2026
38aa948
docs(prds): PRD-548 BufferGeometryUtils box proved on V8 and Wasm
jonit-dev Oct 9, 2026
b8108d7
feat(native-engine): the shadow pass draws casters through the shadow…
jonit-dev Oct 9, 2026
24c9f62
feat(native-engine): the native glTF loader maps KHR_materials_clearcoat
jonit-dev Oct 9, 2026
170848d
test(native-engine): the loaded clearcoat material keeps its factors …
jonit-dev Oct 9, 2026
8fb6d6a
test(native-engine): PMREM filters an HDRLoader-style half-float equi…
jonit-dev Oct 9, 2026
225826d
docs(prds): PRD-548 done, HDRLoader box proved by decode parity and a…
jonit-dev Oct 9, 2026
8270077
Merge feat/native-engine (6b7b25314) into lane/ne-midway, keeping fea…
jonit-dev Oct 9, 2026
2c9e547
test(wasm): a TSL tile and its uniform.value write read back on the r…
jonit-dev Oct 9, 2026
5d52653
fix(native-engine): an InstancedBufferGeometry with no per-instance a…
jonit-dev Oct 9, 2026
80ad502
docs(prds): PRD-540 phase 3 proved on the real Wasm module
jonit-dev Oct 9, 2026
839e604
Merge lane/ne-midway into feat/native-engine
jonit-dev Oct 9, 2026
25aa7fe
docs(prds): PRD-547 core TSL names box green on Wasm after the Midway…
jonit-dev Oct 9, 2026
e3bdee2
docs(prds): PRD-540 done; file PRD-552, BatchedMesh on the native engine
jonit-dev Oct 9, 2026
d98b490
feat(native-engine): BatchedMesh on the native engine (PRD-552 phase 1)
jonit-dev Oct 9, 2026
74f2f11
feat(native-engine): BatchedMesh on V8 and Wasm, and three's clear co…
jonit-dev Oct 9, 2026
9eb0a59
feat(native-engine): V8 refuses unsupported three exports at use, as …
jonit-dev Oct 9, 2026
7c18826
feat(native-engine): the minimal template's journey runs on the V8 de…
jonit-dev Oct 9, 2026
c143933
perf(native-engine): core's projected-size cull runs in the engine, o…
jonit-dev Oct 9, 2026
c0aad2d
feat(native-engine): the Wasm engine reports its GPU time per frame t…
jonit-dev Oct 9, 2026
5c79178
test(wasm): a steady drawn core frame makes at most 37 engine calls (…
jonit-dev Oct 9, 2026
f67adde
style(three-native): format the GPU-time sampling
jonit-dev Oct 9, 2026
2af9b0d
perf(three-native): bound the browser safe point and time GPU per pass
jonit-dev Oct 9, 2026
87b3327
feat(native-engine): Object3D graph events for language listeners
jonit-dev Oct 9, 2026
4407be6
perf(native-engine): pack draw uniforms without per-draw maps
jonit-dev Oct 9, 2026
69eebf4
feat(native-engine): TSL screenSize, screenCoordinate, positionViewDi…
jonit-dev Oct 9, 2026
d96dddb
feat(native-engine): TSL depth and normalWorldGeometry in corpus (PRD…
jonit-dev Oct 9, 2026
2de8d55
feat(native-engine): the vertex stage writes positionViewDirection an…
jonit-dev Oct 9, 2026
8ed0b5c
chore(native-engine): register temp-dir cleanup and waive or remove b…
jonit-dev Oct 9, 2026
76e5191
feat(native-engine): TSL getViewPosition and setGroup in the shared t…
jonit-dev Oct 9, 2026
e50bb43
feat(native-engine): TSL transformNormalToView with a per-draw modelN…
jonit-dev Oct 9, 2026
d4ea585
feat(native-engine): TSL property() as a variable's zero value
jonit-dev Oct 9, 2026
f71a96a
docs(prds): PRD-533 native-stub ledger gate passes (exit 0, empty led…
jonit-dev Oct 9, 2026
17b5f64
fix(native-engine): fresh-eyes review findings on the 2026-10-09 run
jonit-dev Oct 9, 2026
d70e669
fix(three-native): typecheck the projected-cull parity smoke and the …
jonit-dev Oct 9, 2026
2e85cfe
docs(verification): native coverage for the 2026-10-09 engine changes
jonit-dev Oct 9, 2026
0e74935
docs(prds): PRD-553 CPU-busy ratio after uniform packing, median 1.99…
jonit-dev Oct 9, 2026
0cb45ca
fix(runtime-native): two runtime-native test reds on a machine that b…
jonit-dev Oct 9, 2026
67d36b9
docs(verification): native coverage after the web host surface rename
jonit-dev Oct 9, 2026
1f112d8
fix(native-engine): the V8 player answers a playtest bridge's promise…
jonit-dev Oct 9, 2026
a020919
docs(prds): PRD-545 done, Midway's native boot journey passes on the …
jonit-dev Oct 9, 2026
192667a
fix(runtime-native): Dawn timestamps at full resolution, so the GPU t…
jonit-dev Oct 9, 2026
11a9322
docs(prds): file PRD-554, a game's UI runs on the native-engine player
jonit-dev Oct 9, 2026
b8d29b7
docs(verification): native coverage after the player bridge and times…
jonit-dev Oct 9, 2026
815dc40
style(runtime-native): no assignment in an expression in the async-br…
jonit-dev Oct 9, 2026
2106f6a
feat(native-engine): a game's UI bridge on the V8 player (PRD-554 pha…
jonit-dev Oct 9, 2026
1be03b9
feat(native-engine): the engine renderer composites a UI overlay over…
jonit-dev Oct 9, 2026
ecf082e
feat(native-engine): Midway's UI-driven launch journey passes on the …
jonit-dev Oct 9, 2026
a36e40d
docs(prds): PRD-554 done on Linux (Midway's launch journey passes on …
jonit-dev Oct 9, 2026
b90be7a
fix(native-engine): the player loop reaches a game's UI only through …
jonit-dev Oct 9, 2026
a92673a
docs(verification): native coverage after the player UI layer (PRD-554)
jonit-dev Oct 9, 2026
fa328f1
fix(native-engine): link every builder's varyings to the fragment's r…
jonit-dev Oct 9, 2026
80bc023
docs(verification): regenerate native coverage after the varying-link…
jonit-dev Oct 9, 2026
bbbd568
perf(native-engine): build map mips on the GPU and share them with re…
jonit-dev Oct 9, 2026
3e5a42d
perf(three-native): pass typed arrays to the Wasm engine without a li…
jonit-dev Oct 9, 2026
0fa53f7
perf(native-engine): decode web glTF images in the browser, as three …
jonit-dev Oct 9, 2026
2a48a5e
perf(three-native): BufferAttribute element access stays in JS on the…
jonit-dev Oct 9, 2026
2691705
perf(three-native): cache BufferAttribute count, itemSize and normali…
jonit-dev Oct 9, 2026
3abc78d
perf(three-native): a game's images and canvases reach the GPU withou…
jonit-dev Oct 9, 2026
19dc0c2
feat(native-engine): render the scene at 4x MSAA with resolved depth
jonit-dev Oct 9, 2026
3b29482
fix(core): an unsized canvas's own drawing buffer is not a new layout…
jonit-dev Oct 9, 2026
3b72467
docs(verification): regenerate native coverage and census after the w…
jonit-dev Oct 9, 2026
504709d
fix(core): pin an unsized canvas at its layout size once its buffer e…
jonit-dev Oct 9, 2026
a3025be
fix(three-native): material slots are own enumerable properties on we…
jonit-dev Oct 9, 2026
b64e242
fix(native-engine): shadow passes follow the shadow camera's layers (…
jonit-dev Oct 9, 2026
1bc6a04
perf(native-engine): a steady draw reuses its resolved program, pipel…
jonit-dev Oct 9, 2026
de71260
perf(native-engine): steady frames skip key builds and rehashing, safely
jonit-dev Oct 10, 2026
d87c61b
perf(runtime-native): frustum-cull the main pass like projectObject
jonit-dev Oct 10, 2026
b1ff72d
feat(profile): profile:wasm-page --load reports a game's load steps o…
jonit-dev Oct 10, 2026
0b031d8
perf(runtime-native): frustum-cull shadow casters against each shadow…
jonit-dev Oct 10, 2026
05318b3
docs(prds): PRD-553 records the shadow-cull draw counts and the Midwa…
jonit-dev Oct 10, 2026
3a4cc5d
docs(prds): PRD-547 status lines read PARTIAL, as its ticked boxes say
jonit-dev Oct 10, 2026
9fae372
perf(runtime-native): settle the skinned projection decline for 60 fr…
jonit-dev Oct 10, 2026
a188cd4
perf(native-engine): typed bulk fill for BufferAttribute::fromDoubles…
jonit-dev Oct 10, 2026
a69b062
perf(native-engine): one queue submit for every mip chain uploaded be…
jonit-dev Oct 10, 2026
09ef7e8
docs(prds): PRD-553 ticks Midway CPU and frame p50 against three.js; …
jonit-dev Oct 10, 2026
3ca98e5
perf(native-engine): Float32 items read in place; parsed track names …
jonit-dev Oct 10, 2026
7698916
docs(prds): PRD-553 enter gap, measured cause and what remains
jonit-dev Oct 10, 2026
d67b757
perf(three-native): math values live in JS on the web back end
jonit-dev Oct 10, 2026
b4cb98b
perf(native-engine): write new geometry into buffers at creation
jonit-dev Oct 10, 2026
c20b0b3
perf(three-native): cheaper argument crossing and census check
jonit-dev Oct 10, 2026
216d926
docs(prds): PRD-553 load boxes, PRD-530 Perry spike and stop rule
jonit-dev Oct 10, 2026
7da1c30
docs(prd): file the Unreal-class performance epic, PRD-573..579
jonit-dev Oct 10, 2026
0093b06
perf(native-engine): emit a shared shader value once, as a let
jonit-dev Oct 10, 2026
bf456a1
perf(three-native): list a geometry's attributes with one engine call
jonit-dev Oct 10, 2026
e9d4f1f
perf(native-engine): drop the per-frame weak_ptr lock in bindings and…
jonit-dev Oct 10, 2026
16c0f0d
perf(three-native): read an attribute's shape with one engine call
jonit-dev Oct 10, 2026
3669ef8
perf(three-native): list geometry.attributes without reading each att…
jonit-dev Oct 10, 2026
4ba26f1
perf(native-engine): read mixer.time and visible in place; a setter k…
jonit-dev Oct 10, 2026
1faa559
style(three-native): name the attribute shape order in camelCase
jonit-dev Oct 10, 2026
c08f5d1
perf(native-engine): read castShadow and receiveShadow in place; skip…
jonit-dev Oct 10, 2026
751b76f
perf(three-native): run three's Layers bit operations on the mask in …
jonit-dev Oct 10, 2026
02ac2af
perf(three-native): keep each walked object's parent on the Wasm back…
jonit-dev Oct 10, 2026
af8591d
perf(three-native): bind a track in one engine call
jonit-dev Oct 10, 2026
07297c9
perf(three-native): write an object's rotation in place on the Wasm b…
jonit-dev Oct 10, 2026
5ef18a5
perf(three-native): keep a rotation set with the current order in place
jonit-dev Oct 10, 2026
2c3a906
perf(three-native): answer an object's member addresses in one engine…
jonit-dev Oct 10, 2026
318dd43
perf(three-native): read an object's own address from its `__addresse…
jonit-dev Oct 10, 2026
ec77bd6
fix(native-engine): count Object3D flag fields from the first flag, n…
jonit-dev Oct 10, 2026
9c9e698
perf(native-engine): read a geometry's attribute shapes with one engi…
jonit-dev Oct 10, 2026
7fd31d7
perf(native-engine): hand an object's kept members without an engine get
jonit-dev Oct 10, 2026
e2b517b
perf(native-engine): answer a curve's untargeted point with a JS vector
jonit-dev Oct 10, 2026
f2edddb
perf(native-engine): PRD-577 compile pipelines off the frame
jonit-dev Oct 10, 2026
c99149f
fix(native-engine): PRD-577 review fixes for off-frame compiles
jonit-dev Oct 10, 2026
aeb6e49
docs(prds): file PRD-581, Midway runs JS-free through Perry
jonit-dev Oct 10, 2026
5a39d7e
fix(native-engine): PRD-577 initTexture takes every texture class
jonit-dev Oct 10, 2026
b410273
perf(native-engine): one call answers a geometry's attributes and shapes
jonit-dev Oct 10, 2026
aebada1
perf(native-engine): keep a geometry's index and its listed attributes
jonit-dev Oct 10, 2026
c852382
perf(native-engine): quaternion math runs on its lanes in JS
jonit-dev Oct 10, 2026
4b7f7a5
perf(native-engine): Color is a JS value class
jonit-dev Oct 10, 2026
4920c39
docs(prds): PRD-553 census box ticked at 81,481 calls
jonit-dev Oct 10, 2026
7f1538a
perf(three-native): halve the Wasm facade's per-call cost
jonit-dev Oct 10, 2026
5c34a9f
docs(prds): PRD-553 first frame leads by 27-37% in one window
jonit-dev Oct 10, 2026
1e0de8a
perf(native-engine): BufferAttribute constructors read the list in place
jonit-dev Oct 10, 2026
a9b98c5
perf(three-native): write handle fields from the key digits, no per-r…
jonit-dev Oct 10, 2026
c60e3c7
perf(three-native): scan number lists with a loop, one shape lookup p…
jonit-dev Oct 10, 2026
75f022c
perf(native-engine): a typed array crosses the ABI as its own bytes
jonit-dev Oct 10, 2026
46f7bf1
perf(three-native): copy an attribute's data again only after an engi…
jonit-dev Oct 10, 2026
4df2232
perf(three-native): the engine pulls a game's attribute array only wh…
jonit-dev Oct 10, 2026
f923ecb
perf(three-native): an adopted attribute array crosses once, as its c…
jonit-dev Oct 10, 2026
d067f84
perf(three-native): mergeGeometries reads each index array once, not …
jonit-dev Oct 10, 2026
37127ae
perf(runtime-native): a buffer store zeroes its bytes on first use, n…
jonit-dev Oct 10, 2026
c783537
test(runtime-native): the ABI pull stub returns its count, as abi.cpp…
jonit-dev Oct 10, 2026
dd4f18e
perf(runtime-native): a geometry read leaves the store's write count
jonit-dev Oct 10, 2026
509f42a
perf(runtime-native): DataTexture keeps a typed array's bytes over th…
jonit-dev Oct 10, 2026
2007efe
perf(core): foot-plant stride reads each bone's world matrix once per…
jonit-dev Oct 10, 2026
9bbbde2
perf(three-native): glTF page image decode hands the Blob a view, not…
jonit-dev Oct 10, 2026
1ed0142
perf(runtime-native): glTF accessors copy their bytes, not values thr…
jonit-dev Oct 10, 2026
ec46c15
fix(core): request the glTF loader module beside the first model's bytes
jonit-dev Oct 10, 2026
8c6acec
perf(three-native): build glTF image bitmaps in workers on the web en…
jonit-dev Oct 10, 2026
c90d07c
perf(three-native): merge engine geometries in one engine call
jonit-dev Oct 10, 2026
8e506a3
docs(prds): PRD-553 queue writes before ready stay under 10 MB
jonit-dev Oct 10, 2026
3d36951
fix(core): foot-plant stride reads a moved ancestor above the rig
jonit-dev Oct 10, 2026
61d5346
fix(runtime-native): glTF loader shares accessors and geometries like…
jonit-dev Oct 10, 2026
49970d9
docs(prd): fix four broken PRD-489 and PRD-339 links
jonit-dev Oct 10, 2026
fbdd719
perf(three-native): float attributes answer as Float32BufferAttribute
jonit-dev Oct 10, 2026
20c129d
fix(runtime-native): sibling renderers share the attribute GPU copies
jonit-dev Oct 10, 2026
7e7e9ec
docs(prd-553): tick enter and ready on three load-gated pairs
jonit-dev Oct 10, 2026
7c02a03
feat(native): run the shared three facade through Perry C transport
jonit-dev Oct 10, 2026
acc8c6c
docs(prds): PRD-581 Perry frame matches the Wasm frame
jonit-dev Oct 10, 2026
e51084a
feat(three-native): PRD-581 Midway links and loads its assets under s…
jonit-dev Oct 10, 2026
a9723aa
feat(three-native): PRD-581 TSL struct and Perry-safe texture constru…
jonit-dev Oct 10, 2026
c052bf4
fix(three-native): PRD-581 find a wrapper's class without `constructor`
jonit-dev Oct 10, 2026
1eda5af
fix(native-typescript): PRD-581 compile-game starts the game
jonit-dev Oct 10, 2026
652bdbc
feat(native-typescript): PRD-581 — a Perry player host opens the nati…
jonit-dev Oct 10, 2026
4181d62
fix(runtime-native): the engine player picks X11 before SDL_Init
jonit-dev Oct 10, 2026
bab32f0
feat(three-native): PRD-581 — the Perry binary carries the native Rap…
jonit-dev Oct 10, 2026
100a316
fix(three-native): PRD-581 — Perry start settles and reaches core's h…
jonit-dev Oct 10, 2026
fe82307
fix(three-native): PRD-581 — Perry WebGPURenderer answers getMaxAniso…
jonit-dev Oct 10, 2026
aebe923
feat(three-native): PRD-581 — Perry createImageBitmap decodes nativel…
jonit-dev Oct 10, 2026
6ab8521
fix(three-native): PRD-581 — Perry loaders resolve page URLs under pu…
jonit-dev Oct 10, 2026
a441e3b
feat(three-native): PRD-581 — strict Perry WebAudio binds the native …
jonit-dev Oct 10, 2026
a3a67a3
refactor(three-native): PRD-581 — Perry fetch shim and loaders share …
jonit-dev Oct 10, 2026
f1378dc
feat(three-native): PRD-581 P2m — strict Perry binds pass() and Rende…
jonit-dev Oct 10, 2026
d8b69b1
fix(three-native): PRD-581 — strict Perry instanceof follows the prot…
jonit-dev Oct 10, 2026
f90b300
docs(prds): PRD-581 — record the HDR, asset-path and instanceof round…
jonit-dev Oct 10, 2026
a310e19
fix(three-native): PRD-581 — install exact instanceof on bound classe…
jonit-dev Oct 10, 2026
48b513c
docs(prds): file PRD-582, Midway on the web as Perry Wasm over the Wa…
jonit-dev Oct 10, 2026
a876965
fix(three-native): PRD-581 — a refused engine argument names its clas…
jonit-dev Oct 10, 2026
2b5f813
fix(three-native): PRD-581 — a cloned Vector3 crosses to the engine u…
jonit-dev Oct 10, 2026
f64ba0d
fix(three-native): PRD-581 — Perry writes reach an engine vector's me…
jonit-dev Oct 10, 2026
c9b8d77
fix(native-typescript): PRD-581 engine code stops stringifying functions
jonit-dev Oct 10, 2026
9400d58
fix(core): a counted step spends the pending freeze settle
jonit-dev Oct 10, 2026
a2f7d58
fix(playtest): count a synchronous fixed step before the next host tick
jonit-dev Oct 10, 2026
5ecacdf
fix(playtest): a nameless constructor reads unobserved, not a crash
jonit-dev Oct 10, 2026
7ddca76
fix(native): expose Perry frame counters and GPU adapter info
jonit-dev Oct 10, 2026
f9205ac
docs(prd-581): minimal Perry journey reaches 6/8, V8 parity
jonit-dev Oct 10, 2026
aaf320a
feat(perf): benchmark Perry against the V8 native player
jonit-dev Oct 10, 2026
e118da4
fix(three-native): three's type flags stay writable
jonit-dev Oct 10, 2026
a1b40ab
feat(perf): render a Perry-vs-V8 HTML dashboard and add --open
jonit-dev Oct 10, 2026
a045966
test(perf): assert bench:perry writes report.html and index.html
jonit-dev Oct 10, 2026
a00a3b6
fix(perf): bench:perry profiles a slow-exiting Perry game
jonit-dev Oct 10, 2026
67095d3
feat(bench): Tailwind trend dashboard for the Perry vs V8 report
jonit-dev Oct 10, 2026
0f89509
feat(bench): regression gate vs an accepted incumbent run (exit 3)
jonit-dev Oct 10, 2026
23a6d34
feat(bench): refuse to measure on an overloaded host
jonit-dev Oct 10, 2026
8802c62
fix(bench): a failed run no longer replaces the measured dashboard
jonit-dev Oct 11, 2026
3a620db
feat(perf): attribute Perry profiles to TypeScript callers
jonit-dev Oct 11, 2026
4eb416a
perf(perry): bulk-read numeric lists into a reused scratch; skip idle…
jonit-dev Oct 11, 2026
ada12fd
perf(perry): drop per-call parsing and closure loops in the transport
jonit-dev Oct 11, 2026
c1ef353
perf(core): read skinned-mesh envelope invariants once, not per vertex
jonit-dev Oct 11, 2026
d18972f
fix(bench): cook the V8 arm from the same asset root as the Perry arm
jonit-dev Oct 11, 2026
d64e567
fix(three-native): align Perry transport with upstream deferred attri…
jonit-dev Oct 11, 2026
88d6c25
fix(three-native): retain Perry fixed-member converter capture
jonit-dev Oct 11, 2026
07ea466
test(three-native): prove Perry settles parallel parser callbacks und…
jonit-dev Oct 11, 2026
6b6ce03
fix(three-native): every geometry class lists real attribute names
jonit-dev Oct 11, 2026
1b52db7
fix(three-native): declare the scalar tables before the attribute-pul…
jonit-dev Oct 11, 2026
d5a0650
fix(native-typescript): stage import.meta.env and import.meta.hot as …
jonit-dev Oct 11, 2026
762a4a3
style(native-typescript): build the import.meta.env defaults from ent…
jonit-dev Oct 11, 2026
2f74d5e
fix(three-native): drain audio decodes on host frames, not only simul…
jonit-dev Oct 11, 2026
b8323d6
fix(three-native): run loading timers on the frame clock while a runn…
jonit-dev Oct 11, 2026
18b0d5e
docs(prd-581): Midway box 99 cycle 7 - boot passes, launch/cockpit wa…
jonit-dev Oct 11, 2026
8d53dbf
fix(native): deliver Perry UI state and intents through the shared pl…
jonit-dev Oct 11, 2026
6de460a
fix(native): select and link the CSS-only Perry UI backend
jonit-dev Oct 11, 2026
3c1c187
docs(prd581): verify integrated Perry rendering on NVIDIA
jonit-dev Oct 11, 2026
b06be6f
docs(prd581): explain prior unit failures and worker OOM
jonit-dev Oct 11, 2026
8fc9471
docs(prd581): record blocked fresh performance baseline
jonit-dev Oct 11, 2026
750fa0a
docs: record fresh integrated Perry performance incumbent
jonit-dev Oct 11, 2026
18dd977
docs: bound B18 private-slot preparation and verification
jonit-dev Oct 11, 2026
fd28ece
Record B18 private-slot verification and allocation regression
jonit-dev Oct 11, 2026
ccbcde1
docs: record B18 semantic and native draw verification
jonit-dev Oct 11, 2026
b2f6b06
docs: record bounded perf-loop outcome and retained candidates
jonit-dev Oct 11, 2026
58af9de
docs: record r22 promotion and B18/B19 benchmark ties
jonit-dev Oct 11, 2026
2b48e20
docs(prd581): record round-three integration and bounded fork plan
jonit-dev Oct 11, 2026
eb039f9
docs(prd581): record round-three admission and named-read results
jonit-dev Oct 11, 2026
1e4eeaf
Add opt-in native game-frame wall-time attribution
jonit-dev Oct 11, 2026
850eb71
fix(bench-perry): cap the incumbent noise margin at 10%
jonit-dev Oct 11, 2026
b9b669f
docs(prd581): preserve systemic findings and corrected O3 rejection
jonit-dev Oct 11, 2026
5c2969b
docs(prd581): record real LTO link and exception admission failure
jonit-dev Oct 11, 2026
b487a10
fix(bench): gate Perry measurements on scoped CPU pressure
jonit-dev Oct 11, 2026
68f4b1f
docs(prd581): stop LTO trial at async GC correctness gate
jonit-dev Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
24 changes: 24 additions & 0 deletions .agents/skills/native-performance-loop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,30 @@ Confirm apparent wins with a fresh replay/seed or held-out scene before final de
the original baseline after three experiments to detect drift. Avoid retesting known losers unless
new evidence changes the hypothesis.

## Perry vs V8 workload

`pnpm bench:perry --project <template> --scenario <playtest.json> --perry <linked-binary> --v8-bundle <dir> --open`
runs one authored scenario twice on the shared native player (Perry then V8) and writes
`artifacts/perf-loop/<iso>/report.html` plus `index.html`. Any metric slower than V8 is a MISS:

| Metric | Target (Perry / V8) |
| --- | ---: |
| First frame | <= 1.0x |
| Game JS p50 | <= 0.5x |
| Total CPU frame p50 | <= 0.68x |
| Launch to ready | <= 0.33x |

Pass `--incumbent <last accepted run dir>` to gate the candidate: any metric whose Perry / V8 ratio
worsens past its margin is REGRESSED and the bench exits 3 = REGRESSION: never commit the candidate, fix the cause and bench again.

Runs above 0.75 x cores load (`--max-load` overrides) are refused after a 5 minute wait: serialise builds and benches.

Never report fps on Xvfb. Measurements run in the parent session; Codex sandboxes cannot start
Xvfb. A Perry relink takes about 17 minutes; append `--perf` for profiling, excluded from the verdict.
The V8 player needs `TN_ENABLE_NATIVE_PHYSICS=ON`, and its `--v8-bundle` must pack the game's assets:
build it with `bundle-native-engine.mjs --assets <dir>` whose `public/` holds the assets that
`TN_ASSET_ROOT` serves to Perry, or V8 fails `TN_NATIVE_ASSET_MISSING`.

## Guardrails and stopping

Do not buy speed by altering gameplay, appearance, time scale, measurement boundaries, validation,
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/profiling/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ is not presented frame time, and a parity pass is not a performance result.
| V8 CPU profile | `TN_JS_CPU_PROFILE=1 <host>` (window via `TN_JS_CPU_PROFILE_START_FRAME`); `--cpu-prof <file>` on the host (`mystral run <script> --cpu-prof=<file>`) or a playtest (`node packages/playtest/dist/runner/cli.js <scenario> --cpu-prof <file>`) | Attribute JS self-time; open a DevTools profile | Host built with the profiler (desktop default); `--cpu-prof` is browser/desktop only (device refuses `TN_PLAYTEST_CPU_PROFILE_UNSUPPORTED`) | `TN_JS_CPU_PROFILE:` / `_TOTAL:` marker lines; a Chrome DevTools `.cpuprofile` | Perturbs the frame; diagnose only, never a timing verdict |
| Profiled-host counters | Build host `-DTN_ANDROID_JS_PROFILE=ON`, run `node packages/runtime-native/scripts/measure-android-js-engine.mjs --device <serial>` or `measure-desktop-frame-pair.mjs --control <bin> --candidate <bin> --bundle <dir> --output <dir> --runs <n>` | Per-frame JS↔host binding/command cost | Profiled host; physical Android for acceptance | `TN_ANDROID_JS_NATIVE:` and `TN_BRIDGE_BY_NAME:` markers | Profiled build inflates absolutes; emulator proves plumbing only. Recipe: `packages/runtime-native/docs/G5-profiling.md` |
| Linux OS `perf` (optional) | `perf stat -p <host-pid>`; `perf record -e cpu-clock:u --call-graph dwarf -p <host-pid>` then `perf report`; start the host with `packages/runtime-native/build/tn-linux/mystral run <script>` (or under `perf record -- …`) | Native C/C++ CPU attribution the V8 `.cpuprofile` cannot name | Linux; a kernel-matching `perf` from the OS package (`linux-tools`/`linux-perf`), an unstripped host for symbols; unprivileged sampling can be blocked by `perf_event_paranoid` (an OS/operator setting) | `perf stat` counters; `perf.data` + `perf report` call tree | Optional system executable; no bundled binary, wrapper, automatic install or sysctl change. Resolves native frames only — V8/JIT frames stay unnamed, so pair with `--cpu-prof`; no shipped flamegraph or JIT-symbol helper |
| Wasm-engine page vs three.js | `sh scripts/xvfb.sh pnpm profile:wasm-page -- --url <wasm page> [--control <three.js page>] [--calls] [--gpu-calls] [--gpu-time] [--gpu-passes] [--cpu-work] [--save-profile <file>] [--seconds 5] [--json]` | A game is slower on the Wasm engine than on three.js; find where its frame goes | Served pages (`vite preview`), hardware WebGPU (refuses software); Wasm function names need the web host configured with `-DTN_WEB_PROFILING_FUNCS=ON`, JS names an unminified build | rAF p50/p95, CPU split into engine Wasm / JS / WebGPU calls with top inclusive functions, `--calls` = JS->Wasm engine calls per frame by class and member, `--gpu-calls` = WebGPU calls per frame, `--gpu-time` = the engine's GPU time per frame split into shadow, scene, post and output (timestamp-query), `--gpu-passes` = GPU time of every pass on either page by target size and format, `--cpu-work` = main-thread ms per frame less the getCurrentTexture/submit waits (what a faster display still pays), `--save-profile` = the raw profile for DevTools, top self and inclusive functions, subject/control ratio | A blank WebGPU page caps a private-Xvfb lane near 640 fps, so a frame-rate ratio past ~1.7x vs a 370 fps control is the lane's ceiling: compare `--cpu-work` and `--gpu-passes` there. Diagnosis only: shared-GPU noise moves absolutes, so read the ratio on one lane; hold the fix with a counter gate (`render_database_test steady_state`, wasm-engine-boot `steady*` probes), never a timing gate |
| Chromium JS CPU profile | `pnpm profile:native-cpu` (`:fox` = `--visual-evidence fox-scale --allow-software`) | Isolate shared JS cost across scenarios in the browser | Playwright Chromium; `--output-dir` (default `artifacts/native-cpu-profile`) | Samples, stage/render advisor, optional PNGs | Chromium only; proves nothing about the native host |
| Android cold start | `node packages/runtime-native/scripts/measure-cold-start.mjs` | Attribute launch time per segment | Physical Android (emulator exits `TN_COLD_START_EMULATOR_BLOCKED`) | `TN_COLD_START:` segment stamps, median/p95 | Emulator proof is plumbing only. Record: `packages/runtime-native/docs/G5-profiling.md` |

Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ jobs:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
# The compatibility goldens the native drivers are judged against, rechecked against three.
# No render fixtures: their goldens hold one GPU's frame hash and adapter, so the full check
# stays a local gate. This run needs no GPU and reports how many renders it skipped.
- name: Check the non-render compatibility goldens against three
run: pnpm --workspace-root exec tsx packages/three-native/tests/compatibility/run-reference.ts --check --no-renders
- uses: ./.github/actions/playwright-chromium
# The runtime-native contract tests execute real binaries and refuse to skip when one is
# missing — an unbuilt executable is unexecuted, and a silent skip would read as a pass. CI
Expand Down Expand Up @@ -506,6 +511,45 @@ jobs:
if: always()
shell: bash
run: ccache --show-stats --verbose
# PRD-499 gate E. The engine targets configure with no scripting at all and run headless
# under ASan/UBSan; inside the V8 build their link graph must still reach no JS engine; and
# the driver binary is inspected for VM symbols, VM libraries and embedded scripts.
- name: Native engine gate E
working-directory: packages/runtime-native
run: |
set -euo pipefail
cmake --preset tn-linux -B build/tn-linux-engine-asan -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DTN_ENGINE_ONLY=ON -DTN_ENGINE_SANITIZE=ON \
-DMYSTRAL_USE_V8=OFF -DMYSTRAL_USE_QUICKJS=OFF -DMYSTRAL_USE_DAWN=ON
cmake --build build/tn-linux-engine-asan --target tn-native-engine-gate-e --target tn-native-engine-tests --parallel "$(nproc)"
ctest --test-dir build/tn-linux-engine-asan -L native-sanitizer --no-tests=error --output-on-failure
cmake --build build/tn-linux --target tn-native-engine-gate-e --target tn-native-engine-tests --parallel "$(nproc)"
ctest --test-dir build/tn-linux -L native-engine --output-on-failure
node scripts/inspect-js-free.mjs --binary build/tn-linux/tn-native-engine-gate-e \
--manifest ../../artifacts/native-engine/gate-e.json --target tn-native-engine-gate-e --capability gate-e
# PRD-499, owner decision 4: the portable engine core builds and passes its tests as Wasm
# under node, so it stays Wasm-safe from day one. emsdk is pinned by tag and SDK version.
- name: Native engine core as Wasm
working-directory: packages/runtime-native
shell: bash
run: |
set -euo pipefail
git clone --depth 1 --branch 6.0.11 https://github.com/emscripten-core/emsdk.git "$RUNNER_TEMP/emsdk"
"$RUNNER_TEMP/emsdk/emsdk" install 6.0.11
"$RUNNER_TEMP/emsdk/emsdk" activate 6.0.11
source "$RUNNER_TEMP/emsdk/emsdk_env.sh"
emcmake cmake -S . -B build/wasm-check -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build/wasm-check --target tn-native-engine-core-tests --parallel "$(nproc)"
ctest --test-dir build/wasm-check -L native-engine --output-on-failure
# PRD-533's web arms: the browser engine build and the pinned Perry toolchain are what
# web-engine-bench.spec.ts and perry-packed.spec.ts load, so they run here and nowhere
# else (TN_WEB_ENGINE_SPECS); wasm-dis comes from emsdk's binaryen.
cmake --preset wasm-browser
cmake --build build/wasm-browser --target tn-native-engine-wasm-browser --parallel "$(nproc)"
node ../../tools/native-typescript/provision.mjs
export PATH="$PATH:$EMSDK/upstream/bin"
(cd ../.. && TN_WEB_ENGINE_SPECS=1 pnpm exec vitest run \
scripts/__tests__/web-engine-bench.spec.ts scripts/__tests__/perry-packed.spec.ts)
- name: Report the compiler cache hit rate
if: always()
shell: bash
Expand All @@ -531,7 +575,19 @@ jobs:
# `native:physics:parity` ride along inside that script, which is the package's whole gate.
- run: pnpm --filter @threenative/runtime-native test
env:
TN_GATE_E_BINARY: ${{ github.workspace }}/packages/runtime-native/build/tn-linux/tn-native-engine-gate-e
TN_REQUIRE_ANDROID_CANVAS_SKIA_FIXTURE: "1"
- uses: actions/upload-artifact@v7
if: always()
with:
name: native-engine-gate-e-evidence
path: |
artifacts/native-engine/gate-e.json
packages/runtime-native/build/tn-linux/native-engine-target-graph.txt
packages/runtime-native/build/tn-linux/Testing/Temporary/LastTest.log
packages/runtime-native/build/tn-linux-engine-asan/native-engine-target-graph.txt
packages/runtime-native/build/tn-linux-engine-asan/Testing/Temporary/LastTest.log
if-no-files-found: error
- name: Collect bounded native Linux performance evidence
if: always()
shell: bash
Expand Down
164 changes: 164 additions & 0 deletions .github/workflows/native-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ on:
# to four files, one of them touched by ordinary runtime-native PRs.
types: [opened, synchronize, reopened, labeled]
workflow_dispatch:
inputs:
native_engine:
description: Qualify only the JS-free Linux, Windows and macOS native-engine players (no release)
type: boolean
default: false

permissions:
contents: read
Expand Down Expand Up @@ -126,6 +131,7 @@ jobs:
# other PR. Every proof job below needs `gates`, so refusing here spends nothing.
if: >-
${{ !cancelled() &&
!(github.event_name == 'workflow_dispatch' && github.event.inputs && github.event.inputs.native_engine == 'true') &&
(github.event_name != 'pull_request' ||
github.event.pull_request.base.ref == 'main' ||
contains(github.event.pull_request.labels.*.name, 'release-proof')) &&
Expand Down Expand Up @@ -558,6 +564,164 @@ jobs:
path: packages/runtime-native/artifacts/
if-no-files-found: error

native-engine:
name: native-engine-${{ matrix.platform }}
# Independent of release gates: dispatch the selected branch, without a tag or main CI run.
if: github.event_name == 'workflow_dispatch' && inputs.native_engine
strategy:
fail-fast: false
matrix:
include:
- platform: linux
key: linux-x64
runner: ubuntu-24.04
preset: tn-linux
executable: tn-native-engine-player
dawn_dependency: dawn
- platform: macos
key: darwin-arm64
runner: macos-15
preset: tn-macos
executable: tn-native-engine-player
dawn_dependency: dawn
- platform: windows
key: win32-x64
runner: windows-2025
preset: tn-windows
executable: tn-native-engine-player.exe
dawn_dependency: skia-win-static
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
env:
TN_PLAYTEST_ALLOW_SOFTWARE: "1"
NATIVE_ENGINE_PRESET: ${{ matrix.preset }}
NATIVE_ENGINE_PLAYER: ${{ github.workspace }}/packages/runtime-native/build/${{ matrix.preset }}/${{ matrix.executable }}
steps:
- uses: actions/checkout@v7
- uses: actions/cache@v4
with:
path: packages/runtime-native/third_party
key: native-third-party-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('packages/runtime-native/scripts/download-deps.mjs', 'packages/runtime-native/scripts/package-android.mjs') }}
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 20
cache: pnpm
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- run: pnpm install --frozen-lockfile
- name: Build the existing playtest runner
run: pnpm --filter @threenative/playtest build
- name: Install Linux JS-free desktop build and display dependencies
if: runner.os == 'Linux'
run: >-
sudo apt-get update && sudo apt-get install -y
cmake ninja-build binutils pkg-config
libasound2-dev libpulse-dev libudev-dev libx11-dev libxext-dev libxrandr-dev
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb xauth xcompmgr
- name: Provision the host build's pinned SDL, stb and Dawn dependencies
shell: bash
env:
DAWN_DEPENDENCY: ${{ matrix.dawn_dependency }}
run: |
set -euo pipefail
cd packages/runtime-native
node scripts/download-deps.mjs --only sdl3
node scripts/download-deps.mjs --only stb
node scripts/download-deps.mjs --only "$DAWN_DEPENDENCY"
- name: Build the JS-free player with MSVC and inspect PE imports
if: runner.os == 'Windows'
shell: cmd
run: |
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSINSTALL=%%i"
if not defined VSINSTALL exit /b 1
call "%VSINSTALL%\Common7\Tools\VsDevCmd.bat" -arch=x64 -host_arch=x64
if errorlevel 1 exit /b 1
set "CC=cl"
set "CXX=cl"
where cl
if errorlevel 1 exit /b 1
cd packages\runtime-native
cmake --preset %NATIVE_ENGINE_PRESET% -DTN_ENGINE_ONLY=ON -DMYSTRAL_USE_V8=OFF -DMYSTRAL_USE_QUICKJS=OFF
if errorlevel 1 exit /b 1
cmake --build --preset %NATIVE_ENGINE_PRESET% --target tn-native-engine-player --parallel
if errorlevel 1 exit /b 1
cd ..\..
node packages/runtime-native/scripts/inspect-js-free.mjs --binary "%NATIVE_ENGINE_PLAYER%" --manifest packages/runtime-native/artifacts/native-engine/js-free.json
- name: Build the JS-free player on Linux or macOS and inspect ELF or Mach-O
if: runner.os != 'Windows'
shell: bash
env:
CC: clang
CXX: clang++
run: |
set -euo pipefail
cd packages/runtime-native
cmake --preset "$NATIVE_ENGINE_PRESET" -DTN_ENGINE_ONLY=ON -DMYSTRAL_USE_V8=OFF -DMYSTRAL_USE_QUICKJS=OFF
cmake --build --preset "$NATIVE_ENGINE_PRESET" --target tn-native-engine-player --parallel
cd ../..
node packages/runtime-native/scripts/inspect-js-free.mjs --binary "$NATIVE_ENGINE_PLAYER" --manifest packages/runtime-native/artifacts/native-engine/js-free.json
- name: Run inspect, input and capture journeys on the built player
shell: bash
run: |
set -euo pipefail
for journey in inspect input capture; do
artifacts="packages/runtime-native/artifacts/native-engine/$journey"
mkdir -p "$artifacts"
sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js "packages/runtime-native/tests/native-engine/playtests/native-engine-$journey.playtest.json" \
--target desktop --executable "$NATIVE_ENGINE_PLAYER" --artifacts "$artifacts" \
> "$artifacts/runner.log" 2>&1 && status=0 || status=$?
cat "$artifacts/runner.log"
test "$status" -eq 0
done
- name: Record the Linux adapter (software is acceptable)
if: always() && runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
grep -hE '\[WebGPU\] (Adapter|Vendor|Backend):' packages/runtime-native/artifacts/native-engine/*/console.json \
| tee packages/runtime-native/artifacts/native-engine/linux-adapter.txt
grep -F '[WebGPU] Adapter:' packages/runtime-native/artifacts/native-engine/linux-adapter.txt
cat packages/runtime-native/artifacts/native-engine/linux-adapter.txt >> "$GITHUB_STEP_SUMMARY"
- name: Record the Windows adapter (D3D12 WARP is acceptable)
if: always() && runner.os == 'Windows'
shell: bash
run: |
set -euo pipefail
grep -hE '\[WebGPU\] (Adapter|Vendor|Backend):' packages/runtime-native/artifacts/native-engine/*/console.json \
| tee packages/runtime-native/artifacts/native-engine/windows-adapter.txt
grep -F '[WebGPU] Adapter:' packages/runtime-native/artifacts/native-engine/windows-adapter.txt
cat packages/runtime-native/artifacts/native-engine/windows-adapter.txt >> "$GITHUB_STEP_SUMMARY"
- name: Stage the inspected and verified player
shell: bash
env:
NATIVE_ENGINE_KEY: ${{ matrix.key }}
run: |
node --input-type=module <<'NODE'
import { copyFileSync, existsSync, mkdirSync, statSync } from "node:fs";
import { basename, join } from "node:path";
const source = process.env.NATIVE_ENGINE_PLAYER;
const output = join("release", `native-engine-${process.env.NATIVE_ENGINE_KEY}`);
mkdirSync(output, { recursive: true });
copyFileSync(source, join(output, basename(source)));
if (statSync(source).size === 0) throw new Error(`${source} is empty.`);
const map = source.replace(/\.exe$/i, ".map");
if (process.platform === "win32" && existsSync(map)) copyFileSync(map, join(output, basename(map)));
NODE
- uses: actions/upload-artifact@v7
with:
name: native-engine-player-${{ matrix.key }}
path: release/native-engine-${{ matrix.key }}/
if-no-files-found: error
- uses: actions/upload-artifact@v7
if: always()
with:
name: evidence-native-engine-${{ matrix.key }}
path: packages/runtime-native/artifacts/native-engine/
if-no-files-found: error

build-android:
needs: [validate-tag, gates]
if: >-
Expand Down
Loading