Review: Canvas view with activity-based card sizing

review-1406-2026-06-09-r2 Review

review ready

Verdict: APPROVED

Re-review of board item #1406 after refinement. Previous review review-1406-2026-06-09 returned NEEDS_REFINEMENT with 4 items. All 4 have been addressed.

Previous Findings — Resolution

  • [x] story:canvas-layout not in user-stories — acknowledged as cross-cutting gap, not blocking. Precedent set by #36 and #41 reviews. Accepted.
  • [x] Undocumented dependency on #34 (feature_flags) — FIXED. Feature Flag section now says Flag: none. Canvas ships as default layout; flag gate deferred to when #34 lands. Matches #36's approach.
  • [x] Missing AC for initial placement — FIXED. Two new ACs added: auto-placement of links with null canvas_x/canvas_y in a grid pattern, and new links get auto-placed at sensible default. Migration columns specified as nullable.
  • [x] Pan/zoom underspecified — FIXED. AC now specifies "click-drag on empty background" for pan and "scroll wheel" for zoom. Constraints add "No pinch-to-zoom required (desktop-first)."

Template Completeness

  • [x] Type — Feature
  • [x] Lineage — depends on #36, prerequisite for #41
  • [x] Repo — ldraney/palinks
  • [x] User Story
  • [x] Context — includes architecture decision (HTML/CSS vs canvas/WebGL)
  • [x] File Targets — 6 targets + 1 exclusion
  • [x] Feature Flag — "none" with rationale
  • [x] Acceptance Criteria — 7 items
  • [x] Test Expectations — 4 tests + run command
  • [x] Constraints — 6 constraints
  • [x] Checklist
  • [x] Related

Traceability

  • [x] story:canvas-layout label — present on board item #1406
  • [ ] story note MISSING — story:canvas-layout not listed in project-palinks user-stories section. Cross-cutting gap acknowledged in prior reviews (#36, #41). Not blocking per precedent.
  • [x] arch:palinks label — present on board item
  • [ ] arch note MISSING — no arch-palinks note exists. Board-wide gap acknowledged in #36 review. Not blocking.
  • [x] Forgejo issue — ldraney/palinks#40, open

File Targets

  • [x] db/migrate/*_add_canvas_position_to_links.rb — new file to create. Verified no canvas_x/canvas_y columns exist in current schema. Links table has position (integer) but no spatial columns.
  • [x] app/models/link.rb — verified exists (8 lines). Has position/favorite scopes, no click logic. Correct target for canvas_scale method.
  • [x] app/views/links/index.html.erb — verified exists (23 lines). Currently renders div.link-grid with data-controller="sortable". Correct target for canvas container replacement.
  • [x] app/views/links/_link.html.erb — verified exists (40 lines). Renders div.link-card with sortable targets. Correct target for inline position/scale styles.
  • [x] app/javascript/controllers/canvas_controller.js — new file to create. Controllers directory exists with sortable_controller.js as pattern reference.
  • [x] app/assets/stylesheets/application.css — verified exists. Has .link-grid (line 142) and .link-card styles. Correct target for canvas container styles.
  • [x] Do NOT touch: sortable_controller.js — verified exists (230 lines). Correct exclusion.

Repo Placement

Correct. Issue filed on ldraney/palinks, all file targets within the same repo. Single-repo change.

Dependencies

  • #36 (clicks table) — currently in_progress on board-palinks (item #1400). This ticket depends on it for click count data. Dependency is documented in both Lineage and Constraints. #36 must merge first.
  • #34 (feature_flags) — in backlog (item #1398). No longer a dependency. Feature Flag section correctly says "none" and ships ungated. Flag gate deferred to when #34 lands.
  • #41 (link groups) — in todo (item #1407). Downstream dependency: depends on canvas existing. Not a blocker for this ticket. Correctly documented in Lineage.

Acceptance Criteria

  • [x] "Links render on a canvas with absolute positioning instead of a grid" — testable, clear
  • [x] "Card size scales proportionally to click count (min scale 0.7, max scale 1.5)" — testable with specific bounds
  • [x] "Cards can be dragged to new positions, position persists on reload" — testable via integration test
  • [x] "Canvas supports pan (click-drag on empty background) and zoom (scroll wheel)" — testable, interaction methods specified
  • [x] "Links with zero clicks render at base size (scale 1.0)" — testable edge case
  • [x] "Links with no canvas_x/canvas_y are auto-placed in a grid pattern on first render" — testable, addresses existing-links migration
  • [x] "New links get auto-placed at sensible default position" — testable, addresses post-deployment creation
All 7 ACs are verifiable by an agent. Test expectations cover unit tests for scale calculation, nullable columns, and integration tests for drag persistence and auto-placement.

Blast Radius

  • .link-grid CSS class used in 3 places: application.css (lines 142, 450) and index.html.erb (line 7). Replacing with canvas container is safe — no other views reference it.
  • sortable_controller.js (230 lines) manages current grid ordering. Correctly excluded. With "Flag: none," the canvas replaces the grid outright — no coexistence concern since the sortable controller just won't be attached to the new DOM.
  • No clicks table or click_count references exist yet — confirms dependency on #36.
  • No other services or repos affected. palinks is a standalone Rails app.

Decomposition Assessment

6 file targets in 1 repo, 7 acceptance criteria. At the boundary of the 5-minute thresholds but all targets are tightly coupled in a single feature unit: migration + model method + view changes + Stimulus controller + CSS. Natural cohesion. No decomposition needed.

Recommendation

No action needed. All previous NEEDS_REFINEMENT items resolved. Scope is solid.