Skip to content

Starter kits: Unreal look, shooter revamp, performance by construction, kit contract and worker mode (PRD-723/746/747/748/749/752) - #574

Merged
jonit-dev merged 317 commits into
developfrom
pr563-followup
Oct 28, 2026
Merged

jonit-dev merged 317 commits into
developfrom
pr563-followup

Conversation

@jonit-dev

@jonit-dev jonit-dev commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What is this PR about?

Starter kits that a cold agent can ship from: every kit on core's Unreal look (PRD-723), a revamped shooter (PRD-746), kit fixes found by play (PRD-747), performance the engine decides by default (PRD-748), one contract every kit must pass (PRD-749), and web games that run in a worker (PRD-752, folded from #576). It also folds #563's follow-ups, #575 and #578.

Why does it matter?

"Make me a game like X" starts from a kit. A kit that hides the player, ignores restart, hitches or draws 8000 meshes ships those bugs into every game built on it. These changes make the engine catch them, or fix them by default.

Done

  • ✅ Auto batching is on by default (PRD-748). Settled static meshes merge by material, and the sources come back when the game moves them. Shooter: 8307 meshes → 30 draws. Adventure: 51 → 9. collapseTown is deleted. The fresh visual judge read NEUTRAL on 4 of 4 sheets (pr574-screenshots, prd748-autobatch-judge/).
    • Core bug found and fixed: a shared stamp buffer kept a stale matrix tail, so a still parent read as moved and all 8307 sources came back (13838 draws, CPU p95 89 ms). Spec red → green.
  • ✅ The kit contract runs on all 16 kits (PRD-749). 8 rows: explore-clean, camera-sees-subject, hud-clean, no-hitch, foot-slip, restart-resets, action-clip-completes, effect-follows-contact. Each opt-out carries a reason. The rows found 12 real reds on 10 kits, and 11 are fixed:
    • The camera hid the player: the starter and platformer spring arms now pull in, the adventure arm also tests roots and boulders, the puzzle and action-rpg near walls are lower, and the observer now skips cutouts, effect volumes and ropes.
    • Restart did not reset: platformer had no R binding. Running clocks are bounded with lte.
    • The action-rpg swing cut its clip at 52%, and now plays it whole.
  • ✅ HUD contrast passes on all 16 kits. The audit composited the plates the wrong way round; the harness fix has a spec that goes red → green. Kit plates are darker.
  • ✅ The page UI stays behind the loading cover by default. Shooter: 68/91 → 0/59 page, 0/320 worker.
  • ✅ Shooter: vase shards draw again (0 → 9), and the hands play at their authored rate (2.83 → 1.00, core stride sync skips camera-carried rigs).
  • ✅ Kart: the hang is fixed (a per-step vertex walk), and the item key works.
  • ✅ Quality rules: custom ESLint rules in the base kit (Guard rails: page UI hides behind the loading cover, and a ThreeNative ESLint plugin (PRD-748) #578).
  • ✅ Worker mode (perf(web): PRD-752 run web games in a worker so the page cannot stall them #576, PRD-752): merged. The web Wasm is rebuilt from the merged C++ and its stamp passes.

Still open

  • ⚠️ Shooter performance draws: the steady state is about 150 draws (limit 320), but the first 1-3 measured frames draw 770-796 sources before the merged meshes show. The cause is an engine-layer reveal. The bound is not edited. PRD-746 box stays open.

  • ⚠️ Frame-time budgets under load: rain GPU p95 21.5 ms and shooter CPU p95 18.3 ms failed while the 16-kit sweep loaded the host. These need a quiet-host rerun.

  • ⚠️ Automatic instancing of repeated meshes is not built yet. The PRD-748 box stays open.

  • ⚠️ Platformer camera-sees-subject: 5 of 205 samples are blocked, against a limit of 2% (4 samples). The fox falls past non-solid rock faces. Three kit experiments read 6 to 12 blocked, so none shipped. The contract runs locally and weekly, not in PR CI.

Decisions to check

  • The explore-clean row judges the path walked, not the net distance, because a lost life respawns the player on the spawn point.
  • The action-rpg swing fits the 1.533 s clip into its 0.8 s hold, about 1.9× the clip rate, as the roll already does. The feel is not judged.
  • The puzzle near wall is a 0.8 m kerb, and the action-rpg south wall is 0.6 m, so the top-down camera sees over them. A visual judge checks these walls.

@jonit-dev jonit-dev added the prd:50% PRD half the phases verified label Oct 26, 2026
@jonit-dev

Copy link
Copy Markdown
Contributor Author

Exposure audit — progress sheets (PRD-747 Phase 1, not yet judged)

Columns: CURRENT (PR head, core Unreal look) | PRE-MIGRATION (kit look before PRD-723) | AFTER (lane-exposure work in progress: core black-histogram-bucket fix). Run 1 of 2 per arm.

Owner verdict on minimal: CURRENT is the best one, so minimal keeps CURRENT. Rule for every kit: CURRENT is the default to beat; a kit changes only when a fresh judge reads AFTER as IMPROVEMENT vs CURRENT.

Core matches stock UE 5.8.3 term by term (filmic curve, ExpandGamut 1, BlueCorrection 0.6, histogram auto exposure +1 EV, local exposure 0.8, bloom 0.675, vignette 0.4). One deviation: core counts the black histogram bucket, UE ignores it (r.EyeAdaptation.BlackHistogramBucketInfluence 0).

minimal-owner-pick-CURRENT.png

minimal-owner-pick-CURRENT.png

row-minimal-play-1.png

row-minimal-play-1.png

row-minimal-survives-1.png

row-minimal-survives-1.png

row-platformer-performance-1.png

row-platformer-performance-1.png

row-platformer-survives-1.png

row-platformer-survives-1.png

row-puzzle-performance-1.png

row-puzzle-performance-1.png

row-puzzle-survives-1.png

row-puzzle-survives-1.png

The apex was 7.2^2/38 = 1.36 m, so the hitbox's bottom (feet + 0.05 m) never
rose past a 1.5 m block's top: every jump over a block was a crash, and the
advertised space-to-jump did nothing useful. Take-off is now 8.5 m/s: a 1.9 m
apex, clear of the block for 0.44 s (4.8 m at the 11 m/s start speed).
Jump and hitbox constants are exported for the spec.

Red (__tests__/runner.spec.ts): clearance discriminant -3.26 (never clears).
Green: 1/1 passed.

PRD-747
The near-miss loop tested every lane of every chunk, not the obstacles in it:
a lane change on empty road, the first 48 m included, scored near misses and
shook the camera. Chunks now list their obstacles and the loop tests those
while the runner is level with one.

Red (playtests/near-miss.playtest.json): nearMisses 3 after three lane changes
in the first 44 m. Green: 0.

PRD-747
…-747)

render/camera.ts followed a fixed offset with no wall check, so with the
player's back to a wall the lens passed behind it and the wall filled the
frame (free play, frames 9-10). Layer: kit (src/render/camera.ts, framing
only; no colour or look value). followCamera now takes a WallProbe (the
collectathon pattern): a wall between the aim and the offset pulls the lens
in front of it at once, and the shortened arm climbs (the adventure kit's
rule) so the lens looks down on the player; it eases back out when clear.
Play.ts registers 'camera' with debug().hidden (a wall between player and
lens). Needs 62b225d: before it, the probe hit the player's own capsule.

Red-green: playtests/camera-wall.playtest.json (walk back into the south
wall; hidden equals false) red before: hidden false -> true; green after:
pass. Visual judge (fresh read-only Opus, 2 headed WebGPU runs per arm,
NVIDIA Turing, ~/Downloads/pr574-prd747-cam/): IMPROVEMENT overall; at-wall
and walk IMPROVEMENT, spawn and away NEUTRAL; note: the top-down pose at the
wall foreshortens the mannequin.
…(PRD-747)

Play.ts set statePatch.hudFaded = under, then counted
'under && !statePatch.hudFaded': always 0, so enemies.playtest.json's
'covered equals 0' asserted the line above it. Layer: kit (proof only, no
behaviour change). The count and its assertion go; 'under >= 1' still proves
a Sunseed sat under the pill, and Hud.tsx fades on state.hudFaded.

Check: playtests/enemies.playtest.json passes in the scaffold after the
change.
…ld where the run ended

Running out of wind was reported as 'capsized', and under the end panel the
elapsed clock kept counting and the wind kept draining. The HUD now reads the
passage's clock and wind as they stood when it ended; the sea keeps its own clock.

Red: fail.playtest elapsed 3.20 s two seconds after a capsize (bound <= 1.1);
float-after-the-run-ends outcome panel 'capsized' after the wind ran out.
Green: elapsed 1.00 s; outcome 'out of wind' (and 'capsized' after a capsize).

PRD-747
The menu advertised 'C capsize test', a developer hook that ends the run. The
binding stays for playtests/fail.playtest.json and is commented as such; the hint
now reads 'WASD / arrows steer and trim'. Text only, no behaviour change.

PRD-747
After V the crates were rebuilt with their rest pose set to the post-V pose, so
'shoved' and 'push metres' read 0 for a vault the player had already pushed;
replayBodies was read after the rig was cleared, so it was always 0.

Red (playtests/replay-keeps-progress.playtest.json): pushedCrates 0 after V
(1 before it), replayBodies 0. Green: pushedCrates 1, replayBodies 3.

PRD-747
… review

The Three patch ships in every scaffold, so its shared-skin history fix moves all sixteen trees;
with the previous patch bytes only action-rpg moved (checked). create-threenative tests: 116
files, 1836 tests pass.
…metry (PRD-746)

Five greyscale tiles generated through scripts/reference.mjs
(google/gemini-3.1-flash-lite-image, 512 px WebP) and three derived normal
maps, 313 KB in all, recorded in bay-PROVENANCE.md. src/render/surface.ts
takes only each tile's structure (sample over its own mean), so the 7b546d6
neutral palette keeps its colour; a turned large-scale copy breaks the repeat,
wall feet darken, roughness follows the texture and the fine detail fades
with distance. Town surfaces stay standard node materials, so the high-tier
backlight still converts them; the plain grid materials remain the fallback.
Fresh Opus judge vs the step-1 frames: IMPROVEMENT (9 of 11 poses, deck
undersides NEUTRAL).
@jonit-dev

Copy link
Copy Markdown
Contributor Author

Action RPG — owner-reported bugs, fixed red-green (PRD-747, lane-arpg)

Judge (fresh Opus, 2 runs per arm, nvidia/turing): ghost enemy IMPROVEMENT, scene NEUTRAL, HUD IMPROVEMENT, overall IMPROVEMENT.

# Bug Layer Fix Red → green
1 Enemy turns see-through (ghost) engine (three patch Skinning.js) each skinned mesh reads its own previous bones (b6041ab) skinned-velocity-bind-history.spec: 0.447 m error → 8/8
2-4 Slash out of sync, F-spam machine-gun hits, dead hero still kills kit hit at the blade's 0.40 s contact, one hit per swing, no input after death (fb5abd6) swing.playtest.json: 7 asserts red → green
5 WEAPON NONE with a sword in hand kit rusted blade equipped at start (f1912bb) inventory.playtest.json
6-8 Potion in two rows, dev text in HUD, blue-on-blue health kit (UI) one row per item, dev lines removed, readable number (36c6f93) inventory.playtest.json HUD rows

Still open, sent back to the lane: roll clip cut at 0.5 s of 1.47 s, leg/locomotion, the look restore (owner: same as the shooter), native-engine half of the ghost proof, cheat keys behind a dev flag, enemy punch timing.

ghost-sheet-crops.png

ghost-sheet-crops.png

ghost-sheet-full.png

ghost-sheet-full.png

… health bar

Symptom: after a cryo tower, walkers in later waves showed the cyan 'chilled' bar
while moving at full speed. Root cause: src/enemies/Enemy.ts reset() cleared the
slow but kept the bar tint a chilled death left behind. reset() now restores
barFull. Enemy.barTintMatches + battle.barTintMismatches report the invariant.

Red-green: playtests/chill-tint.playtest.json (new; cryo + sentry, auto waves at 3x,
30 samples), headed WebGPU nvidia/turing. Before: components FAIL, barTintMismatches
up to 8. After: exit 0, 36/36 samples 0 through wave 4.
…he fast path and fails closed (owner chose option 1)
Symptom: pressing A to arm attack-move slid the view left, so the follow-up
click landed on different ground. Root cause: src/game.ts bound KeyA to both
'move' (camera pan) and 'attack'. The pan now uses the arrow keys only, as the
letter keys are orders.

Red-green: playtests/rts-attack.playtest.json now asserts state.cameraX stays
-70 at box-release and armed. Headed WebGPU nvidia/turing. Before: FAIL
resource.state.cameraX.atSteps (-71.2 after A). After: cameraX -70 at every
sample; the run's only red is TN_SCENE_WARNING under host load 65 (unverified,
a host-load reading, not this change).
…age attribution, the allocation guard and explore-path bounds
…view

Symptom: every unit dot sat in the top-left 28 px of the tactical overview,
and the view box was a fixed-size square that did not follow zoom or the
isometric angle. Root cause: src/ui/Minimap.tsx drew minimapDots (tenths of a
cell) as raw pixels, and derived the view from cameraX/Z/zoom with the wrong
scale. Play.ts now publishes minimapView, the ground under the frame's four
corners in the same units, and Minimap draws dots and that quad scaled by cell.

Red-green: playtests/rts-build.playtest.json adds a visual region over the
fogged top-left corner of the map. Headed WebGPU nvidia/turing. Before: FAIL
visual.0.region.darkPixels 0.910 < 0.98. After: exit 0, ratio 1.0.
…00 lines (PRD-747)

looks.spec.ts caps each generated render file under 200 lines; 650e607 took
render/fox.ts to 205. The legSwing getter leaves IFox: the left leg group is
named 'legL' and Fox caches it for standingSwing (NaN, so the idle assertion
fails closed, if the name goes). No behaviour change.

Checks: looks.spec.ts 21/21 pass; playtests/idle.playtest.json passes in the
scaffold; scaffold tsc, eslint and biome clean.
… (PRD-747)

0a6ea30 made the WallProbe a required followCamera argument;
template-runtime-cost.spec.ts called it with three. The sentinel now runs
both arms (clear way, and a wall at 1 m that pulls in and climbs) and still
expects zero vector clones or constructors.

Check: template-runtime-cost.spec.ts 7/7 pass.
…sics telemetry

The top-left panel showed developer telemetry to players: seed, solid and phase
crate counts, at rest, push metres, blocked ticks, walked through, seal contacts,
and replay bodies/drift in exponential notation. It now shows the title and
'crates shoved', plus 'replay matched/drifted' after V. The telemetry stays in
the published state the scenarios read.

Visual judge (fresh Opus, headed WebGPU nvidia/turing, 2 runs per arm):
IMPROVEMENT overall; readability NEUTRAL, rest of frame NEUTRAL (no regression).
Frames: ~/Downloads/pr574-prd747-puzzle-hud/.

PRD-747
…fixes

Recomputed 2026-10-26 (PRD-747 kits): collectathon, minimal and platformer
gain play fixes and playtests; values measured by scaffold.spec.ts itself.

Checks: packages/create-threenative/__tests__ 116 files / 1836 tests pass;
scripts/check-core-boundary.ts passes.
Symptom: the mouse wheel zoomed the opposite way to the other kits and to the
genre: wheel up pulled the camera out. Root cause: src/scenes/Play.ts passed
the zoom axis (positive for a wheel rolled away from the user) straight to
zoomRts, whose notches grow the view height. The call now negates it.

Red-green: playtests/rts-zoom.playtest.json (new). Headed WebGPU nvidia/turing.
Before: FAIL resource.state.cameraZoom.atSteps, closer = 32.4 (expected 27.6).
After: exit 0, 30 -> 27.6 -> 30.
@jonit-dev

Copy link
Copy Markdown
Contributor Author

Shooter — owner's 7b546d6 look restored, then Unreal-grade polish (PRD-746 Phase 3, lane-look)

Ready to test at the local dev server (test/pr574-integration build, includes every lane fix merged so far).

Step Commit Judge (fresh Opus, 2 runs per arm, nvidia/turing)
Restore the 7b546d6 neutral look as named overrides (owner: "i actually prefer this version") f97df77 5 poses MATCH; enemy-reaches-walkway DIFFERENT only on the deck underside (the kept deckWood albedo fix)
Polish: 5 generated tiling surfaces + 3 derived normal maps (8 images, 313 KB WebP, google/gemini-3.1-flash-lite-image, provenance in bay-PROVENANCE.md), texture structure over the palette colour, anti-tiling macro variation, darker wall feet, roughness from texture, detail fade with distance 07e8fe0 P6 IMPROVEMENT: 9 poses IMPROVEMENT, 2 NEUTRAL (under-deck, walkway)
reference.mjs: keeps model-id slashes, reads the live /endpoints shape, names a JPEG-in-.png mismatch efeb406, ce43482 red-green: 2 new tests red on HEAD, 7/7 green

Tests: create-threenative 1838/1838, biome clean, core boundary passes, shooter test:templates 33/34 (only maxFrameMsP95 20.9 ms at load average 40, Xvfb, unverified; draws 106 of 320).

⚠️ The BEFORE/AFTER sheets were lost: the lane kept them in ~/Downloads, which was wiped mid-run. New captures come with the next shooter round.
Open: deck-underside wood reads NEUTRAL, not improved; walls now report bullet hits as "plaster", not "stone" (no playtest covers it); image generation cost about $0.23.

The stall rescue counted any car under 0.6 m/s for 1.2 s, with or without
throttle. A player who braked to a stop on the road was rescued every 1.2 s,
and the fourth rescue is a DNF: five seconds parked ended the race (found in
free play: DNF at 00:06.2). A stall now counts only while the engine pushes,
which is the pinned-against-a-hoarding case the rescue exists for.

Red (playtests/stopped-car-is-not-rescued.playtest.json): rescues 4,
raceStatus DNF after a 7 s stop. Green: rescues 0, RACING; rescue, outcome
and reverse scenarios still pass. Unit (racing.spec.ts): parked car
shouldRescue true -> false; driven stuck car still true.

PRD-747
@jonit-dev

Copy link
Copy Markdown
Contributor Author

Exposure — CURRENT | PRE-MIGRATION | AFTER (black-bucket fix) | CANDIDATE 0 EV (PRD-747 Phase 1, lane-exposure)

Core matches stock UE 5.8.3 term for term. The dull look comes from Unreal's own +1 EV default (key 0.36 = 0.18 × 2^1, r.DefaultFeature.AutoExposure.Bias 1.0, UE 5.8.3: Engine/Source/Runtime/Engine/Private/SceneView.cpp:196-199).

  • AFTER (core ignores the black histogram bucket like UE, 4a8cb1c, red-green 13/13): NEUTRAL vs CURRENT on all 14 kits.
  • CANDIDATE 0 EV (key 0.18): judge IMPROVEMENT vs CURRENT on 9 of 9 kits tried, no REGRESSION vs PRE-MIGRATION. Median sRGB luminance drops from ~158 to ~116 (18% grey ≈ 118). Not committed: it deviates from Unreal, owner decision.
  • Owner already picked minimal CURRENT as the best; the judge prefers 0 EV there. Run 1 of 2 shown.

adventure-run1.png

adventure-run1.png

collectathon-run1.png

collectathon-run1.png

kart-run1.png

kart-run1.png

minimal-run1.png

minimal-run1.png

platformer-run1.png

platformer-run1.png

puzzle-run1.png

puzzle-run1.png

racing-run1.png

racing-run1.png

rain-run1.png

rain-run1.png

rts-run1.png

rts-run1.png

runner-run1.png

runner-run1.png

sailing-run1.png

sailing-run1.png

snow-run1.png

snow-run1.png

starter-run1.png

starter-run1.png

tower-defense-run1.png

tower-defense-run1.png

Symptom: at the end of a match the DEFEAT/VICTORY tag sat under the command
column's hint bar (bottom right), half hidden, and nothing could start a new
match short of reloading the page. Root cause: src/ui/Hud.tsx put the result
in the pause button's corner stack, and no intent restarted the game. The
result is now a centred panel with a 'play again' button; src/game.ts maps
the 'restart' intent to game.goto('play'), which resets state and the scene.

Proof: out-of-tree five-minute scenario (box select, gather, train, wheel,
arrow pan, attack-move, pause, all army, base; 17,000 ticks) until the AI wins,
then the button. Headed WebGPU nvidia/turing. Before: DEFEAT clipped under the
hint bar, no restart control. After: exit 0; result 'defeat' -> '', supplyUsed
0 -> 23, clock 04:01 -> 00:03. Not added to playtests/: it is 4x the longest
kit scenario.
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 28, 2026
jonit-dev and others added 2 commits October 28, 2026 10:38
Change scope cancelled three times on PR #574 (merge queue attempt 3, PR run 40727651261, merge queue attempt 7) with actions/checkout still fetching every branch of the 3.9 GB repository when the job's 5-minute timeout hit, on loaded tn-local runners.

Scope reads commits and trees (rev-parse, cat-file, rev-list) and fetches missing commits itself, so the checkout takes filter: blob:none. Measured locally: a blob-less fetch of all 532 branches takes 8 s and 20 MB with the full 2263-commit develop history, and ci-change-scope.mjs prints a byte-identical plan for the PR merge commit 952193d from the blob-less clone and from a full clone (scope=review).

Checks: the 29 workflow specs 804/804.
… (PRD-750) (#580)

* feat(core): every default MCP server reaches an agent that scaffolded mid-session (PRD-750)

A host loads .mcp.json only for a session started in the game folder; five cold
builds scaffolded from their session folder and made zero engine calls (a probe
that wrote .mcp.json mid-session got NO_TOOL). mcp/call.mjs starts any server in
the game's .mcp.json the way the host does and lists or calls its tools. The
scaffold's last line names it and the servers; the --genre digest prints the top
three references' pinned sources and constants. Restores the referenceDigest
assertions that the f71d036 base merge dropped.

Co-Authored-By: Claude <[email protected]>

* feat(create-threenative): the files a cold agent reads name the MCP bridge (PRD-750)

Cold build 8 scaffolded mid-session, saw only a 2 KB preview of the 47 KB scaffold
output, read CLAUDE.md and the threenative-capabilities skill, had no
mcp__threenative-* tool and called none. Both now say how to reach every server
from a session started elsewhere: node node_modules/@threenative/core/mcp/call.mjs
<server> <tool> '<json>'. template.spec locks the line in the skill and all 16
AGENTS.md; the 16 template hashes are re-pinned.

Co-Authored-By: Claude <[email protected]>

* docs(prd-750): record the MCP bridge proof (cold builds 8-10)

Co-Authored-By: Claude <[email protected]>

* fix(skills): the performance skill's harness-gap line fits its 259-word budget; kit hashes re-pinned

CI run 40590048080 test-unit (2/4): template.spec expected 308 words to be under 260.

* feat(collectathon): the third Sunseed clears the course (PRD-750 row 3 retro)

Row 3's cold agent found no win state: the third Sunseed showed only a toast,
so it added its own. The kit now publishes `won` when all three Sunseeds are
held, shows COURSE CLEAR with the run's coins and gems, and emits a `won`
playtest signal. playtests/win.playtest.json places the eight gems and both
Sunseeds on one line and waits on `won`: red before this change
(TN_PLAYTEST_OBSERVATION_UNAVAILABLE, exit 2), green after.

* docs(collectathon): the kit AGENTS.md drops what the installed references already say (PRD-750 row 3 retro)

22 KB read twice is what row 3 paid: the assets cook paragraph described a
cook a kit with no authored asset never runs, the recipe list repeated
eighteen paths the scaffold installs, and one paragraph taught a CI
collector profile. 22,455 B -> 20,103 B, no engine rule lost.

* docs(prd-732): the eleven SM64 cards, their pinned sources and the kit constant each one became

* chore(create-threenative): the collectathon mirror and the sixteen kit hashes follow the trimmed AGENTS.md

* docs(prd-750/732): the row-1 author step's three pieces, and what the box still waits on

* fix(collectathon): the win banner is the one win surface, not a banner plus a duplicate toast

* chore(create-threenative): re-pin the collectathon tree hash for the single win surface

* docs(prd-750): row 4 recorded with the fixes it names, the plateau it does not yet meet, and K2's measured gap

* test(create-threenative): re-pin the sixteen scaffold tree hashes on the merge of PR 574 into PRD-750

---------

Co-authored-by: Claude <[email protected]>
After the scope fix (1af3023) passed, ci-required on PR #574 head 8e6b63c was cancelled the same way: actions/checkout still fetching every branch of the 3.9 GB repository when the job's 5-minute timeout hit (run 40738363046).

ci-required and its modules read commits (rev-parse, merge-base) and a few workflow files through git show, which a blob-less clone fetches on demand (570 ms for one file locally). supply-chain keeps its full fetch: gitleaks scans file history.

Checks: the 29 workflow specs 804/804.
jonit-dev added a commit that referenced this pull request Oct 28, 2026
Change scope cancelled three times on PR #574 (merge queue attempt 3, PR run 40727651261, merge queue attempt 7) with actions/checkout still fetching every branch of the 3.9 GB repository when the job's 5-minute timeout hit, on loaded tn-local runners.

Scope reads commits and trees (rev-parse, cat-file, rev-list) and fetches missing commits itself, so the checkout takes filter: blob:none. Measured locally: a blob-less fetch of all 532 branches takes 8 s and 20 MB with the full 2263-commit develop history, and ci-change-scope.mjs prints a byte-identical plan for the PR merge commit 952193d from the blob-less clone and from a full clone (scope=review).

Checks: the 29 workflow specs 804/804.
jonit-dev added a commit that referenced this pull request Oct 28, 2026
…rates servers, and derive the collector's package flags from the shared rule (#585)

* Wire the animation MCP into the release, registry, golden-path and doctor contracts it joined (PRD-760)

PRD-760 made threenative-animation a default server in every scaffold, but the places that enumerate the servers still listed four.

- doctor: the server resolves to @threenative/core like engine and blender, so its pin is core's version, not an unpublished dependency.
- golden path and registry install: add the server's recorded surface (animation-mcp-tools.json, captured by scripts/capture-blender-mcp-tools.ts animation and by prepare-release) and a safe animation_inspect probe on an owned empty GLB.
- release spec: the bundled server list names the fifth server.
- package externals: threenative-animation-mcp is a self-contained stdio tool (core commits its bundle, its page loads one browser file), so it is excused from the shared-runtime gate, and the excuse requires a bin.
- quality: waiver reason on the v2/v3 recipe cast in upgradeLog.

* fix(native): the production collector derives every workspace package flag from the shared rule

A develop push builds threenative-animation-mcp, so the collector passed it to
the scaffolder and threw TN_PROD_PACKAGE_FLAG_UNSUPPORTED: its copy of the flag
rule named the two MCP packages by hand. macOS desktop core, Windows desktop
core, desktop parity and test-native all died there.

It now applies scripts/workspace-packages.ts workspacePackageSourceFlag's rule
(scoped packages derive their flag, an unscoped threenative-* package drops its
prefix), and profile-production.spec.ts holds the copy equal to the original for
every workspace package, so the next package cannot drift it again.

Verified: profile-production.spec.ts 11 passed; the case fails with the old
rule restored.

* fix(ci): the change-scope checkout fetches commits and trees, no blobs

Change scope cancelled three times on PR #574 (merge queue attempt 3, PR run 40727651261, merge queue attempt 7) with actions/checkout still fetching every branch of the 3.9 GB repository when the job's 5-minute timeout hit, on loaded tn-local runners.

Scope reads commits and trees (rev-parse, cat-file, rev-list) and fetches missing commits itself, so the checkout takes filter: blob:none. Measured locally: a blob-less fetch of all 532 branches takes 8 s and 20 MB with the full 2263-commit develop history, and ci-change-scope.mjs prints a byte-identical plan for the PR merge commit 952193d from the blob-less clone and from a full clone (scope=review).

Checks: the 29 workflow specs 804/804.
jonit-dev added a commit that referenced this pull request Oct 28, 2026
…ack under its 4 MiB budget (PRD-754)

The shooter's web camera turn recorded no less with the stable passes on
(rec+shadow 0.35-0.41 ms off vs 0.35-0.58 ms on, 3+3 probes, NVIDIA Turing),
and the lane put the web engine 74 KB over LIMITS.webEngineBytes. So on
Emscripten stablePasses_ and stableRows_ are constexpr false: no shadow
bundles, uniform slots, batch slot map or rows, and no recordCause order and
offset hashes (the web reads them as 4, other). Web host ABI goes back to
#574's 5; the ?tnStablePasses knob and the worker page-query copy are gone.
Native keeps the shadow bundles and slots on by default and the rows behind
setStableRows.

Also: setStableRows off no longer dereferences rows it never allocated.

Wasm: threads 4,268,326 -> 4,193,163, web 4,258,924 -> 4,183,616 (limit
4,194,304). pnpm budgets 0, typecheck 0, lint 0, three-native + core worker
specs 476/476, related native ctest 88/88, full ctest 561/566 (the same 5
reds as before: 4 pre-lane, player_gpu_stages from #574's sun cache).
Root cause of four cancels on PR #574: actions/checkout fetch-depth 0 downloads every blob of the 3.9 GB repository (532 branches) inside 5- and 10-minute jobs. Hosted supply-chain checkouts took 41-571 s across recent runs, and PR run 40745026751 hit its 600 s limit.

- supply-chain, pull_request only: the checkout fetches commits and trees, then the step fetches the 1573 blobs its PR range reads in one request (2 s locally) and fails when one is still missing. That guard matters: gitleaks exits 0 after scanning 0 commits when a blob is missing. Red-green: with the fetch skipped the step exits 1; with it, gitleaks scans 291 commits with --network none, the same 291 commits and no leaks as on the full repository. Pushes and merge groups keep the full fetch for the full-history scan.
- Integration paths and completion: filter: blob:none. ci-integration-scope.mjs prints byte-identical output from a blob-less clone and a full clone; the receipt collector reads no git history.

Checks: the 29 workflow specs 804/804.
# Conflicts:
#	packages/runtime-native/__tests__/profile-production.spec.ts
#	scripts/capture-blender-mcp-tools.ts
@jonit-dev
jonit-dev added this pull request to the merge queue Oct 28, 2026
@jonit-dev
jonit-dev removed this pull request from the merge queue due to a manual request Oct 28, 2026
…r kept (PRD-754 option 1) (#584)

Owner option 1 (2026-10-28): three's opaque order stays; native desktop replays the shadow passes from bundles with persistent uniform slots, on by default (Midway recording plus shadow CPU -18%, frame p50 and p95 no worse); main/aux/velocity rows behind Renderer::setStableRows (native, off). The web engine compiles the stable passes out (no web gain; back under its 4 MiB budget, web host ABI 5). A batch keeps its member's id, so three's coplanar winner holds.

Proof (local; ci.yml does not run for this base): typecheck 0, lint 0, pnpm budgets 0, three-native + core worker specs 476/476, native ctest 562/567 (the 5 reds predate the lane or come from #574), fresh judge NEUTRAL on Midway native and the shooter web, door stripes gone. PRD-754 stays open: the camera-turn goal and the 80% CPU gate are Deferred.
@jonit-dev
jonit-dev enabled auto-merge October 28, 2026 19:30
@github-actions

Copy link
Copy Markdown
Contributor

The merge queue stopped on integration / Verify required integration receipts (failure).

Fix, push, re-enqueue.

…clone needs

The merge queue stopped on `integration / Verify required integration receipts`
(run 40754093470): supply-chain was cancelled at 10m05s with actions/checkout
still fetching, so the fail-closed receipt verifier found no receipt.

It is the same job and the same full-history scan that took 5m59s two hours
earlier on merge-group run 40715886891. Nothing hangs; the 10-minute budget sits
inside the spread of a hosted runner cloning 3.9 GB.

20 minutes, not a weaker scan: a merge group is the last gate before develop, so
it keeps gitleaks' full git history.

Checked: scripts/__tests__/ci-structure.spec.ts and ci-needs.spec.ts 234 passed.
@jonit-dev
jonit-dev added this pull request to the merge queue Oct 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 28, 2026
Conflicts: port-history.json and unreal-port-progress.svg are generated; took
develop's copies and regenerated with pnpm unreal:status (up to date).
Develop adds docs only (PRD-618/628/761 plans, AGENTS short-CI rule).
@jonit-dev
jonit-dev enabled auto-merge October 28, 2026 22:13
@jonit-dev
jonit-dev merged commit c93b427 into develop Oct 28, 2026
33 of 34 checks passed
jonit-dev added a commit that referenced this pull request Oct 28, 2026
Conflicts kept both sides: ci.yml (xvfb-wrapped benchmark run + Strata startup
steps), frame-budget (drawn-interval tracking + phase sample on hitch), game.ts
(autoBatch.update before startup-shadow registration), renderer.ts (both helper
sets), mcp-install.spec (shared helper + #574 expectations). Three patch: #574's
Skinning.js shared skin history carried into source-delta.patch, 2 compat pairs
for develop-shipped builds, patches regenerated (--check matched). Generated
files regenerated: lockfile, api-surface, scaffold hashes.
Checks: build 0, typecheck 0, lint 0, vitest 14645 passed; template.spec
pruning passes with a Wasm engine present.
jonit-dev added a commit that referenced this pull request Oct 28, 2026
#574 and #583 heads were merged here first; develop's squash of #574 has the
exact tree of its head 554e458, so every conflict keeps this side. Guard:
outside the files the Strata branch changes, the result equals develop.
jonit-dev added a commit that referenced this pull request Oct 28, 2026
…583) merge

pnpm --filter @threenative/runtime-native native:coverage exit 0 on 81bf656:
81.90% total (develop 81.89%). native_engine_pacing failed once at load 26-32
and passed 3/3 alone; the re-run measured clean.
jonit-dev added a commit that referenced this pull request Oct 31, 2026
Since the starter runs game.ts in a web worker (index.html, #574), the
worker's location.search is empty: every harness capture drew the default
L9 sun60 tuned cell whatever tnRef, sun or baseline it asked for, and the
--baseline arm failed with TN_VIRTUAL_SHADOW_INVALID (NaN bias from the
tuned branch). main.html is the template's named page-thread override.

(cherry picked from commit 525418b)
jonit-dev added a commit that referenced this pull request Nov 9, 2026
…ers in one call (PRD-865)

The index read only `situations` and `aliases`, so an exact symbol name answered
`verdict: none`: `godrays`, `traa` and `mergeParts` are exported by the manifest
and were unfindable, while `engine_capability_detail` cost a call per hit for a
row that lacked only `signature`, `overrides` and `deprecated`.

- The tokenizer splits camelCase, and a request that spells a name as a run of
  whole words leads with that symbol: whole-name equality is case- and
  separator-insensitive (`mergeparts`, `characterbody3d`), a node name inside a
  sentence counts (`GPUParticles3D sparks`, `Area3D trigger`), and the lead
  scales with how much of the request the name accounts for, with a longer name
  winning a tie. A name mentioned in passing cannot outrank the mechanic the
  request is about.
- Names carry their own index (digits stripped, `GPUParticles3D` reads as
  `gpu particles`) and the summary is scored over it at low weight, which is
  what makes a thinly described entry like `traa` findable. Keeping it apart
  from the prose index is deliberate: folding it in cost `SnowField` its row in
  the recall budget.
- A row equals the detail minus `situations`/`aliases`. `AGENTS.md`,
  `agent-docs/kit-router.md`, the shared `threenative-capabilities` skill and
  all sixteen kit `AGENTS.md`/`CLAUDE.md` now say read every row and call the
  detail only for a symbol the search did not return.
- MCP text is compact JSON; indentation cost 11.8% of every response and the
  shell command re-indents its own copy.
- A frozen gold set and a two-arm A/B: 150 real queries from 26
  `.threenative/engine-mcp.log` files, 100 dev / 50 held out, held-out sha256 in
  the fixture and the PRD, 20 exact-symbol. Labels came from a fresh opus
  process that read only the logs and the manifest. `search-gold.spec.ts` locks
  the held-out floor; `scripts/capability-search-ab.ts` extracts the base source
  with `git archive`, measures both arms and runs a live `claude -p` arm.

Against ecd2a1a on the frozen held-out half: recall@5 0.580 -> 0.780,
recall@1 0.400 -> 0.620, 0 rows lost from the top 5, modelled tokens -47%.
Live: MCP calls -56..62%, MCP response bytes -30..36%, correct-symbol rate
unchanged, 0 failed runs.

Two independent opus reviews; every finding fixed, including the sixteen
scaffold trees re-measured on the merged tree.

Also lands the develop red this PR was queued behind: the platformer kit gets
the worker entry pair the starter shipped in #574 (`index.html` ->
`src/worker-page.ts`, `main.html` for the page-thread arm, `src/worker.ts`, and
the two-entry build). Its golden-path leg boots `index.html` and asserts threads
on, so without the worker page the scaffolded game never booted and the leg
reported `TN_PLAYTEST_BRIDGE_MISSING`. The platformer's game code is worker-safe
(`render/loading.ts` guards its canvas behind `typeof document`).
jonit-dev added a commit that referenced this pull request Nov 9, 2026
…ers in one call (PRD-865) (#644)

The index read only `situations` and `aliases`, so an exact symbol name answered
`verdict: none`: `godrays`, `traa` and `mergeParts` are exported by the manifest
and were unfindable, while `engine_capability_detail` cost a call per hit for a
row that lacked only `signature`, `overrides` and `deprecated`.

- The tokenizer splits camelCase, and a request that spells a name as a run of
  whole words leads with that symbol: whole-name equality is case- and
  separator-insensitive (`mergeparts`, `characterbody3d`), a node name inside a
  sentence counts (`GPUParticles3D sparks`, `Area3D trigger`), and the lead
  scales with how much of the request the name accounts for, with a longer name
  winning a tie. A name mentioned in passing cannot outrank the mechanic the
  request is about.
- Names carry their own index (digits stripped, `GPUParticles3D` reads as
  `gpu particles`) and the summary is scored over it at low weight, which is
  what makes a thinly described entry like `traa` findable. Keeping it apart
  from the prose index is deliberate: folding it in cost `SnowField` its row in
  the recall budget.
- A row equals the detail minus `situations`/`aliases`. `AGENTS.md`,
  `agent-docs/kit-router.md`, the shared `threenative-capabilities` skill and
  all sixteen kit `AGENTS.md`/`CLAUDE.md` now say read every row and call the
  detail only for a symbol the search did not return.
- MCP text is compact JSON; indentation cost 11.8% of every response and the
  shell command re-indents its own copy.
- A frozen gold set and a two-arm A/B: 150 real queries from 26
  `.threenative/engine-mcp.log` files, 100 dev / 50 held out, held-out sha256 in
  the fixture and the PRD, 20 exact-symbol. Labels came from a fresh opus
  process that read only the logs and the manifest. `search-gold.spec.ts` locks
  the held-out floor; `scripts/capability-search-ab.ts` extracts the base source
  with `git archive`, measures both arms and runs a live `claude -p` arm.

Against ecd2a1a on the frozen held-out half: recall@5 0.580 -> 0.780,
recall@1 0.400 -> 0.620, 0 rows lost from the top 5, modelled tokens -47%.
Live: MCP calls -56..62%, MCP response bytes -30..36%, correct-symbol rate
unchanged, 0 failed runs.

Two independent opus reviews; every finding fixed, including the sixteen
scaffold trees re-measured on the merged tree.

Also lands the develop red this PR was queued behind: the platformer kit gets
the worker entry pair the starter shipped in #574 (`index.html` ->
`src/worker-page.ts`, `main.html` for the page-thread arm, `src/worker.ts`, and
the two-entry build). Its golden-path leg boots `index.html` and asserts threads
on, so without the worker page the scaffolded game never booted and the leg
reported `TN_PLAYTEST_BRIDGE_MISSING`. The platformer's game code is worker-safe
(`render/loading.ts` guards its canvas behind `typeof document`).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prd:25% PRD phase 1 verified

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant