Skip to content

1.15.0 "Where everyone is": roadmap 29's finished core lanes (sessions R4-R6, SDK polish + undo, football suite, Dungeon Realms/Untangle templates, embed flag) - #228

Merged
AlexZ005 merged 19 commits into
release/nextfrom
feat/1.15
Sep 19, 2026
Merged

AlexZ005 merged 19 commits into
release/nextfrom
feat/1.15

Conversation

@AlexZ005

Copy link
Copy Markdown
Collaborator

Roadmap 29's finished core lanes, integrated on feat/1.15 in dependency order and gated on the merged tree — the 1.15.0 release branch.

What landed (six lane PRs, all merged clean)

Gates on the merged tree

gate base (1.14.0, this worktree) merged
svelte-check 341 errors / 47 warnings 341 / 47, message list IDENTICAL
vitest 143 178
npm run build green (server stopped)

Battery (serial, under the machine-wide lock, port 5219), 26 suites: session-scenes 36 · scene-rename 46 · scene-duplicate 29 · sdk-game-seams 78 · embed-boot 20 · game-football 102 · game-dungeon-realms 64 · game-untangle 46 · explorer-duplicate 24 + scene-folder 56 · game-presence 61 · project-file 56 · project-manifest 98 · scene-open-guard 42 · scene-rooms 58 (+truth 83, +matrix 42) · hud-actions 65 · ai-flow-physics 38 · game-stars-room 36 · game-towers 20 · community-seams + contest-starters 102 · knock-node 50 — all green. The two reds are the documented pre-existing ones, identical to base: invite-link-live 21/1 (the public-cloud dial) and scene-levels 35/9 (the handshake premise, A/B'd at f4b59f9 by the sessions lane).

Known / follow-ups

  • Two Play presses inside the sim start-up window start two simulators (football R1 finding) — recorded in CHANGELOG + CLAUDE.md, needs a deterministic yield rule.
  • The two core seams the cloud lanes need (connectToPeer(peerId, cloudMeta) + authProvider.decide + approval label + dialMeta; api.aiPresets + setMeshJobStatus) are lane 29-core-seams, still open → 1.15.x.

🤖 Generated with Claude Code

AlexZ005 and others added 19 commits September 19, 2026 13:09
…rule

- api.flow.nodes() snapshots carry x/y (read-only) — addNodes took positions in,
  nothing gave them back, so a module building a graph could not ask where it was
  already occupied (DEVX #16)
- api.flow.freeRegion({w, h, graphId}) answers where a block of new nodes lands:
  left-aligned under the lowest card, a margin from the origin in an empty graph
- the rule lives in ONE leaf, src/lib/flowLayout.js (imports nothing); gameRecipes'
  copy left core in R3a, and the surviving right-of-everything copy in hudActions'
  addBinding now calls the same function with side 'right' (byte-identical x,
  pinned by a unit test)
- suites: sdk-game-seams +7 checks (positions on both peers, two recipes in a row
  beside a user node overlap nothing and stack downward, the overlap detector
  catches a constant-placed block, unknown graph = margin); new unit test
  tests/unit/flowLayout.test.js (7)
- counterfactual: freeRegion returning a constant -> "two recipes in a row land
  on nothing" and "each block lands BELOW" go red (e2e), 5 unit tests red
- hud-actions 65/65 green (addBinding placement); svelte-check 341/47 = base,
  identical error list; vitest 143 -> 150 at this commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… in the seam

- api.flow.onChange(fn) (graph documents AND the trigger log — a node firing is
  what a collected-state list changes on), api.game.onChange(fn) (the game
  singleton), api.peerVars.onChange(fn) (my row + remote rows): thin subscribe
  wrappers over stores core already has, each journalled for teardown and also
  returning an off() for a toolbox that mounts and unmounts (DEVX #17)
- coalesced INSIDE the seam (src/lib/coalesce.js, a leaf): the svelte
  subscribe's synchronous first call is swallowed, any number of ticks before
  the next frame run the handler once, a flush queued at teardown never runs, a
  throwing handler cannot break the store write. One FRAME, not a microtask: a
  microtask was measured to fold a local burst but not thirty edits arriving
  from a peer (+30 handler calls for 30 messages); a 100ms timer races the frame
  so a background tab still gets its call
- suites: sdk-game-seams +15 checks (60 idle frames with a time node ticking fire
  nothing; a 30-tick bulk edit = 1 call; the peer's handler fires as edits arrive
  and folds them, +1 for 30; a node firing = 1; game-state change fires on both
  peers; 20 setVar in a burst = 1; a peer-var write fires mine and the peer's; the
  returned off() stops one subscriber; deactivate unsubscribes all three);
  tests/unit/coalesce.test.js (8)
- counterfactual: debounce removed (flush called per tick) -> bulk edit 30,
  arriving +30, firing +30, setVar 20: five checks red; microtask instead of
  frame -> "arriving edits are coalesced" red (+30 for 30)
- sdk-game-seams 61/61 (base 34), hud-actions 65/65, vitest 158 (base 143),
  svelte-check 341/47 = base with an identical error list, build green

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… kept, a corner link

- playMode.js: `embedMode` (writable, set ONCE from `?embed=1` at module evaluation —
  before the cloud plugin clears the query with replaceState), `embedSceneId`,
  `embedOpenUrl()` (`/?s=<id>`, or `/`). Absent, or any value but `1`, is the old app.
- Menu.svelte: the chrome tree gets `id="editor-chrome"` and hides on
  `$isLocked || $embedMode` (sidebar, pill, panels, toasts, welcome — everything in it).
- App.svelte: the editor windows block gates on `!$embedMode` too; the dock inset is off in
  an embed; `#embed-chrome` draws the two things an embed owns — `#embed-open-link`
  ("Open in theprototype.app", new tab) and `#embed-play` (▶, only while not playing, so an
  Esc inside the frame has a way back in; a real click = the gesture the pointer lock wants).
- Why: roadmap 29 fork 4 — the community Worker's `/e/<id>` frames the app at
  `/?s=<id>&play=1&embed=1`; no separate viewer build.

Suite `tests/e2e/embed-boot.test.cjs` (20 checks, single page): bare boot and `?embed=0`
keep the old behaviour; `?s=abc123&embed=1` → embedMode, #editor-chrome hidden, link
`/?s=abc123` target _blank, no dock inset whatever the pref, ▶ offered; a real click on ▶
enters play (▶ gone, link stays, canvas full-bleed); exitPlay leaves play and the chrome
STAYS hidden, ▶ returns; `?embed=1` alone links to `/`; no page errors.
Counterfactual: Menu.svelte reverted to `$isLocked` only → in an embed `#editor-chrome`
renders visible while embedMode is true (measured with the suite's snapshot: chrome ""),
so "the editor chrome is hidden" goes red; restored.

Held suites: community-seams + contest-starters 102 (= base). svelte-check 341 errors /
47 warnings, identical message list to the base (line numbers shift only). vitest 143.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d, and Join

In the locked vocabulary (roadmap 29 fork 1): a SESSION is the mesh, one per
invite link, and owns the project; a SCENE is where a peer currently is inside
it, a tag on the peer rather than a second connection; a ROOM stays the
PocketBase discovery record. `peerScenes` already carries the tags (P2b) and
the peers popover already groups by them (round 30); what was missing was the
scene LIST saying who is where, and a way to join somebody from it.

- peerScenes.peersAtScene(map, scene, mine, host): the per-scene session roster,
  us included, resolved through the same roomOf/roomCtx pair the popover and the
  gates use - so an unnamed peer counts as standing in the host's room here too,
  and the card and the popover cannot disagree about who is where. Takes the map
  so a component stays reactive (get() registers no dependency).
- Explorer scene cards (both views) carry a presence badge: `.explorer-here`,
  `data-here=<count>`, a dot per person in that person's colour (emerald = you),
  the names in the title ("You and Bob are here"). Bottom RIGHT on the grid
  card - the one corner nothing else claims. Drawn for held AND
  not-on-this-device scene cards, found by hash first (a version file of Arena is
  Arena whatever it is called) and by file name for a loose file.
- "Join <peer>" entries in the scene card menu, one per peer standing there,
  through `travelToPeerScene` - the guarded route the popover's Go to already
  uses, so the unsaved-changes guard runs. Offered on a scene you have not even
  downloaded yet: the people standing there are the reason to fetch it.
- The invite link IS the session and is unchanged: `#<ID>[~srv=...]`, no scene
  hint - a joiner adopts the host's scene over the handshake (round 30 A1), so a
  hint would only duplicate what `atscene` already says. Asserted in the suite.

Suite: tests/e2e/session-scenes (36 checks, two peers + a late joiner).
Counterfactuals, in-suite against their restore: the joiner's tag stripped ->
the card counts only me, the next tag restores it (the badge is presence and
nothing else); travel -> the roster MOVES (Forge is me, Arena is the joiner, on
both screens); a late joiner converges from the handshake alone (both peers'
tags known before any travel, the card counts three).
Held: game-presence 61/0, invite-link-live 21/1 (the pre-existing public-cloud
dial, as the brief predicted), scene-levels, project-manifest 98/0 - see the
lane handover for the settled-tree numbers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fire-time resolve

Fork 3, locked: the SCENE is primary and its .tpscene files follow; a loose
file's rename is just the file's rename. The header chip reads
`Scene [file.tpscene]`; double-click (or right-click, or a scene card's "Rename
scene...") renames the scene through the same inline editor the project name
uses - never a prompt.

THE PROBLEM A RENAME HAS THAT NOTHING ELSE IN THE MANIFEST HAS. The document is
`scenes: {name -> {history}}` under a UNION merge whose wipe protection carries
"a scene only one side knows about" WHOLE - so a plain key move is undone by the
next document from any peer still holding the old key, and the project ends up
with one history under two names. So a rename is RECORDED (`manifest.renames =
{from: {to, at}}`, omitted when empty - a project that never renamed serializes
byte-identically) and the merge FOLDS a scene arriving under a renamed key into
where the name points now, by LINEAGE: a shared hash means one line seen from a
peer that had not heard; no shared hash means a FRESH scene reusing the old
name, which stays and SPENDS the record (the self-pruning tombstone rule).
Records union per key (newer stamp) - a rename is a fact the side that never
heard cannot retract.

THE DATA HAZARD (the brief's): Travel nodes store the scene by NAME
(`sceneName`) or by frozen HASH + display `levelName`, in graphs inside files on
disk. Rewriting the bytes would mint a new version of every referencing scene on
every peer per rename. Two halves instead, no bytes touched:
- the LIVE graphs are rewritten on EVERY peer locally off the replicated record
  (`rewriteTravelNodes`, pure + vitest, applied through updateFlowNodeData -
  deterministic, nothing sent; a peer in another scene, which the room gate
  would never hand a nodedata to, is covered too), and again whenever a scene
  loads;
- a name in a file nobody rewrote RESOLVES through the record at fire time
  (`travelToScene` -> `resolveProjectSceneName`, transitive with a cycle limit).
`travelToLevel` names an arrived scene by `sceneOfHash` FIRST: a renamed scene's
file keeps the old name inside it.

- src/lib/sceneRename.js (LEAF, imports nothing) + tests/unit/sceneRename.test.js (20)
- projectManifest: normalize `renames`, the merge fold, outbound scoping (a
  private scene's rename never leaves; a joiner sends only the records it may
  name), renameProjectScene / sceneRenames / resolveProjectSceneName
- levels: renameScene (project -> replicated; loose -> file + open name),
  applySceneRenames (IDEMPOTENT local half on every peer: files on both shelves
  via explorer.renameItemsWhere, the open scene's name, the live nodes; then the
  open scene's SIGNATURE is re-stamped, because a rewritten node is honestly a
  change and a rename must not read as an edit), a manifest subscriber
  (microtask - the applier renames records and the shared-library sweep writes
  the manifest back) LAST in the file (TDZ rule)
- explorer.renameItemsWhere(hashes, from, to): both shelves, by hash AND name
- Explorer: `#explorer-scene-file`, `#explorer-scene-input`, the chip's own menu,
  the card's "Rename scene..." (inline mode `rename-scene`; a loose file keeps
  plain Rename, which already carries the open name)

Suite: tests/e2e/scene-rename (46 checks, two peers).
Counterfactuals, in-suite against their restore: the record stripped on the
peer -> travelToScene("Arena") is DEAD ("No scene called"), alive again with it
back; the live rewrite undone by hand -> the applier rewrites the node again
(it is the thing keeping live graphs right); a stale document still keying
Arena -> folded into Vault with its novel save kept, the record surviving; a
FRESH Arena -> kept as its own scene, the record spent. Plus: rename on A ->
the peer's header reads the new name; a taken name refused; a loose file's
rename leaves the manifest untouched.
Held: scene-open-guard 42/0, project-file 56/0 - see the lane handover for the
settled-tree battery.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… one file

Roadmap 24-C1/C3 shipped Duplicate / Copy / Cut / Paste for every kind, and a
Duplicate on a SCENE asks a name (its identity is the name inside the file).
What it left was the PASTE of a scene: Ctrl+C / Ctrl+V copied the RECORD - a
second "Arena.tpscene" card pointing at the same hash with the same name inside,
so one scene had two cards and the next fold hid one of them. A pasted scene now
mints a scene copy under the Finder copy name ("Arena copy", then "Arena copy
2") - the file a Save would have made: its own name inside, a fresh identity,
its own hash, its own manifest entry, landing in the folder it was pasted into.
`sceneCopyBytes` + `sceneCopyName` reused; `duplicateScene` gains an additive
`opts.folderId` (absent keeps Duplicate's beside-its-source rule).

Every other kind keeps 24-C's rule ON PURPOSE: a file is its content hash, and a
second record of it is exactly what Copy/Paste means (24-C2's row identity).
The brief's "refuse a texture duplicate with the rule" predates 24-C; the fork
and the recommendation are in the lane's QUESTIONS file, section 1.

Suite: tests/e2e/scene-duplicate (29 checks, two peers). Counterfactual, in
suite: a pasted TEXTURE is a second record of the SAME hash, its own id, no
manifest scene minted (the rule stated on the Duplicate entry itself); a pasted
scene is three cards with three different hashes; Duplicate through the real
menu prefills the next free copy name and refuses a taken one; a peer learns the
copies as scenes of their own, never a second Arena.tpscene.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… real zip

- tests/e2e/game-football.test.cjs: loads games/football/scene.tpscene from the scenes
  FEED (SCENES_BASE, tag v2; FOOTBALL_TPSCENE or a sibling scenes checkout as fallbacks)
  and installs the REAL football.zip (FOOTBALL_ZIP, a packed sibling modules checkout, or
  the modules CDN) on two peers plus a late joiner. Skip-never-fail: an unreachable scene
  or zip prints the reason and exits green.
- covers the modules test-flight's cases on the file: the world and its physics block,
  joining through a real HUD button click AND the physical button's click path, Start ->
  the playing HUD, the P menu (open / Resume), B's hit -> lastTouch on both + A's hit log,
  a blue goal credited to B's own row only, lamps + DOM HUD score, an own goal, own goals
  ignored, duel refusing a second red, the late joiner mid-match (score, slots, playing
  HUD via showWhile, its own live knock), time mode ending the match for all three, the
  over screen, the saved log, New match back to menu.
- game-shell transitions asserted on the TRIGGER LOG (evstart / evover / evnew stamps),
  never on a perRound flowValue.
- counterfactuals: an untouched ball into the red gate scores for blue and credits NOBODY
  (4.12/4.13, in-suite); C with remoteSimulating cleared has a dead probe (5.8, in-suite,
  restored); the handshake `simulate` push disabled in peerHandler -> 5.3 and 5.8 RED
  (C is never told, and in fact starts its own second sim), restored -> green.
- FINDINGS recorded, not fixed (outside this lane): (1) a DUAL-SIMULATOR race - two Play
  presses inside the sim start-up window both pass maybeSimOnPlay's guard, both peers
  simulate, and the ball is fought over forever (a parked ball snapped back under a
  permanent `hold: external`, no goal could score); the suite enters Play in order and
  asserts 2.2b. (2) the template's P pause menu is session-wide (the keypress pulse
  replicates), asserted as-is in 3.10.
- suite: 102 PASS / 0 FAIL in 71s. held suites at base: game-stars-room 36, game-towers
  20, knock-node 50, knock-physics 76 + its base red 2.14 (no core code changed).
- svelte-check 341 errors / 47 warnings, list identical to base; build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ne-root groups on the card, the dungeon-realms suite

- scripts/author-templates.cjs: a MODULE_DEFS LOADER region (football, dungeon-realms) - each slug is read
  from the sibling modules checkout as modules/<id>/<id>.def.json (moduleDef), emitted by that module's own
  build so the def and the module cannot drift; `--only <slug>` authors one. Additive `thumb.sceneGroups`:
  a def names scene-root module groups to clone into the card render - a dungeon's world is scene-root
  content (golden rule 5), so the card showed a lone arch. Absent = the picture it always was. Kept
  apart from the HUD helper region 29-football edits.
- tests/e2e/game-dungeon-realms.test.cjs (64 checks; two peers + a late joiner; the real
  games/dungeon-realms/scene.tpscene from the feed / DUNGEON_REALMS_TPSCENE / a sibling scenes checkout;
  dungeon.zip + dungeon-realms.zip from DUNGEON_KIT_ZIP / DUNGEON_REALMS_ZIP / MODULES_REPO / a sibling
  modules checkout / the CDN; skip-never-fail): the two-module requirement list; the 6 arch objects, night
  env, click + grounded play block, the HUD document; the Kit regenerates seed 1337 FROM THE NODE after
  applySession's /clear all (a 777 dungeon before the load is replaced); B over the handshake with the same
  Kit checksum; Start -> the `drevent start` stamp in the TRIGGER LOG -> Set Game State playing on both ->
  the HUD screen; HUD Text reads the Realms Value nodes, HUD lists the Realms HUD Rows; a gem -> B's HUD,
  the replicated gem event counts ONCE (Counter -> HUD Text); P pause / Resume; unseal -> portal -> floor 2
  on both; victory across five floors -> `drevent victory` -> the shell is over; the late joiner reads it
  all. B's objects are asserted by UUID: a mesh name with a space arrives underscored on the peer
  ("Entrance_plinth"; a light keeps its space) and the graph binds by uuid anyway.

Counterfactual: dungeon-realms' game.js no longer pulses the start event -> 3.7 "the On start stamp landed
in the TRIGGER LOG" red (+3.8-3.10, 3.14, 5.2 downstream); restored, module.js byte-identical.

Gates: npm run build OK; svelte-check identical to base (341 errors / 47 warnings, same list); vitest
143/143; held game-stars-room 36/36 and game-towers 20/20 at base. Authored scene staged in
cloud-lane-29-staging/games/dungeon-realms/ (9169 B + 3848 B thumb).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ngle suite

- scripts/author-templates.cjs: `untangle` joins MODULE_DEFS (modules/untangle/untangle.def.json, emitted by
  `npm run build:untangle`); its board is scene-root content too (`thumb.sceneGroups: ['untangle-module']`).
- tests/e2e/game-untangle.test.cjs (46 checks; two peers + a late joiner; the real games/untangle/scene.tpscene
  from the feed / UNTANGLE_TPSCENE / a sibling scenes checkout; untangle.zip from UNTANGLE_ZIP / MODULES_REPO /
  a sibling modules checkout / the CDN; skip-never-fail): THE ORDERING - applySession runs /clear all FIRST
  (the module counts one scene clear) and the graph's Untangle Board node applies LEVEL 2 on the next tick,
  from a level-5 board (not 1, not 5); the SAME file loaded again re-applies level 2 (the node re-armed by the
  clear); the pose is node data (editing boardY on A's node moves B's board and back); B builds the identical
  scramble; Start -> playing + the HUD screen; HUD Text reads the Untangle Value nodes (level, crossings) and
  follows a move; a solve -> the `utevent solved` stamp in the TRIGGER LOG on B, the Counter counts ONCE on
  both, autoAdvance to level 3 in lockstep; no sprite on desktop (the VR-only path); P pause / Resume; the
  late joiner has the room, the level, A's positions and the game shell.

Counterfactual: untangle's onSceneClear no longer re-arms the node -> 1.12 "the same file loaded again ...
the node re-applied LEVEL 2 (a board exists)" red (+2.1, 3.2, 4.3, 4.6, 4.7 downstream); restored,
module.js byte-identical.

Gates: the same build + svelte-check run as C6-b (scripts/ and tests/e2e are outside the app bundle and
the checked sources): build OK, svelte-check identical to base. Authored scene staged in
cloud-lane-29-staging/games/untangle/ (8946 B + 2802 B thumb).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…module

- A module's node-data write went out on the wire and never touched the undo
  stack, so the next Ctrl+Z undid whatever came BEFORE it (the sdk-polish
  measurement: 60 writes, 0 entries, the undo removed the nodes' creation).
- setNodeData now records a `flownodes` op:'data' entry with the patched keys'
  previous values (structuredClone'd, so a module mutating its patch later
  cannot rewrite history) and `moduleId`. Same `nodedata` message on the wire.
- flownodes data items may carry their own graphId (overrides the entry's) so
  one entry can span graphs - the batch call in S3b needs it. Additive; every
  existing entry has none and behaves as before.
- sdk-game-seams section 4b: one entry, attributed, one undo restores the
  previous data on BOTH peers, one redo re-applies, unknown id still false.
- Counterfactual: record call removed -> 3 red (depth 5 -> 5, top entry was
  `game`; undo left the edit in place on both peers).
- sdk-game-seams 65/65 (base 59/59); vitest 158/158; svelte-check 341/47,
  identical list; build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- setNodesData([{id, patch}, ...]) writes each node through the same path and
  validation as setNodeData (unknown ids and junk items skipped), records ONE
  `flownodes` data entry for the whole batch (items carry their own graphId, so
  a group spanning one object graph per collectible is still one step), and
  returns how many nodes it wrote. An empty batch records nothing.
- Wire: the ordinary per-node `nodedata`. There is no batched type, and adding
  one would leave older peers unconverged until nodesync - the plan's rule
  (undo correctness first, wire economy second); sdk-polish measured 60
  messages at 7.1 ms, all delivered.
- The flownodes data handler now undoes its items in REVERSE: a batch that
  writes one node twice recorded the second item's `before` after the first
  write, so forward order restored the intermediate value.
- sdk-game-seams section 4c: 12 nodes over the scene graph + an object graph,
  one entry of 13 items spanning 2 graphs, replicated, one undo restores all
  on both peers (incl. the twice-written node), one redo, empty batch inert.
- Counterfactual A: record per item -> 3 red (11 -> 24 entries, undo left 12
  of 12 edited). Counterfactual B: forward-order undo -> 1 red (the
  twice-written node read "grp" after undo).
- sdk-game-seams 72/72 (base 59/59); vitest 158/158; svelte-check 341/47,
  identical list; build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e Ctrl+Z, a late joiner

- Section 7b reproduces sdk-polish's measurement shape: sixty nodes over the
  scene graph and two object graphs, created in three calls (so the entry
  under the edit is a CREATION), one setNodesData call (60 in ~5 ms), all
  sixty on the peer; a REAL Ctrl+Z restores all sixty on both peers and the
  sixty nodes are still there; after redo a late joiner (C dials A) holds the
  edited values from the ordinary full-state reply, and a later Ctrl+Z
  reverts them on the joiner too.
- Counterfactual 1: skip the history entry -> 7 red; Ctrl+Z undid the
  creation (40 of 60 nodes left, the edit still in place), exactly the
  sdk-polish finding.
- Counterfactual 2: drop the batch (one entry per node) -> 5 red; one Ctrl+Z
  reverted one node of sixty (tags ["bulk60","orig"]).
- sdk-game-seams 78/78 (base 59/59); ai-flow-physics 38/38 and hud-actions
  65/65 (the other flownodes users) held; vitest 158/158; svelte-check 341/47,
  identical list; build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…skill lines

- CHANGELOG: the 1.15.0 section (sessions R4-R6, Dungeon Realms + Untangle, the football
  suite, embeds, the SDK seams, the dual-simulator race as a known issue); the 1.12.0
  oversized-scene line corrected to Open anyway / Cancel (the docs lane read the code:
  confirmSceneSize offers TWO ways out, never "the first N")
- CLAUDE.md: the roadmap-22 vocabulary; sceneRename.js / peersAtScene / flowLayout +
  coalesce / author-templates MODULE_DEFS / playMode embedMode architecture entries;
  gotchas from every lane (two Play presses = two simulators, underscored mesh names on the
  peer, the User-tab count, node-data undo, effect nodes pin the pose, transient spawns,
  the union-merge traps); the 1.15.0 status entry; the R29 Module SDK additions
- e2e-verify skill: the GAMES-TAB line (football/dungeon-realms/untangle + their env
  overrides) and the SESSIONS line

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@AlexZ005
AlexZ005 merged commit 09499e8 into release/next Sep 19, 2026
4 checks passed
@AlexZ005
AlexZ005 deleted the feat/1.15 branch September 19, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant