Skip to content

feat(javascript): the Generational Collection story, in 2D and 3D - #225

Merged
mnaimfaizy merged 9 commits into
mainfrom
feature/js-generational-collection-story
Oct 14, 2026
Merged

mnaimfaizy merged 9 commits into
mainfrom
feature/js-generational-collection-story

Conversation

@mnaimfaizy

Copy link
Copy Markdown
Owner

The Generational Collection story, from the approved spec docs/stories/javascript/generational-collection.md, in 2D and 3D. It sits in the Garbage Collection section, after the Garbage Collection story.

What it shows

cache = { calls: 0 } and batch() run twice. Each run makes temporary objects that die young. A minor collection copies cache from semi-space A to semi-space B and updates the reference to point at the copy. All of A is then freed in one step, without visiting the dead objects (the aha, beat 6: a collection costs what survives, not what died). The spaces swap roles, and a second collection promotes cache to the old generation. 10 beats.

Changes

  • Snapshot: four optional fields that traces never set: Snapshot.spaces, HeapObject.copyOf, a copied state and a scavenge phase, plus helpers (spaceOf, isSpaceFreed, originOf, getCopyTrails…). A test traces the story's program and checks none of them appear.
  • 2D and 3D views: one heap region per space, with FROM-SPACE / TO-SPACE / FREE tags; COPIED objects at half strength; a freed space drawn once, faint, with no per-object RECLAIMED tag; a dotted copy trail, and a short glide in 3D (none with reduced motion). Fixture tests prove snapshots without spaces lay out exactly as before in both views.
  • Fix to the shared 2D view: the enter animation held opacity at 1, so collected objects never faded. They now do, in the Garbage Collection story too.
  • Section: the story, a pointer from the first story's note, a "Young generation" card, and "weak" dropped from "generational hypothesis". No Playground example: a run cannot observe a collection.
  • Research report 6.3: one paragraph on what a scavenge does, checked against V8's "Trash talk" (source 26).
  • Quiz: javascript-q34 to q36.
  • beat-model skill: a moved object is drawn as a copy, never as an object changing cell.

Validation

  • npx tsc -b, lint, npm run test:run (1,505 tests in 106 files), build, agents:check, and the lazy-chunk check all pass.
  • Browser at 1280px: 2D beats 4, 6 and 7 (labels 11.3px); 3D beats 4 and 8. A fresh load stepped through all ten beats in 3D and back to 2D with no console errors. The Garbage Collection story's beat 8 still reads correctly with the new fade.
  • Not checked in the browser: the no-WebGL fallback and Hold view.

Known

  • The Garbage Collection story's 3D HEAP caption overlaps #draft at 800px wide on beats 1, 2, 7 and 9. This was already the case before this PR.

🤖 Generated with Claude Code

mnaimfaizy and others added 9 commits October 14, 2026 16:27
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the optional snapshot extension the story needs: Snapshot.spaces,
HeapObject.copyOf, the copied heap state (its slots give no references)
and the scavenge GC phase, with shared helpers spaceOf, objectsInSpace,
isSpaceFreed, originOf, copiesOf and getCopyTrails. Traces set none of
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The views draw a FROM-SPACE / TO-SPACE tag whenever a role is set, so
beats 1, 2 and 10 carry no role. The spec's space table says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… story

The shared 2D stack-and-heap view draws a heap split into spaces: one band
per space with a header above its row, FROM-SPACE / TO-SPACE tags from the
data, FREE on a space freed as a whole (no per-object RECLAIMED there), a
COPIED tag, a dotted copy trail, #origin labels, and frames as roots during
a scavenge. A snapshot without spaces lays out exactly as before (golden
fixture recorded from the earlier stories).

GenerationalCollectionStory follows the Garbage Collection story in its
section, with the prose changes from the approved spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The enter animation ends at opacity 1 with fill-mode both, which overrode
the opacity attribute on an object's group, so COPIED and collected objects
were drawn at full strength. The fade now sits on an inner group.

A heap split into spaces gets a narrower stack (190), a 212-unit column
pitch and fixed 132-unit rows: the drawing is 900x586 instead of 1014x760,
so 12-unit labels are drawn at about 11.3px on a 1280px page. Snapshots
without spaces lay out as before (fixture test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared 3D stack-and-heap view draws the generational collection story:
one floor slab per space with its caption (FROM-SPACE / TO-SPACE / FREE),
copied objects half faded with a COPIED line, no per-object RECLAIMED in a
freed space, a dotted copy trail with a one-beat glide (none with reduced
motion), #origin labels, and frames as roots during a scavenge. A stack arc
to a first-column object behind another lands on its stack-facing side.
A snapshot without spaces lays out exactly as before (fixture test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mnaimfaizy
mnaimfaizy merged commit d7c1538 into main Oct 14, 2026
9 checks passed
@mnaimfaizy
mnaimfaizy deleted the feature/js-generational-collection-story branch October 14, 2026 09:54
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