Skip to content

Make a shared link render as the museum, and lead the README with it - #24

Merged
yktsnd merged 20 commits into
mainfrom
claude/kinetiflux-specification-9vey74
Jul 28, 2026
Merged

Make a shared link render as the museum, and lead the README with it#24
yktsnd merged 20 commits into
mainfrom
claude/kinetiflux-specification-9vey74

Conversation

@yktsnd

@yktsnd yktsnd commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

The page already carried Open Graph tags and a genuinely striking OG image, but no card type — so on X a shared link rendered as a bare title and URL instead of the image. That image is most of the reason anyone clicks, so this was quietly throttling every share.

  • twitter:card = summary_large_image, plus twitter:title / description / image / image:alt. These are read by Slack, Discord and several readers as well, so the benefit is not X-specific.
  • og:url, og:site_name, og:locale, og:image:width / height / alt — scrapers use the dimensions to lay the card out without refetching the image, and the alt text is what screen-reader users hear when the card is shared.
  • A canonical link, since the site resolves at both the bare and trailing-slash forms of its Pages URL.
  • README now opens with that same banner image instead of the small 72px mark, since it is the image most visitors will already recognize from a shared link — explicitly labelled as brand art so it is never mistaken for a screenshot, with the real hero screenshot immediately below it.

Verified the <head> tags survive the Vite build by grepping dist/index.html, not just the source.

Linked issue

None.

Impact checklist

Does this change affect any of the following?

  • Equations (model definitions, rate laws, parameters)
  • Solver behavior (integration method, step size, numerical stability)
  • Visual semantics (what colors/positions/motion communicate)
  • Accessibility (assistive technology, keyboard, contrast, motion)
  • Performance
  • Documentation

Accessibility only in that the shared card now carries alt text; nothing in the application UI changed.

Verification

  • npm run check passed — 31 test files, 239 tests, plus format, lint, typecheck, build
  • npm run test:e2e was run — not applicable, <head> metadata and README only, no UI behavior changed
  • Screenshots attached — not applicable, no visual output changed

Note that the rendered social card cannot be verified from this sandbox — validating it requires X's or Facebook's scraper fetching the live URL. Worth a check with a card debugger after this deploys.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi

claude added 20 commits July 17, 2026 10:08
Lay the structural foundation for the presentation redesign: a single-row
top rail (brand, ghost preset/rate-view tabs, reduced-motion/legend/exhibit
toggles) replaces the bordered header, a museum-caption block carries the
first impression, and the timeline scrubber becomes a dedicated time axis
sharing horizontal layout fractions with the trace strips (src/charts/
trace-layout.ts) so the playback thumb lines up with the trace cursor.

Also: legend now defaults closed (the caption carries first impression),
adds the exhibitMode store flag, and introduces useExhibition — fullscreen
toggle, "e" key, ?exhibit=1, auto-advance-on-completion sequencing, and
UI-recession-on-idle — wired into App.tsx ahead of the network/traces/
overlay restyle that consumes these hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
Network: vessels become open-top instrument columns (hairline sides +
baseline, no top edge, meniscus as the brightest element), channels become
filaments (permanent structural hairline + translucent rate band, one
chevron per lane), the feed sparkline loses its frame, and particles ease
in/out over the first/last 8% of travel at r=2.6 with a calmer 1.4s
crossing (particle-engine.ts emission math untouched).

Charts: trace strips lose all card/figure chrome, x-axis ticks move to the
shared time axis, and each strip gains a right-edge live-readout column
(readout-quantities/readout-rates) built on the shared trace-layout.ts
fractions so the playback cursor lines up with the axis thumb.

Overlays: the parameters panel becomes a right-side slide-over drawer with
focus management and Esc-to-close; the legend becomes a centered "how to
read" overlay (closed by default); the invalid-state takeover gets a
one-line mono diagnostic and a muted-signal-red kicker.

Integration fixes found while wiring these together: advance() now clamps
time at 0 (a browser's first rAF timestamp can rarely precede the wall-clock
reference captured when playback starts, which produced a fleeting negative
time — harmless before, but the new SVG-coordinate readout lacked the old
readout row's implicit NaN-to-"—" guard); the readout row collision-nudge
is now a proper forward+backward+compress pass instead of a forward-only
push that could clip at the strip's bottom edge; and the readout column
was widened so the longest process label no longer collides with its
right-aligned value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
…tion

Legend now opens via toggle instead of being visible on load; the
directional/net rate-decomposition check now reads the rates readout
column (data-testid readout-rates) instead of the removed .chart-legend
row. Adds tests/e2e/exhibition.spec.ts covering the toggle (button +
"e" key + ?exhibit=1) and confirming playback keeps advancing while
exhibiting — full-length auto-advance timing (60s trajectory + 6s hold +
fades + caption) is validated manually against a 4x-speed build rather
than in CI, where it would dominate suite runtime.

Also applies prettier formatting picked up by `npm run check` across a
few files touched by prior workstream commits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
Update docs/visual-language.md, docs/architecture.md, docs/accessibility.md,
AGENTS.md's repo map, README.md, and CHANGELOG.md to describe what's
actually in the code now: instrument-column vessels/basin, filament
channels, chrome-free trace strips with a live readout column, the time
axis as scrubber, the museum caption, and the new src/features/exhibition/
module (auto-advance + UI recession + focus management).

Fix found while capturing exhibition-mode screenshots for review: clicking
the rail's exhibit-toggle button left it focused, so the UI-recession CSS's
`:focus-within` reveal rule (meant to guarantee a focused control is never
hidden mid-interaction) kept the whole rail permanently visible and the
recession feature never engaged. useExhibition's enable() now blurs the
active element on entry, matching a kiosk mode's actual intent (hand off to
the display) and restoring the 4s-idle fade documented in accessibility.md.

Also recaptures docs/media/{hero,wide,responsive}.png and public/og.png
against the finished UI (adapts scripts/capture-screens.mjs to select the
matching preset per shot: steady-feed for hero, tidal-feed for wide).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
…e caption wrap

Creative Director review of the Quiet Instrument redesign requested four
surgical refinements:

- Give the topmost y-tick label on each trace strip headroom (top margin
  10 -> 16 viewBox units) so it no longer clips against the strip edge.
- Tighten the blank band between the vessel labels and the quantities
  strip: cap .system-stage at ~55vh instead of letting it absorb all
  leftover flex space, and let .field collect any remaining slack above
  the system (justify-content: flex-end) so the instrument block stays
  anchored near the viewport bottom.
- Lower the feed sparkline closer to the feed filament (offset 78 -> 56)
  so it reads as an annotation of the feed line, not a floating rule.
- Let the mobile caption tagline wrap to two lines instead of truncating
  with an ellipsis at narrow widths.

Recaptures docs/media/{hero,wide,responsive}.png and public/og.png.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
Version the museum relaunch as 1.0.0, close out the CHANGELOG's
[Unreleased] block as [1.0.0], and add a CITATION.cff so the work is
citable. Resolve the high-severity transitive brace-expansion advisory
via npm audit fix (no major bumps), and drop the legacy
__KINETIFLUX_STORE__ global now that nothing references it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The canonical repository guide still described only the retired
single-instrument app, so contributors and coding agents were being
pointed at a system that no longer exists. Rewrite it against the
shipped code: per-file museum map, the portrait pipeline as an
architectural boundary, museum-wide numerical invariants cited to the
tests that enforce them, semantic-mapping visual invariants, the
authoring commands, and the workflow for adding a work. Update ADR
0001-0003 prose so present-tense product references say DynaMusium
while their historical decisions stay historical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
Recapture every README image from the built museum app instead of the
retired single-instrument screens, and rewrite the README around them.

- capture-screens.mjs now deep-links each shot into a specific work and
  mode via ?work=&mode=&preset=, waits on a real content selector, and
  lets playback develop so no frame is captured blank.
- New shots: hero (Lorenz Atmosphere, Observe), collection (permanent
  collection grid), study (Double Pendulum data table + citation),
  exhibit (Kuramoto, chrome receded), responsive (390px entrance).
- Drop wide.png and public/og.png: both showed the retired instrument
  and neither was referenced any more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The Quiet Instrument redesign that this branch used to carry was merged
into main (8ef0fa8) and has since been superseded by the museum work in
#14, #15, #16, and #18. This merge records that history as an ancestor
so the branch can move forward without a force push; it contributes no
file changes -- the tree is exactly the current main-based work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
Neither job set timeout-minutes, so they inherited the 360-minute
default. A run of this workflow hung for 26 minutes inside
"playwright install --with-deps chromium" -- normally about a minute --
with no way to fail fast. Cap check at 15 minutes and e2e at 20, both
comfortably above their observed runtimes (about 1 and 3 minutes), so a
stalled download surfaces as a failure instead of an open-ended wait.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The security policy still advertised 0.1.x as supported after 1.0.0
shipped, which would tell a reporter the retired single-instrument
release is maintained. Point the table at 1.0.x and note that, for a
static client-side site, the remedy for any advisory is a redeploy
from main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The previous tip of this branch was squash-merged as dfbab21 (#19).
Recorded as an ancestor so the branch moves forward without a force
push; contributes no file changes.
…tion

A visitor who opens SOURCE and finds a different paper has no reason to
trust anything else the museum says. Five links were wrong, and two
conservative works were visibly not conserving.

Source links, each re-resolved through the publisher's own metadata:

- double-pendulum, standard-map, brusselator: the DOIs resolved to
  entirely unrelated papers -- an attractor-properties note, a paper on
  Coulomb gauge QED, and a thermal-diffusion coefficient study.
- hohmann-transfer: the NTRS record was a soft 404 (its API returns
  404 and the report is not indexed there at all); replaced with the
  De Gruyter DOI for Hohmann 1925.
- friedmann-lemaitre: displayed the 1931 MNRAS English title while
  linking Lemaitre's 1927 French original; now links the paper named.
- lotka-volterra: named a different Volterra paper than it linked to.

Conservation. Both works integrated at one RK4 step per displayed
frame. RK4 is not symplectic, so its truncation error appeared on
screen as energy the system never had: the double pendulum drifted up
to 53% and the three-body system grew up to 165% over a run, which for
chaotic systems means the trajectory shown was an artifact of the step
size. `rk4` now separates step size from frame count, and both kernels
substep -- measured drift falls to below 1e-4 relative everywhere in
their parameter ranges.

Other claims that outran the evidence:

- The Lotka-Volterra equation card showed 0.5 and 0.8 where the kernel
  integrates 0.45 and 0.9.
- Friedmann-Lemaitre labelled its matter term with the radiation
  symbol, in both the equation and the slider.
- The double pendulum's "1746 - Daniel Bernoulli" byline could not be
  corroborated from any source; it now names the cited analysis.
- FitzHugh-Nagumo carried one 1961 date, implying Nagumo co-authored
  FitzHugh's solo paper rather than publishing separately in 1962.
- FPUT cited only a report number; Mary Tsingou is now credited.

Guards, so none of this can regress silently:

- `npm run cite:verify` resolves every DOI through CrossRef and checks
  the registered title against the displayed one, maintaining a ledger
  that the offline tests assert against. Changing a citation without
  re-verifying now fails the build. The matcher's own known-wrong cases
  are pinned as tests.
- `tests/museum/conservation.test.ts` measures energy from real kernel
  output across each work's presets and slider extremes.

Screenshots recaptured from the corrected build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The previous tip was squash-merged as 4f42551 (#21). Recorded as an
ancestor so the branch moves forward without a force push; contributes
no file changes.
Not one slider in the museum said what it was measured in. "Gravity
g = 9.81" gave a visitor no way to tell whether that was m/s², a
nondimensionalized quantity, or an arbitrary model number -- and roughly
half the catalog is nondimensionalized, so the ambiguity was real rather
than cosmetic.

`WorkParameter` gains an optional `unit`, rendered as a quiet suffix
after the value. Every parameter of all thirty built-in works declares
one, chosen by reading each kernel rather than by assuming SI: works
whose equations are nondimensionalized or uncalibrated say
`dimensionless` or `model unit` instead of borrowing a physical unit
their kernel does not support. Inventing a plausible-looking unit would
have been the same class of error as a citation that opens the wrong
paper.

The field is optional so manifests written before it existed still
validate, but a test requires one on every built-in parameter, so a new
work cannot ship without declaring what it measures.

Notable calls, all traced to kernel evidence:

- hodgkin-huxley uses the canonical squid-axon parameterization
  (E_Na +50, E_K -77, E_leak -54.4 mV; g_K 36, g_L 0.3), so uA/cm^2 and
  mS/cm^2 are the units those constants are stated in.
- standard-map's momentum enters as theta_{n+1} = theta_n + p, so it
  carries radians per iteration rather than a bare angle.
- lorenz, ising, brusselator, oregonator and the other reduced systems
  are genuinely dimensionless and are labelled as such.

Screenshots recaptured showing the rendered units.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
…ount

v1.0.0 shipped five source links that opened the wrong work and two
conservative systems whose displayed motion came from the integrator.
Both are fixed on main, so the published release needs to be superseded
rather than left as the version a citation would resolve to.

Also records the substepping contract in docs/numerical-method.md. That
document already asserted fixed-step RK4 "is not silently reused for
... Hamiltonian long-time structure", which is precisely what was
happening; it now describes how rk4 separates step size from frame
count, states the drift figures before and after, and requires a
conserved quantity to be measured from real kernel output rather than
asserted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The page carried Open Graph tags but no card type, so X rendered a
shared link as a bare title and URL rather than the museum image --
which is most of the reason anyone clicks through. Adds the Twitter
card tags (also read by Slack, Discord, and several readers, so this
is not X-specific), plus the og:url, og:site_name, og:image dimensions
and alt text that scrapers use to lay the card out without refetching.

Also adds a canonical link, since the site is reachable at both the
bare and trailing-slash forms of its Pages URL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
The README opened with the small 72px mark and went straight to a
screenshot. Leads with the same og-dynamusium.png banner used for
link previews (wordmark, Lorenz attractor, starlit hall) -- the image
most people will already recognize from a shared link -- labelled
explicitly as brand art so it is never mistaken for a screenshot, with
the real hero screenshot immediately below it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7ZWDDttYd82ZAYC3cnbi
@yktsnd yktsnd changed the title Make a shared link render as the museum, not a text stub Make a shared link render as the museum, and lead the README with it Jul 28, 2026
@yktsnd
yktsnd merged commit 37baa4b into main Jul 28, 2026
2 checks passed
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.

2 participants