Commit graph

2,140 commits

Author SHA1 Message Date
Admin
c042c06eba Arcade M5: multi-Claude co-editing — intent log, semantic rebase, soft leases
libs/game/coedit (zero deps, so merge logic is testable without a socket or
a VM). Not a CRDT: transactions are host-serialized into an append-only
generation history, and a conflict is answered by handing the author the new
base so THEY re-derive their intent.

- Transaction carries the author's whole intended file, not a patch: the
  diff against its declared base is derived host-side, so a stale or
  malformed patch can never be applied — and an AI writes whole files anyway
- diff3 over lines with LCS anchors. Merge::Conflict deliberately carries NO
  merged text: a half-merged game file that still parses is worse than an
  honest rejection. Conflict -> Rebase{new base + per-generation summary of
  what landed underneath}. An edit already present in the tip is refused as
  NoChange rather than appended as an empty generation
- Leases are advisory as designed: a test asserts a submit SUCCEEDS while
  another author holds the lease. They shape who chooses to edit; they never
  gate the log. TTL expiry means a crashed author cannot lock a region
- Wire: coedit is reliable-channel ONLY — a test signs a valid submission,
  sends it by datagram, and asserts it is ignored, so no datagram can
  rewrite the game. Every response is addressed, never broadcast
- Arcade bridge routes in exactly one place; local agent and remote authors
  share the queue and the rules, and a test asserts the local agent gets
  rebased identically when it loses a race. Remote players map to
  AuthorId(player+1) so a client holding player id 0 cannot impersonate the
  host's agent
- 200-round deterministic fuzz: 4 authors submit against deliberately stale
  bases, rebase, resubmit — asserting linear append-only numbering, no
  generation claiming a base from the future, and that replaying accepted
  diffs from generation 0 reproduces the head exactly

Two real bugs found building it: validate used `?` on the base lookup, so an
unknown base returned "no refusal" instead of UnknownBase; and
MAX_COEDIT_SOURCE (512 KiB) exceeded MAX_FRAME_BYTES (256 KiB), so the host
would have accepted a source it could never hand back inside a Rebase,
stranding the next author on an answer that never arrives. Both now 192 KiB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:45:31 +02:00
Admin
5817c6b0de Migrate gamemaker onto game_script: -2647 lines, tape still byte-identical
game_view.rs 4525 -> 1878. Deleted in one marker-verified cut: the 84-arm
game_dispatch chain, all arg/option/value helpers, spawn_entity/spawn_terrain
/spawn_car/spawn_plane/spawn_character/spawn_block_body/set_brain, the
GAME_API table, suggest_verb/edit_distance, and the duplicated CallbackTable.
register_game_handle now binds game_script's verb table; unknown verbs keep
the same hard-fail plus did-you-mean. Kept: run_tick and everything parity
depends on — tape input, gamepad poll, camera mailbox, perf channels, agent
RPC, save/log flush.

Rollback deliberately still gamemaker's hand-written WorldSnapshot, not
GameWorld::clone(). Clone is strictly safer and is what Arcade uses, but
eval_body is the parity-critical path and the snapshot is what the tape was
established against — swapping it deserves its own tape run, not a rider on
a 2700-line deletion.

Three pre-existing divergences fixed in game_script (sfx/beep/jingle predated
the 31-verb port and had drifted): beep's `to` defaulted to 0.0 instead of
freq, so every beep swept to silence; beep never read its wave option at all
(allowed but unused, and AudioRequest::Beep had no wave field); gain and
jingle ms were off. Audio drains host-side in run_tick and after a successful
eval so a startup jingle isn't held a frame; a failed eval discards its queue.

Regression the migration exposed: block verbs spawn through the shared box
path, which re-validated keys against the BOX allow-list, so game.car(...)
logged 8 bogus "unknown option" warnings per eval straight into the channel
the agent reads. Added spawn_entity_unchecked for the block path; a real typo
on game.box still warns. Racing fixture: 8 warnings -> 0.

Tape probe BYTE_IDENTICAL (re-run after the audio and warn changes too).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:43:08 +02:00
Admin
564c1b52dd Arcade M3 (part 1): MR/VR stage modes, XR input, settings panel, authoring inbox
Stage as presentation, never simulation. Stage{mode, origin, yaw, scale} is
applied as the scene draw list's view_transform uniform — one value per
frame, so sky/terrain/cubes/skinned characters all move together, cached
static slabs never invalidate (they hold stage-independent world transforms)
and per-instance cost is zero. The view matrix cannot carry this: in XR the
platform overwrites camera_view with the runtime's eye matrices every frame
(openxr_opengl.rs:20-25), discarding anything the app wrote.

- MrDiorama: world scaled onto an anchored slab, sky/fog/terrain-horizon
  suppressed (the room IS the environment), shadow-catcher quad so it looks
  planted. VrFullScale: 1:1, environment intact. Flat: unchanged
- stage_invariants.rs proves the design point: two worlds run 120 ticks, one
  switching flat->MR->VR->MR(new anchor)->flat mid-run, hashes bit-identical
  at every switch, with an assert_ne against a fresh world so the equality
  isn't vacuous. The stage has no API by which it could reach the world
- Stereo needed NO work, and rendering twice would have been wrong: XR uses
  single-pass GL_OVR_multiview2, so draw_pass.camera_view compiles to an
  indexed [VIEW_ID] lookup and the GPU rasterizes both eyes from one
  encoding. The existing draw path is already stereo-correct
- XR input maps controllers/pinch onto the same per-player InputState the
  net layer sends — an XR player is just another player to the sim
- Quest APK packages (arm64-v8a, passthrough/handtracking/anchor/colocation
  manifest bits) with no cargo_makepad changes
- Settings panel: provider/model pickers, masked key entry, pair button
  showing LAN URL + confirm code, active-tier status line
- Authoring inbox: keyless clients' Intent::Authoring queued for the host's
  agent, bounded and refusing with a reason — every entry eventually costs a
  paid call and it is filled by peers who hold the lobby key but are not
  trusted terminals

Pre-existing startup crash fixed: run_tick held a RefMut across
self.world.borrow() (`let _ = w` drops the reborrow, not the RefMut it came
from), aborting the demo path. Reproduced at HEAD with changes stashed.

Not wired, needs a device: passthrough is requested via
StageMode::wants_passthrough() but not handed to xr_passthrough (arcade has
no XR root widget yet); env-depth occlusion has a hook and no consumer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:32:22 +02:00
Admin
2160c28269 game_script: full verb parity with gamemaker (71 -> 102), pinned by test
Ports the 31 verbs game_script lacked — part, move_part, beam, attach,
detach, speed_mult, raycast, overlap_sphere, ground_peak, held, pressed,
axis, player_input, the cam_* readers and set_cam_* writers, save, load,
tone/tone_set/tone_stop, format, api, reset — verbatim, including the
details that bite: part's defaults and half-floor, move_part's only-given-
keys rule plus its leaves-the-static-slab redraw, attach's vec3-or-options
overload (vec3 parses first; the options path defaults to (0,1,0), not the
previous offset) with velocity zeroed, every documented clamp, raycast's
terrain-reports-as-minus-one convention, and save's strings-before-numeric
ordering (the numeric cast NaNs strings).

Audio stays host-installed: AudioRequest gained Tone/ToneSet/ToneStop/
StopAllTones with a ToneWave mirroring synth::Wave's parse fallbacks. tone()
must return an id synchronously, which a drained queue cannot do, so ids are
minted on Ctx and the host maps them to its own voices — script only ever
holds an opaque handle, so this is observationally identical. save/load
needed no hook at all: save_data lives on GameWorld and flushing was always
the host's job.

the_verb_surface_matches_gamemakers pins the count at 102. (Gamemaker's 98
match arms are 102 names — four are || aliases.) This unblocks migrating
gamemaker off its duplicate binding layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:29:34 +02:00
Admin
88183a924b Fix http_server dropping pipelined body bytes; add Intent::Authoring to game_net
from_tcp_stream wrapped the socket in a BufReader that was dropped on
return, so body bytes read ahead into it vanished and handle_post blocked
forever on bytes that no longer existed — one wedged thread per request
that sent headers and body in the same TCP segment. Browsers split the two,
which is why nothing noticed. The function now owns its buffer, reads to
\r\n\r\n, and returns the remainder alongside the headers for handle_post
to consume first. The websocket upgrade path had the identical exposure (a
frame pipelined with the upgrade was silently dropped) and consumes the
same prefix now; EOF mid-head returns instead of spinning to the 4096-line
guard. New tests cover headers+body in ONE write (the case that hung, with
the connection held open afterwards so a regression blocks rather than
passing on EOF), the split case, and a plain GET.

game_net: Intent::Authoring{text} + MAX_AUTHORING_TEXT so a keyless client
in a hosted room can route a creation request to the host's agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:20:54 +02:00
Admin
cec4f5b380 Arcade M4: voice + AI tiers, game library, /pair key flow, and libs/game/script
- libs/game/script (new): the table-driven game.* binding layer game.md
  called for — 71 verbs in a HashMap built once per isolate, replacing
  gamemaker's 84-arm linear chain. spawn_entity/spawn_terrain ported
  verbatim so fixture terrain matches bit-for-bit; generation-tagged
  callback slots; streaming eval + hot reload. Rollback snapshot is
  GameWorld::clone() — M1a made the world Clone, and a clone cannot forget
  a field, which is the exact bug class that put next_id in M0r's fix list.
  (Gamemaker still runs its own copy; migrating it is a follow-up.)
- Capability tiers: Voice (VAD + Whisper + local judge) -> VoiceUnfiltered
  (push-to-talk, every utterance costs a call) -> Chatbox (typing is the
  gate). Text box in every tier, mic only above Chatbox. The chain sits
  behind the local-llm feature so Quest/mobile never link a backend they
  don't have
- Librarian: the local model is an optional override (None = no opinion),
  never a gate — a flaky or absent model can only sharpen a decision.
  Beneath it, deterministic word-overlap matching for load-by-description,
  restart, and manifest-clamped knob writes. Creative requests deliberately
  do NOT match an existing game (tested). Locally-answerable utterances are
  dropped before they reach the cloud
- /pair: self-contained page (no external URLs, asserted), 4-digit confirm
  code so a room of headsets can't take the wrong key, 0600 config-dir
  storage documented as NOT a keystore — Android/iOS must move to the
  platform one before shipping. Key never enters a log, package, or error
- Racing fixture evals through the new dispatch (28 entities) and renders;
  hot-reload rollback verified live — a bad verb reports with a suggestion
  and keeps the last good world

Found (platform, unfixed — out of this task's scope):
HttpServerHeaders::from_tcp_stream buffers past the headers into a
BufReader, so a body arriving in the same TCP segment is swallowed and
handle_post blocks forever on a body that's already gone. Browsers split
the two, which is why nothing noticed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:05:50 +02:00
Admin
a8427cda75 Arcade M2b: multiplayer — players in the sim, tiered replication, host/join
A room of devices now plays one game: host simulates, clients send input
and render replicated truth.

- libs/game/sim/player.rs: Players roster on GameWorld, slot 0 is always
  this device. Player 0's input stays in the world's original held/pressed/
  pad/cam_yaw fields and is mirrored into the roster, which is what keeps
  single-player numerics bit-identical (tape gate confirms). Ids are never
  reused, so a stale reference resolves to None, never to somebody else
- The camera-movement knot resolved: world.player_move(p) rotates that
  player's axes by THEIR cam_yaw, carried in their input packet. Player 0's
  branch is the original expression character-for-character (the f32 cos
  widened to f64 kept deliberately — tidying it would move the numbers)
- libs/game/session/replication.rs: Shared = pos/vel/size/kind/tag;
  Derived = facing/anim/scale/glow/blob shadows, recomputed client-side and
  costing zero wire bytes; Local = camera/audio/effects. Statics never enter
  the per-tick stream
- Protocol (additive, version unchanged): EntityDesc + Descriptors message
  splits rare reliable construction data from volatile unreliable state —
  without it a joiner sees poses for entities it cannot build. EntityDesc
  carries pos because statics never appear in the state stream (the
  late-joiner test caught ground arriving at the origin)
- Script: game.players/player_name/player_entity/player_input/bot/on_join/
  on_leave; blocks gained owner: PlayerId so a car reads its own driver
- Arcade: ARCADE_HOST=1 / ARCADE_JOIN=<addr>; clients skip world
  construction and don't simulate
- Racing wire volume, 6 players x 60Hz x 200 entities: 2400 pps,
  20.9 Mbit/s up (audit projected 74 Mbit for the XR stack). Asserted in
  racing_scenario_wire_volume_fits_a_living_room

Two more not-a-playable-default bugs, same class as M1b's rng-at-zero:
Entity::default() leaves gravity_scale 0 (weightless wheels) and ground
without friction gives no traction — only the DSL path filled these in.

255 tests green; tape probe BYTE_IDENTICAL; xr/arcade/gamemaker build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 01:21:44 +02:00
Admin
c13c867547 Arcade M1b: libs/game/blocks — car/character/plane, brains, race kit; racing game in 72 lines
The thesis proof: examples/gamemaker/resources/fixtures/racing.splash is a
complete playable racing game in 72 lines (12-corner oval from a waypoint
loop, 4 cars, gates, standings, restart) with no physics, no AI and no lap
bookkeeping in script. Blocks run engine-side at 60Hz: Blocks::pre_step
(intent -> motion) before step_world, post_step after; Blocks is Clone and
snapshots beside GameWorld so a failed eval rolls both back together.

- game.car (4 suspension raycasts on a box3d rigid chassis), game.character
  (drives the existing mover sweep + owns idle/walk/run blending),
  game.plane; game.drive/autodrive/speed
- Brains: game.wander/chase/patrol/caught — the fixture's hand-rolled AI,
  absorbed engine-side
- Race kit: spawnpoint, checkpoint, place, race, standings, lap/rank/
  finished, score/score_of (Shared-tier data, ready for replication)

raycast_vehicle audit (defects documented in car.rs, still live in xr):
libm sin/cos in steering (unreplicable), a wrong side-impulse denominator
(iaj.dot(iaj) where it should be (I^-1 aJ).aJ), and an unguarded division.
Kept the structure, replaced Bullet's friction solver with an arcade force
model: suspension acts at the contact point, grip and drive through the
centre of mass, steering as yaw torque — no lateral force can generate
roll, so it is stable by construction rather than by roll_influence fudge.

Engine bug fixed at the source: GameWorld::new() left rng at 0 and
xorshift64* is a fixed point at zero, so rand() returned 0 forever for any
world built through the sim API. reset_content seeds on every eval, which
hid it from gamemaker entirely; found by a wander brain that never left home.

Tape probe byte-identical; racing fixture evals clean and drives (AI follow
the line, gates bank in order).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:54:22 +02:00
Admin
a929dafa18 world grid 2.5 degrees (9792 cells) — dense slices fit the 16GB M4 workers' in-RAM frame receive
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:45:56 +02:00
Admin
a015fdbf82 mapfleet: atomic slice outputs + twice-failed-on-host cells route to the local worker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:42:21 +02:00
Admin
e1d0500771 Arcade M2a: libs/game/net — host-authoritative LAN transport + micro_serde hardening
Harvest-and-rebuild from xr/src/net after the adversarial audit (verdict:
transport promote-with-fixes, authority model rewrite).

- Authority: explicit Host/Client roles. Client->host Join/Input/Intent/
  Leave/Ping; host->client Welcome/StateBatch/Event/Bye/Pong. No per-object
  authority field and no takeover messages — authority theft (audit H-11)
  is unrepresentable, not merely blocked
- Auth: self-contained SHA-256/HMAC; every datagram and frame is
  magic|version|sender|payload|mac, verified BEFORE any peer state is read
  or written. Closes seq-window poisoning, spoofed kick, address hijack
- Endpoints are pumped, not threaded: nothing blocks, so the connect-flood
  stall (H-5) cannot occur and a full session runs deterministically in one
  test process. The host only ever accepts, never initiates
- Harvested: LZ4 frame codec (check-before-allocate), partial-tail drain,
  budgeted read/write loops, MTU batching, peer/config shapes. Frame cap
  4MiB -> 256KiB now that XR alignment payloads are gone
- Per-entity sequencing (a stale datagram drops only its stale members),
  rejoin seq reset, player cap, peer timeout, snapshot-based mid-join
- Measured 6 clients x 60Hz x 200 entities: 2880 pps, 3.13 MB/s (~25 Mbit
  up) — Quest WiFi viable without delta encoding yet
- Hostile suite: one test per audit attack + 3000-mutation fuzz that must
  never panic or wedge the host. 24/24 green
- micro_serde: String::de_bin no longer panics on invalid UTF-8, checked
  offset arithmetic, Vec::de_bin rejects counts the buffer cannot back and
  never sizes allocation from the wire (11/11)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:33:16 +02:00
Admin
bbbbbc54ba Fix makepad-xr build: resource.handle -> has_handle after the per-heap handle change
0ebe270c2 moved CxScriptResource from a single handle to per-heap handles;
these two lookups were missed (xr is not built by the example/app targets
that gated that commit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:33:16 +02:00
Admin
536e0e2be2 mapfleet: 5-degree world grid + chunked socket writes — dense 15-degree cells sliced to 6-23GB, over macOS's 2GB single-write ceiling and worker RAM; oversize slices (>3.5GB) stay on the local worker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:33:13 +02:00
Admin
17263b6666 Arcade M1a: box3d as the dynamics layer (hybrid — movers keep the parity sweep)
- libs/game/sim/dynamics.rs: box3d world inside GameWorld, body mirror
  RECONCILED against the sorted entity list per tick (merge-walk), so
  retain/rollback/reset are correct by construction. Statics + kinematics
  mirrored (kinematics via target transforms so resting rigids inherit
  platform velocity); smooth terrain as a box3d heightfield
- New BodyKind::Rigid + body:"rigid" with density/friction/restitution;
  game.push = mass-scaled impulse; set_pos/set_vel detected via bit-exact
  pose caches (no new dispatch arms). Sphere rigids roll on real spheres
- Entity.orient quat read back per tick; renderer builds quat instance
  transforms for rigids (no shader change). Step order: mover sweep
  verbatim -> reconcile -> world_step(dt, 4) -> readback; rigid-free
  worlds skip the solver
- GameWorld stays Clone via box3d snapshot round-trip (bit-identical
  continuation proven by test)
- Determinism: double-run equality + golden hash 0xa8a2baf71e4a564f
  (aarch64, debug and release). Perf: 0.038 ms/tick for 100 movers +
  50 rigids + 65x65 terrain (budget 2 ms)
- Tape probe BYTE_IDENTICAL; box3d crate untouched; arcade demo gains a
  kicked crate stack (captures verified: settles upright, then topples
  with rotated resting poses)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:04:26 +02:00
Admin
ac5ed14285 Arcade M1c: skinned animated characters (glTF skins/clips, KayKit, CPU skinning)
- libs/game/render/skin.rs: GLB container + owned-JSON parser, dense
  accessors, multi-primitive skinned meshes, skin + inverse binds, full
  node-hierarchy palette, T/R/S clips (linear/step), nlerp blending.
  Hermetic tests via an in-code 2-joint GLB; real-asset test skips with a
  hint when the download hasn't run
- DrawGameSkinned shader (PbrVertex + albedo texture, terrain-style
  lighting); skinned batch draws between opaque and alpha passes.
  CPU skinning for now (sub-ms at 3716 verts): the uniform_buffer GPU path
  has zero in-tree runtime consumers — SkinnedModel::palette is the seam
  for the GPU swap
- apps/arcade: download_assets.sh (KayKit pinned commit + sha256,
  idempotent, dir gitignored, CC0 license note in-repo); Knight patrols
  the demo world with idle/walk blending, yaw follows path; captures
  verified (pose advances, depth-occludes correctly)
- renderer binary_search sites use the shared sorted-id helper from M0r

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:35:09 +02:00
Admin
d41b6e43ec Arcade M0r: parity-neutral sim internals cleanup
- Entity lookup O(log n): shared entity_index_sorted helper, single
  push_entity spawn path with ascending-id assert + per-tick sorted
  debug_assert — the sorted-Vec invariant is now enforced, not hoped
- WorldSnapshot captures next_id: failed-eval rollback can no longer mint
  colliding ids under surviving entities (review defect fixed)
- game.log buffered (1s/16KiB/eval-boundary flush), agent RPC poll gated
  on one .agent dir-mtime stat — no per-tick file I/O left
- Camera rig authoritative on GameWorld (orbit/chase state); widget keeps
  only device-input accumulation; mailbox drain order unchanged
- Per-tick cumulative script budget: with_instruction_limit reports
  consumption (incl. trap-wipe subtlety), gamemaker tick holds ONE 500k
  pool across on_tick + timers + touch events
- Tape probe verified BYTE-IDENTICAL vs pre-cleanup reference

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:34:45 +02:00
Admin
cb6cec91ca mapfleet: drop world.mkmap.prev after successful swap — a retained prev is a full extra world copy against a tight disk budget
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:02:52 +02:00
Admin
f2f4b1bb50 Spool v2: streaming frontier — the fleet bakes while pass 4 runs
Pass 4 collects relation jobs, bbox-scans them against the RAM stores,
sorts by NL-spiral distance and publishes store/spool-frontier.txt (key
of the first unfinished relation; spool flushed before every publish).
pbf-base slices a LIVE store behind that gate (--bbox only, torn-tail-
tolerant block reads, pid-scoped sort chunks so concurrent slices never
clobber shared edge blocks), and mapfleet claims block per cell on the
frontier instead of waiting for the whole spool.

Pass-granular resume: passes 2/3 stamp exact spool block lengths +
counters + stats; a restart rolls the spool back to the newest stamp
and reruns only the remaining passes (unit tests + crash drill).

Weave fixes from the Luxembourg end-to-end smoke run: bake outputs land
via temp+rename so the ledger name never exists half-written, and
transmux verification now checks the union of ALL sources with first-
source-wins ownership (it compared source 0 alone and failed every
multi-source weave).

Also: pass-4 worker clamp 12→14, dead paged NodeStore/WayStore and
emit_lines/emit_polygons deleted (flat stores/prepare_* replaced them),
mapfleet --pbf + startup chunk sweep + end-of-spiral drain and final
weave, world-build.sh refreshes the -run binary copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:59:15 +02:00
Admin
7bb0c3be34 Arcade M0 stage B: rendering extracted to libs/game/render + arcade live viewport
- libs/game/render: the 5 game draw shaders (script_mod registration),
  shape geometry + winding test, terrain mesh, static-slab instancing,
  draw_scene pass (sky/terrain/opaque/alpha) as GameRenderer over GameDraws
  (draw structs stay #[live] on the host widget so script theming works),
  CameraRig + scene_state per-view (multi-view ready), HUD + billboard
  label drawing. game_view.rs 4640 -> 3599 lines
- apps/arcade: first engine-only viewport (arcade_view.rs) — GameWorld
  built via the sim API with no script VM, 60Hz tick, orbit camera,
  offscreen pass composite; ARCADE_CAPTURE=<png> GPU-capture test hook
- Verified: sandbox3d fixture evals clean headless; arcade demo frame
  GPU-captured and visually checked; suites green. Sim untouched — stage-A
  tape byte-parity (probe.txt identical vs pre-refactor binary) stands

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:56:22 +02:00
Admin
f2228a4654 Arcade M0 stage A: gamemaker sim extracted to libs/game/sim + deterministic math + apps/arcade shell
- libs/game/math: cross-arch bit-deterministic f32 kernel (f64-internal
  fdlibm-style polynomial kernels, exactly-rounded IEEE ops only, zero
  platform-libm transcendentals). Parity golden hashes recorded on aarch64;
  equal hashes on every other target IS the determinism test
- libs/game/sim: the gamemaker world moved verbatim (entity/terrain/world/
  queries/step — float expression order preserved so tapes replay
  bit-identically). No Cx, no draw types, no ScriptObjectRef: script
  callbacks are CallbackSlots resolved through a generation-tagged
  host-side CallbackTable in game_view.rs; sim reset no longer touches audio
- examples/gamemaker: game_view.rs 5545 -> 4640 lines (script boundary,
  rendering, input devices, host I/O remain); per-tick args/input objects
  built via unchecked pushes + release_transient (flat-heap tick path)
- apps/arcade: Makepad Arcade app shell + workspace membership
- Verified: headless sandbox3d fixture (90 entities) boots end-to-end;
  gamemaker/sim/math/script-test suites green

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:28:06 +02:00
Admin
0ebe270c20 Script VM GC campaign: isolate GC, host-transient release, cross-VM resource handle fix
- Escape barrier: every checked store funnel tags stored objects REFFED
  (stored => REFFED structural), ret==args guards at native completion,
  fn-arg bind-time barrier (closure-captured scopes retained args untagged
  -> latent use-after-free under eager release)
- vm.release_transient() for host per-call objects; *_unchecked pushes are
  the releasable-container path; call_with_scope native branch no longer
  leaks one scope object per Rust->native vm.call()
- pump_widget_async: dead-isolate reclaim every pump + needs_gc-gated
  round-robin mark/sweep — isolate heaps were never collected before
- res.rs: resource path cache was Cx-global and cached handle VALUES across
  VMs, so isolate heaps held main-heap handle indices (exposed by the first
  isolate mark pass ever to run). Now: data shared globally, handles minted
  per-heap, cache keyed (heap_key, path), per-heap detach on GC
- vm.gc() no longer shrink_to_fits every run; explicit gc_and_compact()
- script-test: GC campaign suite (flat-heap tick loops, retained-input
  survival, escaped-scope capture, isolate churn collection)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:27:27 +02:00
Admin
1e7cf7723c spool: --no-tiles skips pass 5 (nothing consumes it); chain uses it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:15:31 +02:00
Admin
c7f1dbed8e spool: recursive-bisection polygon tiling — continental boundaries clip in O(n log t)
prepare_polygons clipped the FULL ring (and holes) against every tile in
the bbox: a continent-scale boundary relation (millions of points x
millions of tiles) pinned one core for hours at the planet spool's tail.
Halves clip once per split and recurse (geojson-vt scheme); per-tile
output is geometrically identical (equivalence test: same tiles, equal
areas, identical interior vertices — direct's zero-area boundary spikes
are collapsed, which is strictly cleaner).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:08:21 +02:00
Admin
be9d49ceb7 spool.md: largest-first scheduling + clip_ring segment prefilter (ideas 8-9)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 20:54:12 +02:00
Admin
0529960542 spool.md: pass-granular resume (idea 7)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 20:05:02 +02:00
Admin
292a47d7e6 Shader: register _mp_unpack ids in the GLSL/WGSL luts — Linux vertex compile fix
The wrapper names printed as hex LiveId hashes on non-Metal backends
(identifiers starting with digits — GLSL compile failure at startup on
Linux/GLES). Metal had the lut entries; Glsl/Wgsl now do too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 19:53:19 +02:00
Admin
85b292b593 spool.md: v2 bottleneck analysis — spiral-ordered finalization kills the fleet gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:55:26 +02:00
Admin
b30ff3ede3 Fleet compression architecture: q2 throwaway slices, workers deliver uniform q11
map_bake --recompress re-encodes EVERY tile (both pass-through sites) at
the target quality — compression parallelizes across the whole fleet and
the final dataset is uniformly q11. The slicer ships q2 intermediates
(fast, disk-lean). Bake mode also skips runtime-only work per tile:
building/tree vertex extrusion and POI point parsing never reach the
sink and no longer run on workers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
Admin
f202b9d212 mapfleet: slice at brotli q10 — matches bake re-encode, keeps ten bakers fed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
Admin
dbfd3844cf mapfleet: slice gate 3-wide for the ten-worker fleet
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
Admin
bfb0816816 mapfleet: bootstrap retries every 5min — boxes join when healed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
Admin
a5919cd2a4 mapfleet: workers self-gate on the spool marker — hot-joined boxes bootstrap immediately
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
Admin
12e296ac77 mapfleet: eager worker bootstrap during the spool wait
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:42:21 +02:00
admin2
943a5a2a77 Fix partial Android NDK DMG extraction 2026-08-02 18:29:14 +02:00
Admin
acf5442efe Workspace: members = app + examples + studio + necessary tools + tests; makepad-remote runnable from root
tools/remote joins the root workspace (default-run makepad-remote, so
'cargo run -p makepad-remote -- --server' works from the checkout root
on worker boxes). Libraries stay in the build graph as path deps; the
runnable-package list is the things a human actually launches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:55:44 +02:00
Admin
4c81087010 mapfleet: live-joining compute nodes (--hosts-file polled 30s) + dual slice gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
05f40f2ed9 mapfleet: distributed world-cell bake over makepad-remote boxes
Protocol extracted to a shared module + TAG_FILE_PULL (server returns a
file from its cwd, escape-guarded). mapfleet dispatches the NL-spiral:
local slicing against the store (serialized), remote bakes via cargo-run
on each box (bootstrap build cached), pull-back, coalesced weave with
atomic world.mkmap swap. Resume ledger (cell-NNN-baked.mbtiles, empty =
ocean) is shared with world-slabs.sh — run one driver at a time. Failed
cells requeue with backoff; a dead box just stops claiming.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
949365029e Studio: fs-event filter shares the bulk-dir predicate — churn under local/ never reaches reload
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
7534799f29 Studio: fix boot hang — bulk dirs out of the file tree, term history bounded
sample(1) showed the hub thread pegged in FlatFileTree rebuild +
git-status cascade: the world bake writing thousands of files under
local/ stormed fs events and every burst re-sorted the whole repo tree
(incl. target/.rustup/255GB of local). One heavy_nonproject_dir
predicate now excludes them at all scan sites. Separately, .term replay
caps at the last 2MB and files rotate at 4MB — a 208MB terminal history
from the weekend's bake logs was minutes of VT parsing at boot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
a8b52d737d Map: world chain hardening — pipefail (tee masked spool failure), instance locks, mercator-clamped cells
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
882cf6e440 Shader: unpack2f16/unpack4u8 on all backends — GLSL ES3 built-ins, WGSL mapping, headless Rust impls
GLSL emits _mp_ wrappers over unpackHalf2x16/unpackUnorm4x8 (WebGL2
baseline has them native); WGSL names mapped for the future backend;
the headless runtime gets bit-exact CPU decodes. The packed map vertex
format is now portable to the wasm target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
0a0ad2e65a Map: plain alpha fade restored; established-3D arrivals snap (no fade, no grow)
The dithered dissolve traded one artifact for another (user call): the
real fix is not fading at all in an already-3D scene — fades stay for 2D
and the single flat->3D mode reveal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
f4af4db88d Map: growing-archive watcher — the app reloads as the world spiral weaves cells in
5s mtime watch on the active archive (root.mkidx for mkmap); on change,
Failed placeholders clear and the visible loop re-requests. Workers
already reopen per batch, so an atomic shard-set swap appears live —
apps/route now points at world.mkmap and starts empty until cell-001
(NL) lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
cf1b977d00 Map: NL-spiral slab driver — slice/bake/weave per cell, live-growing world shards
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
990f63de26 mkmap: multi-source weave transmux — first-source-wins, spiral-cell assembly
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
489d5aa6a1 Map: world-build chain script — planet download-wait -> spool -> base -> bake -> shards
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
152b936084 Map: tile crossfade = ordered-dither dissolve — kills the arrival flash
Alpha-multiplying opaque 3D through a depth-writing fade let the clear
color bleed through for 0.25s per arriving tile. Surviving fragments now
stay fully opaque; a 4x4 Bayer threshold ramps coverage instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
41c517e4b5 Map: grow-heights plays only on the flat->3D mode reveal — established 3D never re-pops
Fresh/evicted/rebucketed tiles arriving into an already-3D scene were
replaying the pop-up (the zoom-crossing flash).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
cf6e6c5025 Map: road-icon append test updated for packed buffers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
1ed8334e4d Map: dissolve eligibility caps monster same-height sets — jetsam-proof bake
Tile ~22000 (Westland greenhouse belt) put thousands of identical-height
rings in one union and the memory spike got the bake SIGKILLed at the
same tile every run. Groups over 800 jobs / 120k ring points stay
per-building; the cap is computed identically at bake and runtime so
signatures agree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00