Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
317 commits
Select commit Hold shift + click to select a range
416e061
fix(runner): the jump clears a block
jonit-dev Oct 26, 2026
7c06fb2
fix(runner): count a near miss only beside a real obstacle
jonit-dev Oct 26, 2026
0a6ea30
fix(minimal): the follow camera stays in front of the arena wall (PRD…
jonit-dev Oct 26, 2026
3a79623
test(collectathon): drop the HUD 'covered' count that could not fail …
jonit-dev Oct 26, 2026
35d18d7
fix(sailing): the end panel names the loss, and its clock and wind ho…
jonit-dev Oct 26, 2026
7219e92
fix(sailing): take the capsize test key out of the players' hint
jonit-dev Oct 26, 2026
45b30ca
fix(puzzle): the V replay keeps the shove count and reports its bodies
jonit-dev Oct 26, 2026
cf2e18f
test(create-threenative): re-pin scaffold hashes for the PR #574 ARPG…
jonit-dev Oct 26, 2026
07e8fe0
feat(shooter): generated tiling surfaces on the town's procedural geo…
jonit-dev Oct 26, 2026
6d1981d
fix(tower-defense): a pooled walker no longer respawns with a chilled…
jonit-dev Oct 26, 2026
1f8c279
docs(prd-748): plan performance by construction, the engine decides t…
jonit-dev Oct 26, 2026
007e18c
fix(rts): A arms attack-move without panning the camera
jonit-dev Oct 26, 2026
321f6ce
docs(prd-748): evidence from the kit perf inventory; Phase 1 gains st…
jonit-dev Oct 26, 2026
b2c3781
fix(rts): the minimap draws units where they are and the real camera …
jonit-dev Oct 26, 2026
052563a
docs(prd-749): one kit base and a kit contract, fix a bug once and pr…
jonit-dev Oct 26, 2026
01897ce
refactor(platformer): read the fox's leg by name, keep fox.ts under 2…
jonit-dev Oct 26, 2026
2f50f8f
test(minimal): runtime-cost sentinel passes followCamera a wall probe…
jonit-dev Oct 26, 2026
c0673cb
fix(puzzle): the HUD shows the player's progress, not the vault's phy…
jonit-dev Oct 26, 2026
4f14b83
docs(prd-748,749): default-on perf capabilities audit; agent docs des…
jonit-dev Oct 26, 2026
b4f5e18
test(create-threenative): re-pin scaffold hashes for the PRD-747 kit …
jonit-dev Oct 27, 2026
2285fba
fix(rts): rolling the wheel away from you zooms in
jonit-dev Oct 27, 2026
b7bb146
docs(rts): list the rts-pause and rts-zoom scenarios
jonit-dev Oct 27, 2026
63ecd53
fix(racing): a car the player stopped is parked, not beached
jonit-dev Oct 27, 2026
e87a6fd
fix(rts): a finished match shows its result and can be played again
jonit-dev Oct 27, 2026
5ba2e35
docs(rts): name the zoom axis sign by DOM deltaY
jonit-dev Oct 27, 2026
9d1a848
PRD-749 phase 1: the kit contract suite, sightLine and hudAudit asser…
jonit-dev Oct 27, 2026
4d3325a
test(scaffold): re-pin the kart, puzzle, racing, runner and sailing t…
jonit-dev Oct 27, 2026
b74b9fe
test(create-threenative): re-pin the rts and tower-defense scaffold h…
jonit-dev Oct 27, 2026
efe3091
test(kart): motion blur off (owner: the kart seems to hang while driv…
jonit-dev Oct 27, 2026
09399a2
feat(core): AnimationPlayer.play takes a one-shot speed, like Godot's…
jonit-dev Oct 27, 2026
7c4ced6
fix(action-rpg): the roll plays its whole clip, travels and faces whe…
jonit-dev Oct 27, 2026
3485aea
feat(templates): the starter kits share one 0 EV exposure meter (PRD-…
jonit-dev Oct 27, 2026
94bb7dd
fix(action-rpg): the dungeon returns to the owner's 7b546d6 neutral l…
jonit-dev Oct 27, 2026
d363a83
fix(core): a stride-synced walk in the 32-128 px band evaluates every…
jonit-dev Oct 27, 2026
c5b5e9e
fix(kart): every kart pitches with the road between samples, so no ka…
jonit-dev Oct 27, 2026
384e311
fix(shooter): no motion blur, a body that eases to speed and stops, a…
jonit-dev Oct 27, 2026
9d1561f
Merge branch 'lane/pr574-exposure' into pr563-followup
jonit-dev Oct 27, 2026
5afa0dc
Merge branch 'lane/pr574-look' into pr563-followup
jonit-dev Oct 27, 2026
e473626
Merge branch 'lane/pr574-arpg' into pr563-followup
jonit-dev Oct 27, 2026
be4aa8d
Merge branch 'lane/pr574-kits-a' into pr563-followup
jonit-dev Oct 27, 2026
a05b48e
Merge branch 'lane/pr574-kits-b' into pr563-followup
jonit-dev Oct 27, 2026
c58ef0f
Merge branch 'lane/pr574-kits-c' into pr563-followup
jonit-dev Oct 27, 2026
1905481
Merge branch 'lane/pr574-kitbase' into pr563-followup
jonit-dev Oct 27, 2026
60aca2c
Revert "fix(physics): a ray from inside a capsule skips the capsule (…
jonit-dev Oct 27, 2026
366efa7
test(scaffold): re-pin the 16 kit trees after merging the PR #574 kit…
jonit-dev Oct 27, 2026
3846da2
Reapply "fix(physics): a ray from inside a capsule skips the capsule …
jonit-dev Oct 27, 2026
024c9d5
fix(runtime-native): a native ray from inside a capsule or ball skips…
jonit-dev Oct 27, 2026
585fbf4
chore(pr574): format the merged kit-contract files, fit create-threen…
jonit-dev Oct 27, 2026
313c242
test(pr574): re-pin 5 kit hashes after the biome format pass
jonit-dev Oct 27, 2026
fde6169
test(native-engine): the TRAA invariant-scope test counts the per-sha…
jonit-dev Oct 27, 2026
7f0cd69
fix(shooter): the hands walk on Walk and sprint on Run at their autho…
jonit-dev Oct 27, 2026
3ece387
PRD-752: run web games in a worker so the page cannot stall them
jonit-dev Oct 27, 2026
6cb7191
profile:wasm-page --load runs a page under a stated CPU load (PRD-752)
jonit-dev Oct 27, 2026
253ee50
docs(prd-748): the engine owns the sim budget, query cost, tick signi…
jonit-dev Oct 27, 2026
43cf584
docs(prd-573): Phase v2.7, the web bridge's JS side under 2% of the f…
jonit-dev Oct 27, 2026
c4f4644
Core reads window and document only through platform.ts; loaded-host …
jonit-dev Oct 27, 2026
3795190
UI bridge loopback over a MessageChannel behind ?tnUiLoopback=1 (PRD-…
jonit-dev Oct 27, 2026
580cefe
fix(kart): a spun or tumbled kart lifts by its own dip instead of rot…
jonit-dev Oct 27, 2026
69166e5
PRD-752 phase 2 done: native desktop gate and starter loopback proof
jonit-dev Oct 27, 2026
453193d
fix(ui): DebugOverlay reads the registry snapshot only while it is op…
jonit-dev Oct 27, 2026
b713dec
perf(shooter): soldiers check the grip on the rifle's own box, sight …
jonit-dev Oct 27, 2026
8ca40db
fix(ui): DebugOverlay reads the registry snapshot only while it is op…
jonit-dev Oct 27, 2026
45a1605
docs(prd-748): the loading cover hides page UI by default; debug-only…
jonit-dev Oct 27, 2026
8f8fcbc
Engine side of the worker boot: platform hook, worker engine bundle, …
jonit-dev Oct 27, 2026
459fe28
fix(kart): models draw between fixed ticks, a hit costs 8 box corners…
jonit-dev Oct 27, 2026
929d0d9
Worker game host: @threenative/core/worker runs a game in a worker on…
jonit-dev Oct 27, 2026
e8f494f
Worker game audio: the page's AudioContext plays a worker game's soun…
jonit-dev Oct 27, 2026
8c60a7e
feat(quality): a ThreeNative ESLint plugin: tree walks and GPU resour…
jonit-dev Oct 27, 2026
f66681f
fix(engine): sibling renderers no longer hand a texture change back a…
jonit-dev Oct 27, 2026
94a0f7a
Worker game HUD: the UI bridge works when the game connects while its…
jonit-dev Oct 27, 2026
e575927
Starter: worker.html runs the same game in a web worker (PRD-752)
jonit-dev Oct 27, 2026
150a391
profile:wasm-page times frames on the game's own thread (PRD-752)
jonit-dev Oct 27, 2026
2cc1c66
fix(shooter): shot vessels burst into drawn shards again; Breakables …
jonit-dev Oct 27, 2026
2013a6f
Worker game pointer lock: mouse look works for a game in a worker (PR…
jonit-dev Oct 27, 2026
725bdfa
Merge branch 'lane/pr574-kits-b' into pr563-followup
jonit-dev Oct 27, 2026
ab3142a
test(shooter): the idle hands case calls locomotion with its two argu…
jonit-dev Oct 27, 2026
5f2dc58
docs(prd-748): a material the game holds stays the material that draw…
jonit-dev Oct 27, 2026
4970303
profile:wasm-page times the frames the game presents (PRD-752)
jonit-dev Oct 27, 2026
25518c3
Shooter: worker.html runs the same game in a web worker; PRD-752 deci…
jonit-dev Oct 27, 2026
ea25ac5
Worker game playtest bridge: mirror it while start is held for the ru…
jonit-dev Oct 27, 2026
b3e9b02
fix(core): stride sync leaves a camera-carried rig at its authored ra…
jonit-dev Oct 27, 2026
7957c1c
feat(ui): page UI reads nothing behind the loading cover; useUiStateD…
jonit-dev Oct 27, 2026
2cedb1b
test(scaffold): re-pin the shooter hash for the hands rate guard
jonit-dev Oct 27, 2026
a0ecc6f
fix(engine): a render target's renderer no longer draws an output ima…
jonit-dev Oct 27, 2026
21f8a38
Worker is the default page for the starter and shooter kits (PRD-752)
jonit-dev Oct 27, 2026
aec24ba
fix(core): the camera cull stops walking the camera's children when t…
jonit-dev Oct 27, 2026
28109e0
test(shooter): hands-authored-rate measures the clip seconds the hand…
jonit-dev Oct 27, 2026
d8e10d3
docs(prd-748): record the render-cost attribution, three fixes and th…
jonit-dev Oct 27, 2026
cabf269
Merge branch 'lane/pr574-look' into pr563-followup
jonit-dev Oct 27, 2026
6630a2a
Merge branch 'lane/prd748-render-cost' into pr563-followup
jonit-dev Oct 27, 2026
7bc7600
Engine raycast: a cached triangle BVH per geometry (PRD-752)
jonit-dev Oct 27, 2026
b511479
feat(core): static meshes that share a material merge into one draw b…
jonit-dev Oct 27, 2026
105f492
Worker mode: fix the review findings in the worker host, remote audio…
jonit-dev Oct 27, 2026
32a62eb
profile:wasm-page --vsync keeps the browser's frame pacing for worker…
jonit-dev Oct 27, 2026
3800a24
fix(playtest): the HUD audit composites a parent's background under t…
jonit-dev Oct 27, 2026
e41513c
fix(core): auto batch merges at scene enter, and warm-up and projecti…
jonit-dev Oct 27, 2026
1d32b8f
Merge branch 'lane/quality-rules' into pr563-followup
jonit-dev Oct 27, 2026
ca2bbf0
Playtest: a worker game relaunches without the unlimited frame rate (…
jonit-dev Oct 27, 2026
d600e7b
PRD-752: the worker KPI box, measured with vsync on both kits
jonit-dev Oct 27, 2026
3a07599
profile:wasm-page spec: record the launch args without a comma expres…
jonit-dev Oct 27, 2026
7fe753b
Playtest: a live-clock hold counts the ticks the game ran, not the re…
jonit-dev Oct 27, 2026
e4d9081
PRD-752: starter and harness boxes ticked; worker-in-Xvfb and live-cl…
jonit-dev Oct 27, 2026
4bc3055
PRD-752: visual judge NEUTRAL ticked; shooter performance on the real…
jonit-dev Oct 27, 2026
298a806
PRD-752: no worker-only frame rate under Xvfb (scheduler probe, draw-…
jonit-dev Oct 27, 2026
4bc3150
PRD-752: phase 4 ceiling measured (shooter about 6%, Midway about 25%…
jonit-dev Oct 27, 2026
6639da5
PRD-752: phase 4 threaded-build probe (1-5% single-thread cost); job …
jonit-dev Oct 27, 2026
c4fe5ef
PRD-752: threaded-build cost re-measured on one source tree (about 1.…
jonit-dev Oct 27, 2026
e2462de
Merge branch 'prd-573-v5-worker-mode' of https://github.com/ThreeNati…
jonit-dev Oct 27, 2026
38a15fb
docs(prd-748): tick the loading-cover box with the shooter page and w…
jonit-dev Oct 27, 2026
c7f09f5
fix(kits): adventure, collectathon and rain HUD text reaches 3:1 (PRD…
jonit-dev Oct 27, 2026
c859964
Merge branch 'lane/pr574-hudclean' into pr563-followup
jonit-dev Oct 27, 2026
4e74eea
Starter-kit authoring loop, one KPI dashboard, reverse-engineer refer…
jonit-dev Oct 27, 2026
b30e395
Merge lane/prd748-autobatch (e41513cce): static meshes that share a m…
jonit-dev Oct 27, 2026
e743be3
fix(core): auto batch keeps a still parent merged when its children h…
jonit-dev Oct 27, 2026
11d0ff3
Merge remote-tracking branch 'origin/pr563-followup' into pr563-followup
jonit-dev Oct 27, 2026
c4c7c50
restart-resets: bound running clocks with atSteps lte; platformer get…
jonit-dev Oct 27, 2026
6f46f6b
explore-clean: judge the path walked, not the net displacement (PRD-749)
jonit-dev Oct 27, 2026
167fa7f
action-clip-completes: judge each clip at its own step; action-rpg pu…
jonit-dev Oct 27, 2026
dfde9f7
scaffold spec: re-pin action-rpg and platformer hashes after the rest…
jonit-dev Oct 27, 2026
016f4cd
fix(playtest): sightLine no longer counts cutouts, effect volumes or …
jonit-dev Oct 27, 2026
045aaab
fix(kits): the camera no longer hides the player in starter, platform…
jonit-dev Oct 27, 2026
5ea8da2
fix(playtest): a sliver is a rope or a rail only, a thin panel still …
jonit-dev Oct 27, 2026
5fc0382
action-rpg swing scenario: the fitted swing lands near tick 10, so th…
jonit-dev Oct 27, 2026
aaa09a6
Merge branch 'lane/pr574-rows-restart' into pr563-followup
jonit-dev Oct 27, 2026
a7ad16e
Merge branch 'lane/pr574-rows-camera' into pr563-followup
jonit-dev Oct 27, 2026
835552b
scaffold spec: re-pin action-rpg, adventure, platformer, puzzle and s…
jonit-dev Oct 27, 2026
6ef2145
style: biome format the camera lane's kit files; re-pin platformer, p…
jonit-dev Oct 27, 2026
6b40878
docs(prd-748): auto batch box progress: census numbers and the NEUTRA…
jonit-dev Oct 27, 2026
9b0bdc8
docs(prd-749): contract suite box ticked (16 kits, opt-out rule spec)…
jonit-dev Oct 27, 2026
b826711
Merge remote-tracking branch 'origin/develop' into pr563-followup
jonit-dev Oct 27, 2026
7661b10
test(native-engine): the offscreen GPU timer sample has three passes …
jonit-dev Oct 27, 2026
2522f25
docs(native): refresh the native coverage record after the develop merge
jonit-dev Oct 27, 2026
cfbc222
docs(prd-749): platformer camera row experiments and why none shipped
jonit-dev Oct 27, 2026
7eae4c0
build(web-engine): the ray BVH and geometry generators build at -Os o…
jonit-dev Oct 27, 2026
2d6fab1
fix(test): lift the camera-cut exposure rooms over the black bucket (…
jonit-dev Oct 27, 2026
9085617
perf(core): auto batch counts scene adds and removes from graph event…
jonit-dev Oct 27, 2026
73ffa77
perf(native-engine): the renderer's batch decision leaves hidden subt…
jonit-dev Oct 27, 2026
9e38c0e
perf(core): the five-second projection window line no longer walks th…
jonit-dev Oct 27, 2026
bf1a59f
style(core): sort the projection spec imports
jonit-dev Oct 27, 2026
32f312f
fix(test): lift the plain exposure room one stop over the black bucke…
jonit-dev Oct 27, 2026
da486c1
feat(core): the frame meter reports drawn frames beside presented
jonit-dev Oct 27, 2026
39a9d19
fix(test): core spec fixtures match the projection window and drawn-f…
jonit-dev Oct 27, 2026
e51f9b1
docs(agents): make performance mistakes painful to write: engine firs…
jonit-dev Oct 27, 2026
29665a9
fix(core): the worker's six unknown casts carry their waiver reasons
jonit-dev Oct 27, 2026
60846cd
fix(native-engine): the pruned matrix walk defers a hidden node's own…
jonit-dev Oct 27, 2026
b430a97
perf(templates): the backlight graph reads its controls once per render
jonit-dev Oct 27, 2026
410cf3f
docs(native): refresh the native coverage record after the matrix wal…
jonit-dev Oct 27, 2026
a1da6d1
fix(build): the desktop UI build bundles src/ui alone, not the kit's …
jonit-dev Oct 27, 2026
8f4ca99
fix(test): the hot-reload lane proves the starter's page-thread entry…
jonit-dev Oct 27, 2026
5d7bf50
docs(perf): record the 2026-10-27 shooter campaign levers and the cro…
jonit-dev Oct 27, 2026
f550095
fix(budgets): worker-only browser globals carry native allowlist reas…
jonit-dev Oct 27, 2026
d7cd652
perf(three-native): the web engine keeps what game code reads again, …
jonit-dev Oct 27, 2026
95e3376
perf(core): the camera pass keeps its rig and camera walks while the …
jonit-dev Oct 27, 2026
efa9e1d
perf(core): on the native engine the camera pass asks each rig part i…
jonit-dev Oct 27, 2026
6ddf30c
chore(native): registry snapshot and native coverage record for the f…
jonit-dev Oct 27, 2026
e66e7af
wip(bughunt): 02-autobatch-matrix-refresh (arm result, unreviewed)
jonit-dev Oct 27, 2026
8c45a52
wip(bughunt): 04-unchanged-frame-storage-upload (arm result, unreviewed)
jonit-dev Oct 27, 2026
f683c14
wip(bughunt): 05-nondynamic-force-resets (arm result, unreviewed)
jonit-dev Oct 27, 2026
295b2f9
wip(bughunt): 06-readback-staging-churn (arm result, unreviewed)
jonit-dev Oct 27, 2026
8c44c86
wip(bughunt): 07-cached-lamp-uniform-work (arm result, unreviewed)
jonit-dev Oct 27, 2026
1c18355
wip(bughunt): 08-bridge-scope-empty-allocation (arm result, unreviewed)
jonit-dev Oct 27, 2026
b942237
wip(bughunt): 03-full-rate-animation-bookkeeping (arm result, unrevie…
jonit-dev Oct 27, 2026
a2ffeaa
fix(core): a zero-dt held tick still banks action time before a switc…
jonit-dev Oct 27, 2026
87bfb96
fix(skills): the performance skill's harness-gap line fits its 259-wo…
jonit-dev Oct 27, 2026
b4b274d
chore(native): native coverage record for the bug-hunt renderer fixes
jonit-dev Oct 27, 2026
f7d2adc
fix(ci): the benchmark browser runs headed under Xvfb, where SwiftSha…
jonit-dev Oct 27, 2026
6274683
perf(three-native): keep an action's schedule, transform-only calls a…
jonit-dev Oct 27, 2026
889264d
fix(native-engine): a scratch object's death leaves the hierarchy cou…
jonit-dev Oct 27, 2026
94e7c06
perf(physics): Rapier reads a fixed or sleeping body's pose once and …
jonit-dev Oct 27, 2026
95a8f3d
docs(perf): record the Rapier crossings lever on real GPU and the phy…
jonit-dev Oct 27, 2026
0eabee8
test(core): the worker messageerror case polls for the page bridge in…
jonit-dev Oct 27, 2026
d76bac4
chore(native): native coverage record for the folded engine fixes
jonit-dev Oct 27, 2026
49c37b6
Merge origin/develop (226ec257a, PRD-741 temporal upsampling) into PR…
jonit-dev Oct 27, 2026
8756937
fix(core): SMAA builds in a worker on the three.js engine
jonit-dev Oct 27, 2026
32872b4
perf(three-native): the web renderer converts a number clear colour once
jonit-dev Oct 27, 2026
1ae833d
feat(engine): engine-cost counters for the cull rig map, rig walks an…
jonit-dev Oct 27, 2026
87bfb74
perf(core): the camera pass tests its frustum and forward ray in plai…
jonit-dev Oct 27, 2026
4fcf9bd
fix(core): a worker game reads the page's navigator, so a touch devic…
jonit-dev Oct 27, 2026
77cec53
fix(core,playtest): the startup hand-off is bounded, and the runner w…
jonit-dev Oct 27, 2026
a9ddffa
fix(playtest,three-native): the bridge census leaves the playtest obs…
jonit-dev Oct 27, 2026
95a4c25
fix(core,three-native,playtest): a lost WebGPU device is named on bot…
jonit-dev Oct 27, 2026
8ee502f
test(core): the ui-bridge loopback test polls for port delivery
jonit-dev Oct 28, 2026
9ecd6b2
fix(core): the worker SMAA lookup read drops its double cast
jonit-dev Oct 28, 2026
45c8c70
chore(native): native coverage record for the folded lane commits
jonit-dev Oct 28, 2026
a4b6767
fix(create-threenative): the performance skill keeps the two sentence…
jonit-dev Oct 28, 2026
34dd191
wip(bughunt2): 02-post-effect-size-cache (arm result, unreviewed)
jonit-dev Oct 28, 2026
8048333
wip(bughunt2): 03-physics-unchanged-quaternion (arm result, unreviewed)
jonit-dev Oct 28, 2026
0dad0ed
wip(bughunt2): 04-stride-rate-rewrite (arm result, unreviewed)
jonit-dev Oct 28, 2026
67a9d63
wip(bughunt2): 05-uniform-lane-allocation (arm result, unreviewed)
jonit-dev Oct 28, 2026
da17845
perf(engine): AutoBatch checks its merged sources in C++, one lap per…
jonit-dev Oct 28, 2026
8dbca27
wip(shooter): unfinished kit crossing fixes (lane handed to leanpi)
jonit-dev Oct 28, 2026
a2724a0
perf(shooter): the kit reads collider bounds, model height and hand g…
jonit-dev Oct 28, 2026
4b1d47b
fix(create-threenative): resolve the scaffold.spec hash conflict from…
jonit-dev Oct 28, 2026
d6837a1
fix(three-native): a uniform write hands the sink its own snapshot, n…
jonit-dev Oct 28, 2026
ce63861
perf(runtime-native): one job pool for native and web, the matrix wal…
jonit-dev Oct 27, 2026
89fcdaf
feat(web): a cross-origin-isolated page loads the threaded engine and…
jonit-dev Oct 27, 2026
ed47d90
feat(create-threenative): serve and build web games cross-origin isol…
jonit-dev Oct 27, 2026
feb5345
perf(runtime-native): split a deep subtree across the job pool instea…
jonit-dev Oct 27, 2026
a1711f3
docs(prd-752): phase 4 isolation box proved; pool and A/B numbers; th…
jonit-dev Oct 27, 2026
136617c
perf(runtime-native): the walk shares out on composes, not visits, an…
jonit-dev Oct 27, 2026
fc20074
docs(prd-752): the shooter reaches the pool ceiling; threejs kits che…
jonit-dev Oct 27, 2026
fcde88a
fix(runtime-native): the pool keeps the pruned walk, decomposes camer…
jonit-dev Oct 28, 2026
044dbb8
docs(prd-752): pruned contract on the pool, the camera race, and a po…
jonit-dev Oct 28, 2026
442b751
docs(prd-753): a scene change updates only the batches it touches (Un…
jonit-dev Oct 27, 2026
c2b4e6f
feat(native): a walk counter names who rewalks the projection scene, …
jonit-dev Oct 27, 2026
5a777b0
docs(prd-753): deferred at sizing, the projection walks take 0.06-0.3…
jonit-dev Oct 28, 2026
59adec9
chore(native): native coverage record for the threads, AutoBatch guar…
jonit-dev Oct 28, 2026
c686e6f
fix(core): a hot restart keeps the HUD of a session that already show…
jonit-dev Oct 28, 2026
cb425c7
fix(core): the kept HUD survives an update that lands before it is po…
jonit-dev Oct 28, 2026
bb6016a
test(browser): the hot-reload proof waits for the first boot's reveal…
jonit-dev Oct 28, 2026
0effba4
test(runtime-native): the pool tests expect no threads in a single-th…
jonit-dev Oct 28, 2026
53684c8
perf(runtime-native): the sun's shadow map is kept while nothing it d…
jonit-dev Oct 27, 2026
5ba1089
chore(native): native coverage record for the sun shadow cache fold
jonit-dev Oct 28, 2026
3e5ee50
feat(engine): count the work the shooter repeats every frame, by class
jonit-dev Oct 27, 2026
8974fd5
docs(perf): the uniform re-lane lever, measured and handed to its lan…
jonit-dev Oct 27, 2026
937bb39
test(playtest,core,engine): every kit's engine work stays under its m…
jonit-dev Oct 28, 2026
9cbfd81
perf(three-native): each bridge crossing pays 27% less JS glue
jonit-dev Oct 28, 2026
52b4bc2
perf(three-native): a node the engine raycasts nothing for crosses no…
jonit-dev Oct 28, 2026
366b54f
chore(native): native coverage record for the bridge counter fold
jonit-dev Oct 28, 2026
de89ab0
test(runtime-native): the waste counter spec takes its directory from…
jonit-dev Oct 28, 2026
eb4cf11
perf(three-native): a material uniform resolves by name hash, not by …
jonit-dev Oct 28, 2026
35d0fc7
chore(native): native coverage record for the uniform name hash fold
jonit-dev Oct 28, 2026
6dc4419
perf(three-native): the glue counters count the wrapper entries the c…
jonit-dev Oct 28, 2026
e1c0ac9
perf(three-native): the glue bench times the two entries a cached rea…
jonit-dev Oct 28, 2026
5df378c
fix(native): restating a texture field leaves its version, so a resta…
jonit-dev Oct 28, 2026
b0b87a3
fix(three-native): the environment preparer waits on no draw for a sk…
jonit-dev Oct 28, 2026
5d9b62b
fix(core): a worker game on the three.js engine builds its AudioBus
jonit-dev Oct 28, 2026
10532ee
fix(core): the page audio server drops ops on a source that already e…
jonit-dev Oct 28, 2026
1d7be63
perf(core): the camera cull reads a geometry's buffer version once pe…
jonit-dev Oct 28, 2026
b865be7
fix(core): the worker AudioContext check reads window through hostWindow
jonit-dev Oct 28, 2026
9166826
chore(native): native coverage record for the restart and audio folds
jonit-dev Oct 28, 2026
d6553c3
docs(perf): record the session's levers: uniform name hash, job pool …
jonit-dev Oct 28, 2026
5403b48
fix(playtest): an iOS simulator launch waits until the new install re…
jonit-dev Oct 28, 2026
9c0f130
fix(playtest): read the iOS bundle path from the listapps entry past …
jonit-dev Oct 28, 2026
2b7e3c0
fix(engine): a rig's walk holds while an unrelated subtree changes (p…
jonit-dev Oct 28, 2026
e1e43c7
Merge remote-tracking branch 'origin/develop' into pr563-followup
jonit-dev Oct 28, 2026
3970585
fix(lockfile): the animation-mcp importer resolves three through this…
jonit-dev Oct 28, 2026
2ef8f59
Wire the animation MCP into the release, registry, golden-path and do…
jonit-dev Oct 28, 2026
ed4fda5
chore(native): native coverage record for the PR-574 tree
jonit-dev Oct 28, 2026
1a00197
fix(ci): the kit-loop KPI id carries an inline gitleaks allow, since …
jonit-dev Oct 28, 2026
083e99e
fix(native): the production collector passes every unscoped tool pack…
jonit-dev Oct 28, 2026
c71b3f5
fix(ci): the Linux production collector names its phases and gets a 3…
jonit-dev Oct 28, 2026
1af3023
fix(ci): the change-scope checkout fetches commits and trees, no blobs
jonit-dev Oct 28, 2026
8e6b63c
Every default MCP server reaches an agent that scaffolded mid-session…
jonit-dev Oct 28, 2026
ef06a9b
fix(ci): ci-required's checkout fetches commits and trees, no blobs
jonit-dev Oct 28, 2026
de94b94
fix(ci): short CI jobs fetch only the history they read
jonit-dev Oct 28, 2026
de345b8
Merge remote-tracking branch 'origin/develop' into pr574-green
jonit-dev Oct 28, 2026
cc8ebad
Recorded shadow passes and uniform slots on native, three's draw orde…
jonit-dev Oct 28, 2026
735a644
fix(ci): supply-chain's full-history scan gets the budget its 3.9 GB …
jonit-dev Oct 28, 2026
554e458
Merge develop (54ac77209) into pr563-followup
jonit-dev Oct 28, 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
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .agents/skills/native-performance-loop/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: native-performance-loop
description: Run an autonomous ThreeNative native-performance optimization loop using existing harnesses, quick synthetic load tests, and representative real games. Profile bottlenecks, make focused changes, accept or reject measured experiments, and maintain an HTML benchmark dashboard. Use for sustained native runtime optimization, frame pacing, startup, memory, or thermal efficiency campaigns, including the Tempest (rain) campaign where the web Wasm engine must beat stock three.js with the same image.
description: Run an autonomous ThreeNative native-performance optimization loop using existing harnesses, quick synthetic load tests, and representative real games. Profile bottlenecks, make focused changes, accept or reject measured experiments, and keep a campaign tab in the one `pnpm dashboard` hub. Use for sustained native runtime optimization, frame pacing, startup, memory, or thermal efficiency campaigns, including the Tempest (rain) campaign where the web Wasm engine must beat stock three.js with the same image.
---

# Native performance loop
Expand Down Expand Up @@ -28,8 +28,8 @@ the developer to supervise each iteration. Creating or editing this skill does n
Browser results are comparison or diagnosis, never substitute native evidence. Read the local
`measure-steady-state-fps` and `probe-android-startup-and-heat` skills when their lanes apply.
4. Create or resume `<primary>/artifacts/native-performance-loop/<campaign-id>/`. Record the
contract and results described in [dashboard.md](references/dashboard.md), and create
`index.html` immediately, including an honest empty state. Choose workloads and targets
contract and results described in [dashboard.md](references/dashboard.md), and show them in a
`pnpm dashboard` tab immediately, including an honest empty state. Choose workloads and targets
autonomously and state them. Default to a two-hour invocation budget, including setup and
verification, unless the user supplied another duration or experiment limit. Reserve the last
20 minutes for confirmation, restoration, and delivery; do not launch work that cannot finish
Expand Down Expand Up @@ -139,7 +139,7 @@ hypothesis; reaching a target does not establish a hardware limit. Do not instal
scheduler or claim work continues after the agent stops.

Before delivery, reproduce the best accepted build, rerun affected repository checks and native
scenarios, refresh `index.html`, and record unrun platforms honestly. Preserve the dashboard and
scenarios, update the campaign record, and record unrun platforms honestly. Preserve the record and
raw artifacts outside a disposable task checkout. Follow the repository's PR and worktree cleanup
rules within the session's existing authorization; no implicit push, merge or publication. Report
any retained worktree's exact path, size and cleanup blocker. Finish with the dashboard link,
Expand Down
36 changes: 18 additions & 18 deletions .agents/skills/native-performance-loop/references/dashboard.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# Campaign state and HTML dashboard
# Campaign state and the dashboard tab

Generate this on invocation, not when installing the skill. The HTML is the developer's view of
one machine-readable campaign record, not a second manually maintained evidence report.
Create this on invocation, not when installing the skill. The developer's view is one tab of the
single KPI hub, `pnpm dashboard` (`scripts/perf-live.ts`, PRD-751), reading one machine-readable
campaign record. Do not generate a per-campaign HTML page or a second evidence report.

## Files and updates

Use `<primary>/artifacts/native-performance-loop/<campaign-id>/` with:

- `results.json`: versioned contract, environment identities, workloads, baselines, incumbent,
experiment history, status, heartbeat, limits and next hypothesis.
- `index.html`: an offline, self-contained dashboard generated from `results.json`.
- `raw/<experiment-id>/`: existing harness output, exit codes, sample data, profiles, screenshots,
candidate patch/commit reference, build identities and exact reproduction commands.

Reuse a suitable existing report renderer if present. Otherwise generate a small HTML file with
inline CSS, embedded data and minimal browser JavaScript; save the small renderer alongside the
campaign only if needed to regenerate it reliably. No web framework, backend, CDN or new dependency.
It must open with `file://` without fetching JSON. Write JSON and HTML via temporary siblings and
atomic replacement, preserving the last readable snapshot if generation fails.
Reuse an existing hub tab when its reader fits the record (the Shadows vs Unreal tab reads a
campaign `results.json`). Otherwise add one: a small reader beside `scripts/shadow-perf/dashboard.ts`
that reads `results.json`, writes nothing and fails closed (missing = `pending`, malformed =
`invalid`), plus one `TABS` row and one route in `scripts/perf-live.ts`, with a test in
`scripts/__tests__/perf-live.spec.ts`. No web framework, CDN or new dependency. Write
`results.json` via a temporary sibling and atomic replacement, preserving the last readable record.

Create the dashboard before the first benchmark, showing “baseline pending,” never sample wins.
Refresh after each state transition and decision. During long commands, publish a timestamp and
heartbeat at least every 60 seconds using the existing gate status where available. The HTML must
show its snapshot time; when an old snapshot is open, say “reload for current progress” instead of
pretending it is live. If serving locally is already supported, optional refresh must preserve
filters. A stale heartbeat is “stale / check process,” never proof the worker is still active.
Add the tab before the first benchmark, showing “baseline pending,” never sample wins. Update the
record after each state transition and decision. During long commands, publish a timestamp and
heartbeat at least every 60 seconds using the existing gate status where available. The tab shows
the record's update time. A stale heartbeat is “stale / check process,” never proof the worker is
still active.

Escape benchmark names, descriptions, commands and log content as text. If embedding JSON in an
HTML script element, escape `<` as `\u003c` so data cannot terminate the script. Use textContent for
Expand Down Expand Up @@ -73,14 +73,14 @@ FPS derived from percentiles or average several games into an unqualified perfor

## Verify and resume

Before handing off an HTML snapshot, open it in a browser when a browser tool is available. Check
that it opens offline, shows actual recorded values, distinguishes missing/provisional data, and
Before handing off, open the tab in `pnpm dashboard` in a browser when a browser tool is available.
Check that it shows actual recorded values, distinguishes missing/provisional data, and
that filters, keyboard controls and evidence links work. A renderer with non-trivial logic needs
one small runnable check covering metric direction, missing values and unsafe text. If no browser
is available, check generated structure and links and mark visual inspection unverified.

On resume, load the contract and history, verify ownership and process state, and reconcile the
worktree with the recorded incumbent before editing. Check executable/workload/environment hashes;
if any comparison dimension changed, create a new series and baseline instead of connecting its
points to the old curve. Preserve all previous decisions. Link to this dashboard in the PRD/PR and
points to the old curve. Preserve all previous decisions. Link to this tab in the PRD/PR and
final response rather than copying its experiment log into another report.
8 changes: 4 additions & 4 deletions .claude/skills/ci-speed-loop/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: ci-speed-loop
description: Run ThreeNative's CI speed loop — measure how long a change takes to merge, name the biggest cost from the first failing jobs and queue wait, change one thing, re-measure the same window, record the iteration and refresh the HTML dashboard. Use when asked to speed up CI or the merge queue, work PRD-550, check progress toward the 30-minute merge goal, or refresh docs/ci-speed/index.html. Not for runtime FPS (perf-loop).
description: Run ThreeNative's CI speed loop — measure how long a change takes to merge, name the biggest cost from the first failing jobs and queue wait, change one thing, re-measure the same window, record the iteration. Use when asked to speed up CI or the merge queue, work PRD-550, check progress toward the 30-minute merge goal, or read the CI speed tab of `pnpm dashboard`. Not for runtime FPS (perf-loop).
---

# CI speed loop
Expand All @@ -20,11 +20,11 @@ node scripts/ci-merge-latency.mjs --since 2026-10-09 # measure only, pr
node scripts/ci-speed-loop.mjs record --since 2026-10-09 --label "gating board" --notes "..." [--decision keep]
node scripts/ci-speed-loop.mjs show # the ledger
node scripts/ci-speed-loop.mjs discard --iteration 3 --reason "queue outage, 2 PRs only"
node scripts/ci-speed-loop.mjs report # regenerate the HTML only
pnpm dashboard # the CI speed tab renders the ledger
```

`docs/ci-speed/ledger.json` is the record. `docs/ci-speed/index.html` is generated from it and opens with
`file://`. Never edit either by hand. Commit both with the change they measure.
`docs/ci-speed/ledger.json` is the record; the CI speed tab of `pnpm dashboard` renders it on each load
(PRD-751). Do not edit it by hand. Commit it with the change it measures.

## The turn

Expand Down
31 changes: 18 additions & 13 deletions .github/actions/native-engine-wasm/action.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Build the product web Wasm engine, or restore the last build of the same sources
description: >-
A game on the default `engine: "native"` runs in the browser on packages/runtime-native/build/web/
tn-native-engine-web.{mjs,wasm}. Nothing else builds it, `pnpm pack` drops a missing `files` entry
tn-native-engine-web{,-threads}.{mjs,wasm} (the threaded one for cross-origin-isolated pages). Nothing else builds it, `pnpm pack` drops a missing `files` entry
without an error, and a web build without it stops at TN_WASM_ENGINE_MISSING. Run this after
`pnpm install` and before anything that builds a web game or packs runtime-native.

Expand Down Expand Up @@ -29,7 +29,7 @@ runs:
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/packages/runtime-native/build/web
key: native-engine-web-wasm-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.emsdk-version }}-${{ hashFiles('packages/runtime-native/src/engine/**', 'packages/runtime-native/include/**', 'packages/runtime-native/cmake/**', 'packages/runtime-native/CMakeLists.txt', 'packages/runtime-native/CMakePresets.json', 'packages/runtime-native/native-deps.lock.json', 'packages/runtime-native/scripts/download-deps.mjs', 'packages/runtime-native/scripts/web-engine-stamp.mjs') }}
key: native-engine-web-wasm-v2-${{ runner.os }}-${{ runner.arch }}-${{ inputs.emsdk-version }}-${{ hashFiles('packages/runtime-native/src/engine/**', 'packages/runtime-native/include/**', 'packages/runtime-native/cmake/**', 'packages/runtime-native/CMakeLists.txt', 'packages/runtime-native/CMakePresets.json', 'packages/runtime-native/native-deps.lock.json', 'packages/runtime-native/scripts/download-deps.mjs', 'packages/runtime-native/scripts/web-engine-stamp.mjs') }}
- name: Build the web engine
if: steps.wasm.outputs.cache-hit != 'true'
shell: bash
Expand All @@ -49,21 +49,26 @@ runs:
node scripts/download-deps.mjs --only "$dependency"
done
source "$GITHUB_WORKSPACE/.cache/emsdk/emsdk_env.sh"
cmake --preset wasm
cmake --build --preset wasm --parallel "$(nproc)"
for preset in wasm wasm-threads; do
cmake --preset "$preset"
cmake --build --preset "$preset" --parallel "$(nproc)"
done
# Fail closed: a partial cache entry or a build that wrote nothing must not reach a pack.
- name: Check the web engine exists
shell: bash
working-directory: packages/runtime-native
run: |
set -euo pipefail
for file in build/web/tn-native-engine-web.mjs build/web/tn-native-engine-web.wasm; do
test -s "$file" || { echo "TN_WEB_ENGINE_MISSING: $file was not built" >&2; exit 1; }
done
wasm_bytes=$(stat -c %s build/web/tn-native-engine-web.wasm)
allowed_bytes=4194304
if [ "$wasm_bytes" -gt "$allowed_bytes" ]; then
echo "TN_WEB_ENGINE_OVER_BUDGET: measured $wasm_bytes bytes, allowed $allowed_bytes bytes" >&2
exit 1
fi
echo "web engine ready: $wasm_bytes byte wasm (allowed $allowed_bytes bytes)"
# The single-thread engine and the threaded one (PRD-752 phase 4), each under the budget.
for name in tn-native-engine-web tn-native-engine-web-threads; do
for file in "build/web/$name.mjs" "build/web/$name.wasm"; do
test -s "$file" || { echo "TN_WEB_ENGINE_MISSING: $file was not built" >&2; exit 1; }
done
wasm_bytes=$(stat -c %s "build/web/$name.wasm")
if [ "$wasm_bytes" -gt "$allowed_bytes" ]; then
echo "TN_WEB_ENGINE_OVER_BUDGET: $name.wasm measured $wasm_bytes bytes, allowed $allowed_bytes bytes" >&2
exit 1
fi
echo "web engine ready: $name.wasm $wasm_bytes bytes (allowed $allowed_bytes bytes)"
done
38 changes: 35 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,11 @@ jobs:
# The old 90 s cap sat under the floor and killed a healthy run at exactly 90 s (exit
# 124). Only this outer watchdog moves: every runtime, assertion, sample and duration
# budget inside the collector is unchanged, so a hang still dies, just not a slow run.
timeout 180s node packages/runtime-native/scripts/profile-production.mjs \
# 300 s, not 180 (PR #574): hosted runs took 103 s and 138 s, then two runs of one
# unchanged tree were killed at exactly 180 s while their build steps ran no slower; four
# local replays on 4 cores took 68-83 s with no stall. The collector now prints a
# TN_PROD_PHASE line per phase into collector.log, so a killed run names its phase.
timeout 300s node packages/runtime-native/scripts/profile-production.mjs \
--profile production --target desktop --duration 1 --cold-starts 1 --repetitions 1 --warmup 1 \
--hosted-software \
--prebuilt-artifact "$GITHUB_WORKSPACE/packages/runtime-native/build/tn-linux/mystral" \
Expand Down Expand Up @@ -925,6 +929,8 @@ jobs:
# in, or this lane would quietly stop covering 21 scenarios it has always covered.
extras="$GITHUB_WORKSPACE/packages/create-threenative/template-playtests/${{ matrix.template }}"
if [ -d "$extras" ]; then cp -r "$extras/." "$target/playtests/"; fi
# PRD-748: the engine-cost contract row runs on every PR, for every kit that does not opt out.
pnpm exec tsx scripts/kit-contract-row.ts engine-cost "${{ matrix.template }}" "$target/playtests"
scenario_list=$(node "$GITHUB_WORKSPACE/scripts/non-visual-scenarios.mjs" "$target")
mapfile -t scenarios <<< "$scenario_list"
test "${#scenarios[@]}" -gt 0
Expand Down Expand Up @@ -1033,7 +1039,7 @@ jobs:
- uses: ./.github/actions/playwright-chromium
# `test:browser` is the test-browser job's gate; running it again here bought nothing but
# a second copy of the slowest step in the workflow.
- run: pnpm exec playwright test --config=benchmark.playwright.config.ts
- run: sh scripts/xvfb.sh pnpm exec playwright test --config=benchmark.playwright.config.ts

# visuals is NOT a job here any more. It captures frames from a WebGPU canvas, and this runner
# has no GPU — its first execution in the repository's history failed on
Expand Down Expand Up @@ -1376,7 +1382,12 @@ jobs:
# git history — gitleaks' default git mode — which is strictly stronger than the incremental
# PR range and immune to force-push SHAs the checkout may not have.
runs-on: ubuntu-latest
timeout-minutes: 10
# The full-history scan pays a 3.9 GB clone: on a hosted runner it has taken 5m59s
# (merge-group run 40715886891, success) and over 10 minutes (run 40754093470, killed by
# this budget with actions/checkout still fetching, which then failed the merge queue's
# fail-closed receipt verifier). Two runs, same tree cost, opposite verdicts: the budget
# was under the work, so it gets headroom rather than the scan losing its full history.
timeout-minutes: 20
permissions:
contents: read
env:
Expand All @@ -1386,6 +1397,10 @@ jobs:
with:
ref: ${{ needs.scope.outputs.candidate_sha || github.sha }}
fetch-depth: 0
# A PR scans only its own commits, so it fetches commits and trees and then just the blobs
# its range reads (below): every blob of the 3.9 GB history outlasted this job's 10 minutes
# (PR #574, run 40745026751). Pushes and merge groups scan the full history and keep them.
filter: ${{ github.event_name == 'pull_request' && 'blob:none' || '' }}
- name: Verify the PR commit range is present
if: github.event_name == 'pull_request'
env:
Expand All @@ -1395,6 +1410,20 @@ jobs:
set -euo pipefail
range="$BASE_SHA..$HEAD_SHA"
test "$(git rev-list --count "$range")" -gt 0
# The checkout fetched no blobs. Fetch every blob the range's commits read in one request,
# and fail when one is still missing: gitleaks exits 0 after scanning 0 commits when it
# cannot read one.
blobs="$RUNNER_TEMP/gitleaks-range-blobs"
git rev-list --no-merges "$range" | git diff-tree --stdin -r --no-commit-id --raw \
| awk '$1 != ":160000" && $2 != "160000" { print $3; print $4 }' | grep -v '^0*$' | sort -u > "$blobs"
if [ -s "$blobs" ]; then
git -c fetch.negotiationAlgorithm=noop fetch --quiet --no-tags --no-write-fetch-head \
--recurse-submodules=no --filter=blob:none --stdin origin < "$blobs"
fi
if GIT_NO_LAZY_FETCH=1 git cat-file --batch-check < "$blobs" | grep ' missing$'; then
echo "::error::the PR range's blobs are missing, so gitleaks would scan nothing"
exit 1
fi
echo "TN_GITLEAKS_RANGE=$range" >> "$GITHUB_ENV"
- name: Scan PR commits for leaked secrets
if: github.event_name == 'pull_request'
Expand Down Expand Up @@ -1475,6 +1504,9 @@ jobs:
- uses: actions/checkout@v7
with:
fetch-depth: 0
# As in scope: this job reads commits and a few workflow files (`git show`, fetched on
# demand), so it fetches no history blobs and fits its 5 minutes on a loaded runner.
filter: blob:none
ref: ${{ needs.scope.outputs.candidate_sha || github.sha }}
- name: Reject every unsuccessful or missing selected job
env:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
# Integration scope reads commits, a name-only diff and two workflow files (`git show`,
# fetched on demand from the public repository), so it fetches no history blobs: every
# blob of the 3.9 GB history outlasted 5- and 10-minute jobs on PR #574.
filter: blob:none
ref: ${{ inputs.candidate_sha }}
- id: filter
env:
Expand Down Expand Up @@ -1090,6 +1094,8 @@ jobs:
persist-credentials: false
ref: ${{ inputs.candidate_sha }}
fetch-depth: 0
# The receipt collector reads artifacts and the Actions API, never git history blobs.
filter: blob:none
- uses: actions/download-artifact@v7
if: needs.paths.outputs.required_jobs != '[]'
with:
Expand Down
Loading
Loading