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 --silent passes 5815/5815 tests
  • [ ] npx webpack --config webpack.config.prod.js builds without errors (3 size warnings are OK)
  • [ ] npm run preview loads 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.
  • [ ] No setCanvasSize is not a function errors 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 by app.getCanvasUnit() returns { units: 'mm', pxPerUnit: 3.7795... }
  • [ ] app.unitToPx(10) returns approximately 37.79 (10 mm in pixels at 96 DPI)
  • [ ] app.pxToUnit(37.79) returns approximately 10
  • [ ] app.setCanvasUnit('in') then app.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() shows mm again

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 cornerAccents 5 times in a row)

1.5 — Magic Interactive checkbox tooltip (Bug 4)

  • [ ] In the generator panel, pick a generator that has the interactive parameter (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 help on 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') returns false (not loaded yet)
  • [ ] Console: app.setRulersEnabled(true)
  • [ ] Console: app._initialized.has('measurementSystem') now returns true
  • [ ] 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().units returns '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.png through frame_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 viewBox and an id attribute 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.svgViewBox should be non-empty
  • [ ] Console: app.selectedItems[0].data.svgId should 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 HIERARCHY block at the top with ROOT + nested JOINT blocks, then MOTION block with Frames: and Frame 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 (NOT 2480 × 3508 pixels)
  • [ ] 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.html in 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 flickable or physics_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: popOut is 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 — only lightSpeedOutRight existed)
  • [ ] Expected: lightSpeedOutLeft is 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() returns true
  • [ ] Console: app.isReducedMotionActive() returns true
  • [ ] 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
  • [ ] Visit /knowledge-hub (URL renamed from /blog in 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.html and 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 /LICENSE directly 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 .map files load in the Network tab
  • [ ] Console: document.querySelectorAll('script[src]') and inspect .src — none should reference .map files
  • [ ] 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 Axis dropdown is in the params with x and y options
  • [ ] 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 xhr and fetch
  • [ ] 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.md Changelog has v0.5.0-beta section (or task to add)
  • [ ] MEMORY.md index 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.md has draft: false (not draft)

v0.5 deliverables verified

  • [ ] All 27 v0.5 task list items are completed (or explicitly deferred to v0.6)
  • [ ] No TODO / FIXME comments 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.