Skip to content

fix(viewer): recompute related traits on client-side navigation#258

Open
jamesdabbs wants to merge 3 commits into
mainfrom
fix/255-stale-traits-on-navigation
Open

fix(viewer): recompute related traits on client-side navigation#258
jamesdabbs wants to merge 3 commits into
mainfrom
fix/255-stale-traits-on-navigation

Conversation

@jamesdabbs

Copy link
Copy Markdown
Member

Closes #255

Root cause

Related.svelte derives its rows via $: all = related($traits), which only re-runs when the related prop or the traits store changes identity. Both callers (Spaces/Properties.svelte, Properties/Spaces.svelte) passed hoisted function declarations that closed over their space/property prop — a data dependency Svelte's reactivity can't see. On client-side navigation between two spaces, SvelteKit reuses the mounted component tree: the name/description updated (they read the prop directly) but the traits table never recomputed, so S27's page showed S29's traits — and after a refresh, back-navigation showed the reverse.

Fix

  • Declare the related closures reactively ($:) in both callers, so a new function identity is passed whenever the entity changes.
  • Make the rel tab-link prop reactive on the space page (it was captured once at mount), matching the property/theorem pages.

Regression test

New fixture-only Cypress spec drives the exact flow from the issue: visit a space, click an in-description space link (unpadded id, like the live {S27} links), assert the traits table recomputes, then assert it recomputes again on browser back. To support it, S000001's fixture description now links to {S4}; "Indiscrete" is asserted false for S000001 and true for S000004, so the value icon flips iff the table actually recomputes (client-side deduction can add traits but never contradict asserted ones).

Since live descriptions differ from the fixture (live S000001's description is empty), the spec is declared with a fixtureIt helper and shows as pending in live runs; doc/testing.md documents the convention plus two fixture-mode gotchas discovered while verifying (the bundle is baked in + SSR-injected for localhost builds, so cy.intercept on main.json is inert; the preview server must be restarted after rebuilds).

Verification

  • Spec verified red against the unfixed code (fails exactly at the stale-table assertion, with the S41 page showing S29's trait values) and green with the fix.
  • Full hermetic suite: 17/17 passing.
  • Live suite against topology.pi-base.org: 16 passing / 1 pending / 0 failing (was 16/16 before the new spec).

A follow-up commit on this branch refactors Related to take the anchor entity as data instead of a closure prop, making this bug class unrepresentable.

🤖 Generated with Claude Code

jamesdabbs and others added 2 commits July 6, 2026 18:52
Related.svelte derives its rows via `$: all = related($traits)`, which
only re-runs when the `related` prop or the traits store changes
identity. Both callers passed hoisted function declarations that closed
over their space/property prop, so navigating between two spaces (or
properties) reused the mounted component and kept showing the previous
entity's traits table. Declare the closures reactively so a new function
is passed whenever the entity changes.

Also make the `rel` tab-link prop reactive on the space page, matching
the property and theorem pages.

Adds a fixture-only Cypress regression spec driving the exact flow from
the issue: an in-description space link (unpadded id), asserting the
traits table recomputes, including on browser back. S000001's fixture
description now links to {S4} to support it; "Indiscrete" is asserted
false for S000001 and true for S000004, so the value icon flips iff the
table recomputes (deduction can add traits but never contradict asserted
ones). Verified red against the unfixed code, green with the fix.

Closes #255

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Document the fixtureIt convention for specs that depend on
fixture-pinned content, and two traps when running fixture mode against
a production-style build (also what CI runs): the bundle is baked in and
SSR-injected so cy.intercept on main.json is inert, and the preview
server must be restarted after every rebuild to avoid serving HTML that
references the previous build's hashed chunks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
graphs ce5ec72 Commit Preview URL

Branch Preview URL
Jul 07 2026, 02:04 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
topology ce5ec72 Commit Preview URL

Branch Preview URL
Jul 07 2026, 02:04 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying topology with  Cloudflare Pages  Cloudflare Pages

Latest commit: ce5ec72
Status: ✅  Deploy successful!
Preview URL: https://60b1423f.topology.pages.dev
Branch Preview URL: https://fix-255-stale-traits-on-navi.topology.pages.dev

View logs

Related took a `(traits) => rows` closure prop, leaving its real data
dependency (the space or property being viewed) invisible to Svelte's
reactivity — the trap behind #255, avoidable only by every caller
remembering to re-create the closure reactively. Take the anchor entity
itself as a discriminated-union prop and compute the rows inside
Related: the object literal at the call sites is re-evaluated whenever
the entity changes, so recomputation falls out of ordinary prop flow
and the stale-closure mistake is no longer expressible. This also
subsumes the separate `mode` prop.

Covered end-to-end by the navigation regression spec (17/17 fixture
suite passing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jamesdabbs jamesdabbs requested a review from StevenClontz July 7, 2026 02:12
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.

Navigation problem in pi-base web site

1 participant