PinePaper v0.5.0-beta — Release Test Checklist
Walkthrough checklist for the v0.5 release session. Every item below maps to a specific change made during v0.5 development. If any item fails, do not merge to main — fix first.
Estimated walkthrough time: 60–90 minutes for a thorough pass.
Run order matters in some places (e.g. Phase 1 unit field must work before Phase 2 measurement system tests have meaning). Follow the section order top-to-bottom.
Pre-flight
- [ ]
npx jest --silentpasses 5815/5815 tests - [ ]
npx webpack --config webpack.config.prod.jsbuilds without errors (3 size warnings are OK) - [ ]
npm run previewloads the editor in browser with NO console errors- Critical: this verifies the obfuscator TDZ fix. If you see
Cannot access 'X' before initialization, the fix regressed.
- Critical: this verifies the obfuscator TDZ fix. If you see
- [ ] No
setCanvasSize is not a functionerrors when clicking canvas size buttons
Phase 1 — Foundation bug fixes
1.1 — Canvas units API (canvasSize.units, canvasSize.pxPerUnit)
In the browser console after the editor loads:
- [ ]
app.getCanvasUnit()returns{ units: 'px', pxPerUnit: 1 } - [ ]
app.setCanvasUnit('mm')followed byapp.getCanvasUnit()returns{ units: 'mm', pxPerUnit: 3.7795... } - [ ]
app.unitToPx(10)returns approximately37.79(10 mm in pixels at 96 DPI) - [ ]
app.pxToUnit(37.79)returns approximately10 - [ ]
app.setCanvasUnit('in')thenapp.getCanvasUnit()returns{ units: 'in', pxPerUnit: 96 } - [ ]
app.setCanvasUnit('px')resets to pixel mode
1.2 — Scene unit persistence
- [ ]
app.setCanvasUnit('mm') - [ ] Save scene (Scene panel → Save current scene)
- [ ] Reload editor (refresh page)
- [ ] Load the saved scene
- [ ] Expected:
app.getCanvasUnit()showsmmagain
1.3 — Generator preserve mode (Bug 3 fix)
- [ ] Open the generator panel
- [ ] Pick Function Plot, set animation to Scroll Left, click Generate Scene
- [ ] Expected: function plot draws and scroll-left animation runs
- [ ] Tick the “Preserve canvas” checkbox at the top of the generator panel
- [ ] Pick Sunset Scene, click Generate Scene
- [ ] Expected: sunset draws on top, AND the function plot’s scroll-left animation is still running (this was broken before — function plot would freeze)
1.4 — Magic empty-canvas + repeat-generator avoidance (Bug 0)
- [ ] Clear canvas
- [ ] Click Magic button 5 times in a row
- [ ] Expected: each click picks a DIFFERENT background generator (not
cornerAccents5 times in a row)
1.5 — Magic Interactive checkbox tooltip (Bug 4)
- [ ] In the generator panel, pick a generator that has the
interactiveparameter (e.g. drawConcentricRings) - [ ] Hover over the “Interactive” label (with the small ⓘ glyph next to it)
- [ ] Expected: tooltip “Makes all generated items selectable and draggable…” appears immediately (no 1-second delay)
- [ ] Cursor should change to
helpon hover - [ ] On a touch device: tap and hold should show the tooltip
1.6 — Preserve checkbox tooltip + position
- [ ] Open generator panel
- [ ] Verify the “Preserve canvas” checkbox sits ABOVE the dynamic knobs (no scrolling needed to find it)
- [ ] Hover over “Preserve canvas” label
- [ ] Expected: tooltip appears immediately explaining what preserve does
- [ ] ⓘ glyph visible next to the label
1.7 — Callback queue diagnostics (Bug 1 backstop)
- [ ] Open browser console
- [ ] Verify the callback queue cap is 500: console should show no errors at moderate workloads
- [ ] If you can artificially add many callbacks (e.g. spam Magic), expect a
Callback queue at X/500 (Y%)warning at 80% capacity, and a top-prefix dump if it ever hits 500
1.8 — Function plot animation singleton fix
- [ ] Run Function Plot with Scroll Left animation
- [ ] Enable Preserve canvas
- [ ] Run Sunset Scene (or any other generator) on top
- [ ] Expected: function plot’s scroll-left animation continues running (not frozen)
- [ ] This was the original regression — the function plot’s animation callback used to read from a shared singleton that got overwritten
Phase 2 — Measurement system
2.1 — MeasurementSystem lazy load
- [ ] Console:
app._initialized.has('measurementSystem')returnsfalse(not loaded yet) - [ ] Console:
app.setRulersEnabled(true) - [ ] Console:
app._initialized.has('measurementSystem')now returnstrue - [ ] Console:
app.measurementSystem.getVisibility()returns{ rulers: true, grid: false, dimensionReadout: false }
2.2 — Settings UI panel (top of canvas size dropdown)
- [ ] Click the canvas size button at the bottom-left of the editor
- [ ] Expected: at the top of the dropdown, “Display Units” section with 6 unit picker buttons (px / mm / cm / m / in / ft)
- [ ] Below that, “Measurement Tools” section with 5 checkboxes: Rulers, Unit Grid, Snap to Grid, Dimension Readout, Alignment Guides
2.3 — Unit picker
- [ ] Click mm button →
app.getCanvasUnit().unitsreturns'mm', button highlights indigo - [ ] Click in → switches to inches, button highlights
- [ ] Click px → returns to pixel mode
2.4 — Rulers
- [ ] Toggle Rulers on
- [ ] Expected: top + left ruler bars appear with tick marks and labels
- [ ] Switch units to mm — labels should now show mm values
- [ ] Zoom in (mouse wheel or pinch) — ruler tick spacing adapts; labels remain readable
- [ ] Pan the canvas — rulers follow
- [ ] Toggle Rulers off — rulers disappear
2.5 — Unit-aware grid
- [ ] Toggle Unit Grid on with units =
mm - [ ] Expected: grid lines appear (major + minor)
- [ ] Major lines should align with the ruler’s major ticks
- [ ] Minor lines should be 5x denser (1/5 of major spacing)
- [ ] Switch units to
px→ grid disappears (px mode = no unit grid) - [ ] Switch back to
mm→ grid returns
2.6 — Snap to grid
- [ ] Enable Unit Grid + Snap to Grid with units =
mm - [ ] Drag an item across the canvas
- [ ] Expected: item snaps to nearest minor tick (1mm increments)
- [ ] Hold Shift while dragging
- [ ] Expected: item snaps to major ticks instead (1cm increments)
2.7 — Dimension readout HUD
- [ ] Toggle Dimension Readout on
- [ ] Click an item to select it
- [ ] Expected: small HUD appears in canvas top-right showing
W × H units(e.g.120 × 80 mm) - [ ] Resize the item — HUD updates live
- [ ] Deselect — HUD hides
2.8 — Alignment guides toggle
- [ ] Verify alignment guides default ON: drag an item near another item → pink/cyan snap lines appear
- [ ] Toggle Alignment Guides off
- [ ] Drag again → no snap guides
- [ ] Toggle back on → guides return on next drag
Phase 3 (partial) — Ecosystem interop
3.1 — glTF / GLB export
- [ ] Console:
app.createObject3D('cube', { x: 0, y: 0, z: 0, size: 100 })(creates a cube) - [ ] Console:
app.createObject3D('sphere', { x: 150, y: 0, z: 0, radius: 50 })(creates a sphere) - [ ] Console:
app.exportGLB({ download: true, filename: 'test.glb' }) - [ ] Expected: browser downloads
test.glb - [ ] Open the file in Blender (File → Import → glTF 2.0)
- [ ] Expected: the cube and sphere appear at the correct positions, colored correctly
- [ ] Optional: open in https://gltf.report or Three.js editor to verify it parses
3.1b — PNG sequence export
- [ ] Load any animated template (e.g. Solar System or any keyframe scene)
- [ ] Console:
await app.exportPNGSequence({ duration: 3, fps: 30, download: true }) - [ ] Expected: browser downloads
pinepaper-frames.zip - [ ] Open the zip — should contain 90 files named
frame_00001.pngthroughframe_00090.png - [ ] Extract and open the first frame in any image viewer — should be a clean PNG
- [ ] Optional Blender VSE test: File → New → Video Editing → Add → Image/Sequence → select all extracted frames → strip should appear with all frames in correct order
- [ ] Optional DaVinci Resolve test: drag the extracted folder into the media pool → frames should auto-import as a sequence
3.1c — Lottie round-trip
- [ ] Load a simple template (e.g. Solar System or any keyframe scene)
- [ ] Console:
const lottie = app.exportLottie(); JSON.stringify(lottie).length - [ ] Expected: non-empty Lottie JSON, layers/shapes present
- [ ] Save the JSON to a file:
const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([JSON.stringify(lottie)], {type:'application/json'})); a.download='test.json'; a.click() - [ ] Optional Bodymovin viewer test: open https://lottiefiles.com/preview, drag the JSON → should render
- [ ] For full limitations see
docs/LOTTIE_COMPATIBILITY.md— gradients, masks, effects are documented as out-of-scope
3.1d — SVG import metadata preservation
- [ ] Find any SVG file with a
viewBoxand anidattribute on the root<svg> - [ ] Drag-and-drop or import via the editor’s add-image flow
- [ ] Console: select the imported item →
app.selectedItems[0].data.svgViewBoxshould be non-empty - [ ] Console:
app.selectedItems[0].data.svgIdshould match the SVG’s root id - [ ] For full limitations see
docs/SVG_COMPATIBILITY.md— SMIL, filters, patterns are documented as not supported on import
3.1e — BVH motion capture export
- [ ] Load a template that uses the rigging system (e.g. a humanoid or quadruped scene)
- [ ] Console:
const skelId = [...app.riggingSystem.skeletons.keys()][0] - [ ] Console:
app.exportBVH(skelId, { download: true, filename: 'test.bvh' }) - [ ] Expected: browser downloads
test.bvh(text file) - [ ] Open the file in a text editor — should contain
HIERARCHYblock at the top withROOT+ nestedJOINTblocks, thenMOTIONblock withFrames:andFrame Time:lines and per-frame channel rows - [ ] Optional Blender test: File → Import → Motion Capture (.bvh) → select the file. Skeleton should appear in the viewport. Note: PinePaper is 2D so Z is always 0; you may need to flip Y axis on import (Blender import settings)
- [ ] If the skeleton has saved poses, console output should show one frame per pose (else a single bind-pose frame)
- [ ] Multi-root warning: if your skeleton has 2+ root bones, console should log
[BVHExporter] Skeleton has N root bones; only the first will be exported
3.2 — Canvas size picker shows natural units
- [ ] Open canvas size dropdown
- [ ] Find A4 Portrait in the Print category
- [ ] Expected: dimensions display as
210 × 297 mm(NOT2480 × 3508pixels) - [ ] Find US Letter Portrait → expected
8.5 × 11 in - [ ] Find Tabloid → expected
11 × 17 in - [ ] Pick A4 → the canvas size label in the toolbar shows
210 × 297 mm - [ ] Pick Instagram Post → label shows pixel dimensions (Instagram is a px-mode preset)
Phase 4 — “3D” → “Perspective” rename
4.1 — about.html
- [ ] Visit
/about.html - [ ] Find Card 15 (the v0.4 perspective rendering card)
- [ ] Expected: title reads “Perspective Rendering”, NOT “3D Projection”
- [ ] Description mentions “Project primitives… onto the canvas”
4.2 — Locale strings
- [ ] In the language switcher, switch to a non-English language (Spanish, French, etc.)
- [ ] Visit
/about.htmlin that language - [ ] Expected: Card 15 title is also “Perspective Rendering” (English fallback in all 52 locales)
4.3 — Template name
- [ ] Open the templates panel
- [ ] Find the Perspective Showcase template (was “3D Projection Showcase”)
- [ ] Expected: new name displayed
- [ ] Search the templates for “3D” — the template should still be findable (tags include both “3d” and “perspective” for discoverability)
Phase 5 — InteractionSystem persistence
5.1 — Save/load round-trips behaviors
- [ ] Load a game template (e.g. one that uses
flickableorphysics_body) - [ ] Verify items respond to clicks/drags/physics
- [ ] Save scene
- [ ] Reload editor (refresh)
- [ ] Load the saved scene
- [ ] Expected: items still respond to clicks/drags/physics (behaviors restored)
5.2 — Undo/redo round-trips behaviors
- [ ] Load the same game template
- [ ] Make a change (move an item)
- [ ] Cmd+Z (undo)
- [ ] Expected: items still respond to interactions (the audit found this was previously broken — undo silently stripped behaviors)
- [ ] Cmd+Shift+Z (redo)
- [ ] Expected: still works
Phase 9 — Animation parity & accessibility
9.1 — popOut exit animation
- [ ] Open animation panel for an item
- [ ] Set entry animation =
popIn - [ ] Set exit animation =
popOut(this was missing before v0.5) - [ ] Expected:
popOutis now in the dropdown - [ ] Play the timeline → item pops in then pops out
9.2 — lightSpeedOutLeft exit animation
- [ ] Set entry =
lightSpeedInLeft - [ ] Set exit =
lightSpeedOutLeft(was missing — onlylightSpeedOutRightexisted) - [ ] Expected:
lightSpeedOutLeftis in the dropdown, plays correctly (item exits to the left, mirror of OutRight)
9.3 — prefers-reduced-motion
Test via OS setting:
- [ ] Enable reduced motion at the OS level:
- macOS: System Settings → Accessibility → Display → Reduce motion
- Windows: Settings → Accessibility → Visual effects → Animation effects (off)
- Linux GNOME: Settings → Accessibility → Reduce animation
- [ ] Reload the editor
- [ ] Load a template with shake / wobble / bounce / jelly animations
- [ ] Expected: items appear in their static baseline pose, no jittering
- [ ] Console:
app.prefersReducedMotion()returnstrue - [ ] Console:
app.isReducedMotionActive()returnstrue - [ ] Console:
app.setRespectReducedMotion(false)→ animations resume even with OS setting on - [ ] Console:
app.setRespectReducedMotion(true)→ animations suppress again - [ ] Disable OS reduced motion → all animations should resume
Other v0.5 fixes
O.1 — morphs_to denoising regression
- [ ] Load Morph Cross-fade Demo template
- [ ] Expected: four rows visible — PointText (HELLO/WORLD), Shapes (circle/star), Letter Collage (XOXO/MOMO), CompoundPath (figure-8/triangle)
- [ ] All four rows show BOTH source AND target items (this was broken — only target was visible)
- [ ] Press K to play the timeline
- [ ] Watch sources morph into targets
- [ ] Loop should restart cleanly with sources visible again
O.2 — Featured blog visibility
- [ ] Visit
/knowledge-hub(URL renamed from/blogin v0.5) - [ ] Expected: the “Point Paper, Mathematics, Measurement, and PinePaper” post is the FEATURED post at the top
- [ ] Click into it — full post loads
- [ ] Verify the closing paragraph mentions “free, and it will always be free” + edge models / Gemma
- [ ] Verify the section about KG says “is already in place” (not “coming”)
- [ ] Old URL redirect: Visit
/blog— should 301-redirect to/knowledge-hub/(SEO preservation)
O.2b — 7 new locales + 59-language claim
- [ ] Visit
/(homepage) - [ ] Find the “Global by Default” card under principles
- [ ] Expected: description says “Support for 59 languages” (not 41), explicitly mentions Sindhi, Swahili, Amharic
- [ ] Open the language selector dropdown — should have an “African Languages” optgroup with Swahili, Hausa, Yoruba, Igbo, Amharic, isiZulu
- [ ] Sindhi (سنڌي) appears as a top-level option above the optgroups
- [ ] Visit
/about.html— Languages Supported stat shows 59 (was 52) - [ ] Pick Swahili from the language switcher → page reloads at
/sw/with Swahili UI strings (footer/nav at minimum) - [ ] Repeat for Sindhi (
/sd/), Amharic (/am/), Igbo (/ig/) - [ ] Sindhi RTL: verify text flows right-to-left in the Sindhi page
- [ ] Amharic Ge’ez script: verify አማርኛ glyphs render (system fallback fonts OK)
O.2c — Licensing & Open Source Credits section
- [ ] Visit
/about.htmland scroll to the bottom - [ ] Find the “Licensing & Open Source Credits” section above the footer
- [ ] Expected — “PinePaper Studio itself” sub-section: lists 4 items:
- Application code → Proprietary Freeware (links to /LICENSE and /terms-of-service.html)
- PinePaper Ontology → CC0 1.0 Public Domain (links to /ontology/)
- MCP Server → MIT (links to github.com/pinepaper/mcp-server)
- PineWidget runtime → Embeddable
- [ ] Expected — vendor sub-section: lists Paper.js (MIT), math.js (Apache-2.0), opentype.js, planck-js, d3-geo, d3-geo-projection, topojson-client, gif.js, jsPDF, @sentry/browser with correct license labels
- [ ] Each link points to the project’s repo or homepage (verify a couple)
O.2d — LICENSE file accessible
- [ ] Visit
/LICENSEdirectly in the browser - [ ] Expected: plain-text license file displays inline (not download), starts with “PinePaper Studio — Proprietary Freeware License”
- [ ] Section 3 lists the carve-outs: MCP Server (MIT), Ontology (CC0), PineWidget (embeddable)
- [ ] package.json
"license"field is"SEE LICENSE IN LICENSE"(not the old invalid"Freeware")
O.2e — Source-map exposure check (CRITICAL)
- [ ] Open browser DevTools → Sources tab on
/editor - [ ] Expected: code is obfuscated/mangled — no readable function/variable names
- [ ] Expected: no
.mapfiles load in the Network tab - [ ] Console:
document.querySelectorAll('script[src]')and inspect.src— none should reference.mapfiles - [ ] Run
find dist -name '*.map'— should return nothing - [ ] Run
grep -r sourceMappingURL dist/js/ | head— should return nothing - [ ] Debug protection: open DevTools while editor is loaded — should trigger debug protection (page may freeze or redirect)
- [ ] Console: any direct
console.log()calls in app code should produce no output (drop_console + disableConsoleOutput)
O.3 — Function plot y-axis (axis: 'y')
- [ ] Open Function Plot generator
- [ ] Expected: an
Axisdropdown is in the params withxandyoptions - [ ] Set Axis =
y - [ ] In the Expression field, type
sin(y * 3) - [ ] Click Generate
- [ ] Expected: a vertical sine wave (oscillates horizontally as y varies)
- [ ] Set animation = scroll-left → the wave should scroll vertically over time
Critical regression checks
Tooltips
- [ ] Native HTML title attribute is NOT used — hover tooltips should appear with no delay (~700ms native delay would be obvious)
- [ ] Touch tap shows tooltip on mobile/tablet
- [ ] Esc key dismisses tooltip
- [ ] Tooltip auto-positions — hover near the bottom edge of the viewport, tooltip flips above
Mobile
- [ ] On a mobile device or DevTools mobile emulation:
- [ ] Pinch-to-zoom works on the canvas
- [ ] Three-finger pan works
- [ ] Tap interactions work (drag items, select items)
- [ ] Mobile canvas size buttons work (in the More tab)
Privacy / no data leaving the machine
- [ ] Network tab in DevTools, filter by
xhrandfetch - [ ] Use the editor for 30 seconds
- [ ] Expected: no requests to PinePaper backend for canvas data (everything runs in the browser)
- [ ] The MCP server (if running) is local —
localhost, not a remote URL
Memory + audit verification
Documentation freshness
- [ ]
CLAUDE.mdChangelog has v0.5.0-beta section (or task to add) - [ ]
MEMORY.mdindex lists all v0.5 memory files (ecosystem positioning, dual corpus, BYO-AI, etc.) - [ ] Featured blog post
2026-04-08-point-paper-mathematics-measurement-and-pinepaper.mdhasdraft: false(not draft)
v0.5 deliverables verified
- [ ] All 27 v0.5 task list items are completed (or explicitly deferred to v0.6)
- [ ] No
TODO/FIXMEcomments added in v0.5 work without a corresponding task entry
Sign-off
If every box above is checked, v0.5.0-beta is ready to merge to main.
Final pre-merge checklist:
- [ ]
npx jest --silent— full test suite passes - [ ]
npx webpack --config webpack.config.prod.js— production build clean - [ ]
npm run preview— preview loads with no console errors - [ ] Manual smoke test of the headline features: unit system, rulers, generator preserve, morph demo, glTF export
- [ ] CLAUDE.md changelog updated with v0.5.0-beta entries
- [ ] Featured blog post date updated to actual release day
- [ ] Filename of featured blog post matches the release date
- [ ] Git commit signed off with v0.5.0-beta tag
- [ ] You verbally say “ship it”
Last updated: 2026-04-09 — generated from the v0.5 development task list and changelog.