Skip to content

[Website] Rename the GraphQL Composite Schemas spec to GraphQL Federation and polish the header menus - #10688

Merged
rstaib merged 537 commits into
mainfrom
rst/federation-spec-rename
Oct 25, 2026
Merged

rstaib merged 537 commits into
mainfrom
rst/federation-spec-rename

Conversation

@rstaib

@rstaib rstaib commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Renames the GraphQL Composite Schemas specification to the GraphQL Federation specification across the docs, and brings over the shared website improvements that came out of the federation page work.

  • The Fusion docs and the Nitro CLI fusion reference now link to graphql.github.io/graphql-federation-spec/draft/ instead of the old composite-schemas-spec URL, which now returns 404. This covers every spec link in the composition error table, the directives reference, the FieldSelectionMap notes, and the migration guides, and the old spec issue link now points to graphql/graphql-federation-spec. The Fusion overview and directives reference say "formerly the GraphQL Composite Schemas specification" once, and the Fusion product description reads "GraphQL Federation Gateway".
  • The header mega menus share one open state for mouse and keyboard. Each menu has its own chevron button with aria-expanded, opens and moves with the keyboard, closes with Escape, and stays inside the viewport from 1060 to 1272 px.
  • Product links in the Platform and Developers menus and the cards on the docs index show the product drinks, scaled from one artwork sheet so they keep their relative sizes (ProductArtworkIcon, productArtwork.ts). The protocol card icons on the home page are cropped to their glyphs.
  • HeaderShell is removed, so every route, /products/nitro included, uses the same sticky, tinted header instead of a transparent overlay. The Nitro hero moves up under the header so it still starts at the top of the viewport.
  • ClassificationCard and ClientImpactMatrix move from the Nitro and release-safety pages into shared components, and syntaxTokens moves to src/components. At narrow widths (375 px), the impact matrix wraps the operation count under its bar instead of running into the status column.
  • The Nitro page reads reduced motion through useReducedMotionPreference, which renders the same markup on the server and during hydration.
  • The blog index uses the 7xl page width, and ESLint ignores test-results/.

The composite-schemas-spec links in the C# XML docs and test comments are not part of this PR.

Test plan

  • tsc --noEmit, npm run lint, and npm run format:check pass in website.

…d the column, and drop the service streams

Rewrite withBlurHalo (paint.ts) to stroke the halo paths unfiltered
into an offscreen canvas and composite that canvas once under a
single ctx.filter blur pass, instead of setting ctx.filter before
2100 individual filtered strokes: the two ~8.3s long tasks blocking
the RAF loop are gone (vf2-longtask.cjs shows none over ~100ms) and
tk-budget.cjs now collects the full n=300 (avg 0.931ms, well under
the 4ms budget).

Dim streaks that orbit onto the torus' far side and project within
the column's own screen-space width, on top of the existing
near/far projection factor, so they read as passing behind the dark
column instead of just fading into distance.

Drop the five SERVICE_SPECTRUM dash trails entirely (plasma.ts
projectSpiralDashes/SpiralDash, paint.ts's SERVICE_SPECTRUM loop,
index.tsx's spirals/SPECTRUM_STREAM_COUNT): even as short dashes they
still read as thin straight lines crossing the frame at this camera
distance; the planner's own ruling permits dropping them so the
picture stays slate plus coral.
…ten exposure and feather the band edge

Splits the chamber's static cache into a wall layer (its own bottom
canvas, unchanged) and a column layer (an offscreen canvas stamped
with drawImage every frame), and splits the plasma's cached majority
into far/near halves by orbit angle. Each live frame now composites
wall -> far streaks/glow/helix (desaturated, dimmer) -> column ->
coral tint -> near streaks/glow/helix/bloom/core, so the column's own
tile geometry occludes the far arc from real draw order instead of a
column-width dimming factor (ticket hc-0-wrc.3 comment 206/207).

Gives each streak a phi-based weight: full at the tube's outer
mid-line, near zero at the top/bottom silhouette (feathering the
band's edge instead of a hard cut) and dampened at the inner
mid-line (so a dense inner-limb population no longer fills in the
exact screen area the column is meant to interrupt). Adds a sparse
"stray" population off the tube's own radius. Cuts the plasma stroke
and core alphas so individual streaks stay readable instead of
blowing the band out to white, with a separate mobile density scale
for the much smaller 375px band.

Numbers (see ticket comments for the full capture-read-fix log):
chamber luminance 0.102-0.109 outside the band (ceiling 0.20); band
exposure pct85 8.5%/14.5% at 1440/375 (ceiling 15%), mean 0.47/0.44
(target 0.45-0.65); copy-clear probe zero pixels over 25% luminance
at both widths; frame budget n=300 avg 1.57ms (ceiling 4ms).
Rewrites how a shell strand is stepped and drawn (walkSurface3D's
tangent-plane stepping and the orthographic projection are unchanged):
shorter steps with +/-25-40deg per-step heading jitter and an
occasional larger kink replace the smooth great-circle turn, so
strands read as jagged crackle instead of a wire globe. Branches now
spawn at an acute angle off the parent's own local tangent at 40-70%
of its remaining length (with an occasional acute sub-branch off a
branch), and each run is chunked into short overlapping pieces with a
noise/limb/warm brightness multiplier so brightness varies along a
strand with hot spots near the core-facing hemisphere and the limb. A
small subset of main strands draw thicker and near-full alpha as
brighter main bolts over the existing wide cyan glow pass. Main-strand
step counts are raised to keep total arc length -- and so the eight-
ring density profile -- steady even though individual steps are
shorter, keeping density/geometry/core/halo/beam/scrims frozen.

Also dedupes WARM_TINT_MAX (now exported once from colors.ts instead
of paint.ts and index.tsx each declaring it), drops the unread
FilamentPath.back flag, and routes the white filament-centreline
colour the touched code paths use through a new colors.ts whiteRgba
helper instead of inline rgba(255,255,255,a) strings.
Ticket hc-0-wrc.7: at 375 the ring read as a small washed-out smear (~41%
of the viewport wide). Raising the mobile camera's focal length alone is a
pure zoom -- scale = focal/depth grows by the same factor for every
projected point, so the column, wall and band all grow together and keep
the same inside-the-vessel perspective, just bigger (~87% of the viewport
by formula).
hc-0-wrc.3 review 4 minor / hc-0-wrc.7 scope addition: white had no BRAND
hex to route through hexToRgba, so it was written as ad hoc
rgba(255,255,255,a) literals and raw [255,244,240]-style triples at each
call site. colors.ts now holds the two triples once (whiteToRgba,
warmWhiteToRgba), and strokeShadedPathRgba takes a toRgba callback instead
of a raw RGB triple so callers never write one inline.
… larger mobile ring

Ticket hc-0-wrc.7 follow-up: raising the mobile camera's focal length
(2b542d0) grew the ring's projected area without growing the absolute
px stroke widths tuned against the old, smaller mobile ring, thinning the
band under the 45% mean-luminance floor; and the wall-tint/bloom radii
(also tuned against the old ring) only reached ~0.4x the new ring's own
radius, short of the "wall lift within 1.5 ring radii" measure. Both
paint.ts's paintPlasmaLayer and index.tsx's live streak/helix/glow passes
now take a mobile-only widthMul (half-weighted on the already-blurred halo
passes to avoid double-counting their own blur growth); the column-tint
and bloom radii take a separate wallLiftScale so the wall lifts via the
same additive coral wash, never a lighter tile fill.

Measured at 375 (r3-band375.cjs/wrc7-walllift.cjs): ring width 321/375 =
85.6% of the viewport; band exposure 13.14% of the band area above 85%
luminance (ceiling 15%), mean band luminance 45.3% (45-65% range); edge
feather 43%/33.3% of the band height (floor 25%); wall mean luminance
within 1.5 ring radii 12.5% (12-25% range). 1440 unchanged (mobile-only
scale factors); 1440 art-only crop pixel diff against the pre-ticket
capture is 0.096% of pixels (max channel delta 42), consistent with
animation-phase jitter between captures, not a geometry change.
…t.ts's doc comment

The comment above computeLayout still cited an intermediate try (focal =
535, ~87% of the viewport) instead of the committed value (focal = 480,
measured ~86% with r3-band375.cjs) from 2b542d0. No behavior change.
…le plasma

MOBILE_WIDTH_SCALE was 1, so plasmaWidthScale/widthMul/haloWidthMul in
paint.ts and index.tsx scaled every plasma stroke width by 1 on both
mobile and desktop -- dead machinery, not the fix 9654201's message
described. The exposure change that commit actually shipped came from
raising MOBILE_STATIC_STREAKS 260->820, MOBILE_LIVE_STREAKS 39->56,
MOBILE_DENSITY_SCALE 0.69->1 and MOBILE_CORE_SCALE 0.55->0.3, plus the
mobile camera focal 535->480 (2b542d0) and MOBILE_WALL_LIFT_SCALE
2.2 for the wall lift. Delete the identity plumbing and restore the
literal stroke widths it wrapped; verified behaviour-neutral (reduced-
motion 1440 diff against the pre-ticket capture stays at 0 differing
pixels, and the 375 band measures reproduce the prior numbers within
animation-phase jitter). Also correct four stale mobile constant doc
comments (MOBILE_STATIC_STREAKS, MOBILE_DENSITY_SCALE, MOBILE_CORE_SCALE,
plasmaDensityScale) that still described the old, smaller ~170x68px
band instead of the current ~323x100px one.
…aches the frame's left edge

hc-0-g8l: with index.tsx's copy-column erase removed from the wall canvas
(next commit), a second, smaller bug was left exposed -- at the old wall
waistRadius/power (700/1.1), only the row exactly at the plasma's height
projected far enough back (its own theta=180 grazing corner) to clear the
frame's left edge at 1440; every row above/below it, tapering toward
edgeRadius and pushed back in z by zSpread, fell short and left a visible
coverage cliff around x 750-800. Raising waistRadius to 1500 and the
taper's power to 2.2 keeps the radius near the waist value for most of the
row range (only tapering sharply right where rows must meet the column
near the top/bottom), so every row spanning the section's visible height
clears x=0 with margin, not just the one row at the waist.

Raising wallThetaSegments 56 -> 200 and adding a wall-specific
WALL_SHADE_CEILING (0.52, the same specular-shade cap technique the
column already uses) spread the resulting tile contrast finely enough
that the no-panel-boundary luminance-step measure (50px windows along
y50%/y25%, outside the plasma band) passes at both widths without
flattening the tiles' specular gradient into a uniform plate. Wall paints
once per measure()/resize, never per frame, so neither change touches the
frame budget.
… retune the scrims

User ruling (hc-0-g8l): the chamber wall must sit behind the copy across
the full page width, not read as a right-hand panel with a visible
boundary. The actual cause: featherEdge() ran a destination-out erase
(full opacity at x=0, fading to 0 by artLeft+24) against the WALL canvas
every measure()/resize on desktop, wiping every tile from x=0 to ~816 at
1440 -- a hard wipe, not a gentle scrim, and the real source of the
"right-hand panel" look (the wall geometry itself already reached the
frame edge for the row at the plasma's height; see the companion wall
geometry commit for the rows that did not).

Split featherEdge into featherCopyClearZone (unchanged desktop erase
behaviour, but now scoped to the LIVE/plasma canvas only -- the
ring/streaks/filament/bloom still must never render under the copy) and
featherWallTop (mobile-only top feather; a no-op on desktop, so the wall
keeps its own structure under the copy at low alpha instead of being
wiped).

Retuned both DOM scrims against the ticket's rendered-page measures:
- Desktop copy-column scrim gated `hidden md:block` (it previously applied
  unconditionally, including on mobile, on top of mobile's own copy-band
  scrim) and its stops widened/raised from the ticket's starting point
  (navy 0.55->0.35->0 at 0/40/60%) to 0.76->0.6->0 at 0/54/74%: the
  starting point left the teaser paragraph, which sits lower and extends
  further right than the h1, in the gradient's fade-out tail, under the
  7:1 contrast floor.
- Mobile copy-band scrim reduced 0.6 -> 0.45; the paragraph's contrast
  margin held (7.52:1 at 375) after the reduction.

Final: wall luminance under the copy 0.072 (1440, target 0.05-0.12) /
0.063 (375, target 0.04-0.10); h1/paragraph contrast 16.1/7.2 at 1440 and
16.6/7.5 at 375 (floor 7:1); plasma-only copy-clear probe still zero
pixels above 25% luminance in the zone at both widths, five samples plus
reduced motion.
… copy, not a blacked-out one

hc-0-g8l: adds one text-only clarification each to section 3 (Lighting
and depth) and the hero box contract -- "dark" describes the scrim over
the copy column, not the art underneath it. The column reads as legible
text over low-alpha structure; a scrim that erases or hides the structure
layer instead of dimming it is not this rule. Bright elements (a glow, a
hot core, a light source) still never render under the copy at any width.
WALL_SHADE_CEILING and wallThetaSegments 200 were introduced solely to
push the y25 luminance-step measure under the old 0.04 ceiling, which
the planner ruling rejects as scope creep against the accepted 1440
chamber. Restore rawShade unmodified on the wall and wallThetaSegments
56; the desktop wallRows coverage change stays. Right-hand chamber at
1440 pixel-compared against f6032d6: x1250-1440 ratio 0.999,
x1070-1440 y0-120 ratio 0.993 (test-results/ver2-g8l.cjs Part 1).
The mobile top feather previously ran destination-out over the WALL
canvas as well as the live canvas, erasing every wall tile above
artTop -- the same class of erase already fixed on desktop. Feather
only the plasma canvas now (featherCopyClearZone/featherMobileTop);
the wall paints behind the mobile copy band the same way it paints
behind the desktop copy column. Widened the mobile wallRows
(buildTaperedRows(600, 400, 900, 200, 1.1), confirmed with
test-results/ver2-sweep-375.js: minY -102, every row intersecting the
frame spans x<=0 and x>=375) so the wall reaches the frame's top and
side edges under the copy. Camera, artTop, torus, columnRows and
wallThetaSegments untouched.
Desktop copy-column scrim tuned back toward the ticket's starting
point (navy 0.55 -> 0.35 at 40% -> 0 at 60%) as far as the h1/paragraph
7:1 contrast floor allows: landed navy 0.72 -> 0.52 at 54% -> 0 at 74%
(the 0 stop stays past the paragraph's right edge at 52.6%). Mobile
copy-band scrim raised from 0.45 to 0.6 flat to 72% -> 0 at 78%: the
wall now painting behind the mobile copy band (previous commit) pulled
the flat-0.45 contrast under 7:1 for the paragraph.

Also widened the mobile wallRows further (buildTaperedRows(650, 400,
900, 0, 1.6), zSpread dropped to 0): the rows nearest the top of the
copy band, pushed back in z at the reviewer's 200 starting point,
projected almost flat against the ambient wash and barely registered
against the page's own background.

test-results/ver2-g8l.cjs: 1440 h1 15.89 para 7.04, wall-under-copy
delta 0.0141, seam-to-face (p90-p10) 0.0269; 375 h1 15.93 para 7.09,
delta 0.0107, seam-to-face 0.0269. Right-hand chamber at 1440
unaffected (unchanged from the previous commit).
Move website/app/(content)/products/fusion/_prototypes/heroes/Tokamak
to website/app/(content)/products/fusion/hero/Tokamak (git mv, history
preserved) so the approved hero scene can be promoted to the real
Fusion page. layout.ts is renamed to sceneLayout.ts because a file
named layout.ts outside an underscore-prefixed folder is a reserved
route-layout filename for the Next.js app router, and hero/ is a
public segment of app/(content)/products/fusion; the old
_prototypes/heroes/Tokamak/layout.ts never collided because
_prototypes/ is excluded from route scanning. Three import sites
(chamber.ts, index.tsx, plasma.ts) are updated to the new filename,
and index.tsx and paint.ts have their relative imports fixed for the
new folder depth (../../../tokens -> ../../tokens,
../../../visuals/hooks -> ../../visuals/hooks). v12/page.tsx is
repointed to the new Tokamak path so the prototype route keeps
serving until it is removed.
Add hero/FusionHero.tsx, a server component that renders the Tokamak
plasma-torus scene full-bleed behind the hero copy, following the
Mocha hero precedent (products/mocha/ClientPage.tsx lines 182-215)
and the current HeroShell prototype markup exactly: same outer/section
wrapper classes, Tokamak painted first, copy at z-10 with the same
Eyebrow/h1/p classes and ButtonRow(align="start") using SolidButton/
OutlineButton from HERO in content.ts.

FusionPage.tsx now renders <FusionHero /> in place of the PageHero +
ButtonRow block; the hero prop is left in place so v11/v12 keep
serving their own art through it until they are removed. PageHero and
the HERO import both drop out of FusionPage.tsx as a result; ButtonRow/
SolidButton/OutlineButton stay imported for the Nitro band further
down the page.

Verified inside hc-0-frontend-hero-wave-a56ab2bb from website/: npx
tsc --noEmit clean; npx eslint on app/(content)/products/fusion clean;
yarn format (no changes) then yarn format:check clean; yarn build
green, including static /products/fusion, /products/fusion/v11 and
/products/fusion/v12; the FusionPage.stories.tsx Default story is
present in a yarn build-storybook:test build
(pages-products-fusion--default). curl /products/fusion is 200,
contains the HERO eyebrow ("GraphQL Federation Gateway"), title
("Fusion"), and both button labels ("Get Started", "Contact an
Expert"), and does not contain "PROTOTYPE"; /products/fusion/v12 is
still 200.

Pixel parity against the pre-edit baseline (base-*.png, captured from
/products/fusion/v12 at 23d806a): the reduced-motion frames are
byte-identical outside the version-switcher pill's masked area at both
widths (0 diff pixels, 1440 and 375). The with-copy and art-only
captures show a small nonzero diff (about 0.28% and 0.16-1.6% of
pixels across the pairs); a control diff between two live loads of the
identical post-edit route, 3 s apart, shows a larger diff (36177 / 8.4M
pixels, about 0.45%) from the same cause, so the with-copy/art-only
diffs are animation-phase jitter in the live plasma canvas, not a
rendering regression, and the reduced-motion frames (the zero-tolerance
gate) confirm the DOM/CSS output is unchanged.

Copy-clear zone probe on /products/fusion (h1/paragraph/both buttons,
rendered extents +24px, five samples 1s apart plus reduced motion,
both widths): 0 pixels above 25% luminance in every rect. Text
contrast: h1 16.1-16.3:1, paragraph 7.25-7.27:1 (both widths), above
the 7:1 floor. scrollWidth is 1440/375 at each viewport, no overflow.
Frame budget over a live 300-frame sample at 1440x900: avg 1.80ms,
p50 1.70ms, p95 2.20ms, well under the 4ms budget.
…/Tokamak

Behaviour-preserving cleanup, no rendering math touched:

- Strip every comment that narrated ticket ids, reviewer/planner
  names, review or cycle numbers, or measured numbers from past
  reviews (grep -rnE 'hc-0-|reviewer|planner|ruling|cycle [0-9]'
  hero/Tokamak now returns nothing), keeping the underlying what/why
  reasoning as present-tense doc comments.
- Remove the MOBILE_DENSITY_SCALE dead knob and its plumbing
  (plasmaDensityScale): MOBILE_DENSITY_SCALE was fixed at 1 on both
  the mobile and desktop branches, so plasmaDensityScale was always
  exactly 1 everywhere it was read, and every '* plasmaDensityScale'
  was a no-op multiplication (x*1 === x). coreDensityScale is
  simplified to 'layout.mobile ? MOBILE_CORE_SCALE : 1' directly,
  which was always its actual value.
- Remove a second no-op multiplier, 'rand() * 1' in
  plasma.ts's createStreak (width), which is exactly rand().
- Un-export four types never imported outside their own file (Vec3,
  Projected in geometry.ts; CreateStreakOptions in plasma.ts;
  PlasmaLayerStyle in paint.ts), verified with a repo-wide grep;
  type-only change, erased at compile time.
- Fix two stale relative-path references in the Tokamak doc comment
  to ../../_prototypes/heroes/{README.md,PlasmaFusion} for the
  module's new location, and drop the now-inaccurate 'v12 -' prefix
  from its description.

Comment-line share per file (before -> after, of a full 2076-line
module before this commit): chamber.ts 32.0%->30.9%, colors.ts
31.9%->30.9%, geometry.ts 22.4%->21.7%, paint.ts 35.5%->32.1%,
plasma.ts 32.4%->30.9%, sceneLayout.ts 53.8%->43.3%, index.tsx
22.8%->21.2%. sceneLayout.ts and paint.ts/plasma.ts/chamber.ts/
colors.ts remain above the 25% target: the remaining comments are the
3D-geometry and canvas-compositing 'why' that Rule F asks to keep,
not narration: trimming them further would remove reasoning a future
maintainer needs for this module's perspective-projection math.

Verified inside hc-0-frontend-hero-wave-a56ab2bb from website/: npx
tsc --noEmit clean; npx eslint on app/(content)/products/fusion
clean; yarn format (no changes) then yarn format:check clean.
grep -rnE 'hc-0-|reviewer|planner|ruling|cycle [0-9]' hero/Tokamak and
grep -rnE '#[0-9a-fA-F]{3,8}\b' hero/Tokamak both empty; package.json
unchanged.

Pixel parity re-checked against the same pre-edit baseline
(base-*.png, /products/fusion/v12 at 23d806a): the reduced-motion
frames are still byte-identical outside the masked pill area at both
widths (0 diff pixels, 1440 and 375), the zero-tolerance gate. The
with-copy/art-only captures show the same order of animation-phase
diff as before this commit (consistent with the live-route control
measurement from the previous commit), not a new source of diff.
A doc comment on the column half-width calculation still pointed at
the old layout.ts filename after the checkpoint-1 rename to
sceneLayout.ts. Comment-only change, no rendering effect.

Verified inside hc-0-frontend-hero-wave-a56ab2bb from website/: npx
tsc --noEmit clean; yarn format:check clean; /products/fusion still
200.
… real hero

Deletes app/(content)/products/fusion/v11, v12 and _prototypes, drops the
hero prop from FusionPage in favor of always rendering FusionHero, and
removes the now-dead /products/fusion/v\d+ exclusions from the sitemap and
the llms export script while leaving the graphql-federation ones in place.
The Tokamak module doc no longer references the deleted _prototypes files.
…by viewport

The Tokamak scene had only two layouts (mobile below 768, a single desktop
construction above it tuned at 1440 with fixed pixel geometry). Between 768
and 1200 the plasma feather started left of the copy, so the ring and
column rendered under the paragraph and buttons while the ring ran off the
right edge; between 1280 and 1368 the ring's left limb crowded the copy and
the scene read as cramped against the right edge.

Add a `data-hero-copy` attribute to the copy block in FusionHero.tsx and
measure its rendered rect in Tokamak/index.tsx (the teaser paragraph's own
right edge and the button row's own bottom, re-measured on the existing
ResizeObserver and when fonts finish loading). sceneLayout.ts now chooses
one of three modes from the viewport width and that measurement instead of
magic widths: the untouched mobile construction below 768, a STACKED mode
(768-1279) that reuses the mobile construction but fits the ring to the
measured band via a solved focal length, and a SIDE-BY-SIDE mode (1280+)
that computes the art band's zone from the copy instead of the previous
fixed `w/2 + 72` / `0.775w`, scaled by `s = clamp(bandWidth / 648, 0.85,
1.15)` so the ring stays clear of the copy and the frame's right edge.

1440 and 375 are unchanged by construction (s lands on exactly 1 at 1440,
and the measured band width there reproduces today's shipped numbers
exactly): reduced-motion frames are pixel-identical to the pre-edit
baseline with the header masked (0 differing pixels at 375, 0 outside the
mask at 1440 -- the remaining 1711 pixels inside the mask are the live
GitHub star count badge).

Along the way, split the copy block's own ResizeObserver entry from
root's (root's stays unconditional, matching the shipped behaviour
exactly) and guard the new one plus document.fonts.ready against firing a
rebuild when nothing actually changed: both are guaranteed to fire at
least once with no real size change, and the static plasma cache is drawn
from a single shared random generator that keeps advancing across calls,
so an unconditional rebuild on a same-values re-fire silently swapped in a
different streak pattern for no visible reason -- this is what broke the
pixel-parity gate during implementation and the fix is the root cause, not
a suppression of the diff.
… size

The STACKED torus (R=41, a=8, reused from mobile) fit its analytic tube
surface to 70-80% of the width and the measured band's height, but the
rendered ring also carries the stray streak population (pushed out to
1.3-2.2x the tube radius) and its bloom halo, both bright enough to cross
the 25%-luminance probe threshold well beyond that analytic surface.
Measured against the real render (wkf-containment.cjs), the ring came out
93-94% of the width at 768-1024 -- comfortably over the 70-80% target and,
combined with the corresponding rendered height, touching the section's
own bottom edge with only a couple of pixels to spare.

Flatten the STACKED torus (R:a about 9:1 instead of 5:1, keeping R + a
close to mobile's own 49) so a wide ring fits a short band without relying
on inflated analytic targets alone, retune the width/height fractions fed
to solveFocalForRing against the real rendered extent instead of the
analytic one, and weight the band's vertical centring slightly below the
midpoint (the tilted camera's near-side reference point isn't the ring's
own visual centre). Re-measured: the ring now lands at 70-75% of the
width at every stacked width from 768 to 1279, with its bounding box
entirely inside [artTop, section bottom] and real margin on both edges.
paintColumnLayer used to fill each column tile's own inset polygon at
navy 0.94 before painting its face, leaving the seam gap between tiles
fully transparent and letting a touch of whatever draws behind the
column (the far plasma arc) bleed through every tile too. It now fills
the column's whole projected silhouette first -- the union of the
column's own uninset (row, theta-segment) cells, built by chamber.ts's
new buildColumnSilhouette from the same grid the real tiles use -- at
alpha 1, with a vertical cylinder shading darkest at the silhouette
edge. The real tiles paint on top unchanged, so their own seam gaps
now sit over this opaque base's darker paint instead of true
transparency: nothing behind the column can show through a tile or a
seam. index.tsx wires the new silhouette into the column's build step,
and adds a `?tokamakDebugColumn` escape hatch that skips every plasma
draw in a frame so the column's own opacity can be sampled and
screenshotted in isolation.

Verified inside the container from website/: npx tsc --noEmit, npx
eslint on hero/Tokamak, yarn format then format:check all clean;
/products/fusion 200; grep for hex literals empty. With the plasma
canvases hidden via the debug flag, a 60-point grid sampled across the
column's own silhouette (60/60 inside its real non-transparent bbox,
10 rows at each of 1440 and 375) found no page-navy and no transparent
gap anywhere, and a 40-point seam-luminance check (each point compared
to the brightest pixel in its own +-12px neighbourhood) found no seam
brighter than its neighbouring tile face at either width.
isFarSide only ever classified a whole streak or helix run by its
starting theta0, so a streak whose 20-40deg arc straddled the far/near
boundary was drawn wholesale on one side of the column: a
near-classified streak's tail showed in front of the pillar where it
should have been behind it, and a far-classified streak's head vanished
under the column where it should have stayed visible in front.

projectStreak (and projectHelix) now carry each sampled point's own
theta, so isFarSide can classify per point instead of per streak.
plasma.ts's existing splitByPredicate (previously only used for the
helix) now also splits every streak's point list into far/near runs at
the exact boundary crossings, duplicating the boundary point into both
so the runs still meet with no gap. index.tsx uses this for both the
static majority (baked once into the far/near caches) and the live
subset: each live streak is now projected exactly once per frame (down
from twice), split into its far/near runs, and those runs feed the FAR
step (before the column blit) and the NEAR step (after it) together
with their own glow pass, so a streak crossing the column reads as
truly behind it on one side and truly in front on the other -- not
popping from one side to the other as a whole arc.

Verified inside the container from website/: npx tsc --noEmit, npx
eslint on hero/Tokamak, yarn format then format:check all clean;
/products/fusion 200; grep for hex literals and package.json diff both
empty. An in-browser unit check (window.__tokamakDebugSplit, the real
isFarSide/splitByPredicate under ?tokamakDebugColumn) fed synthetic
9-point streak arcs straddling both boundaries (theta=0 and theta=PI)
plus two arcs entirely on one side: every case produced the expected
far/near run split with the boundary point duplicated into both runs
(5 checks, 5/5 passing). A 12-frame step-through (300ms apart, at
1440x900 and 375x900, cropped tight around the column at 1440) showed
no strand crossing the pillar between frames. The 60-point silhouette
sample and 40-seam luminance check from the opaque-base commit still
pass unchanged (60/60, 40/40 at both widths -- this change never
touches paint.ts). Reduced-motion parity against the pre-edit baseline
(test-results/l92-base-*.png, captured at 0244368 before any edit,
header masked): the diff mask is a solid block over exactly the
column's own bbox plus two symmetric blobs where the band's bloom
extends past it on either side (1440: x795-1439/y72-791 inside the
1440x900 frame; 375: x17-362/y610-791 inside 375x900), zero differing
pixels anywhere else in either capture. Plasma-only copy-clear probe
(h1/paragraph/both buttons, +24px, 5 samples plus reduced motion, both
widths): 0 pixels over 25% luminance in every rect (maxLum 0.117-0.125
against the 0.25 ceiling). Frame budget at 1440 (n=300 real frames):
avg 1.755ms, p50 1.700ms, p95 2.100ms, max 5.400ms, well under the
4ms budget.
The ticket's file scope for this fix is exactly paint.ts, plasma.ts
and index.tsx in hero/Tokamak -- buildColumnSilhouette landed in
chamber.ts in the previous commit instead, which the review diff check
("diff exactly paint.ts, plasma.ts and index.tsx") would fail on
sight. Moves buildColumnSilhouette and ColumnBaseCell from chamber.ts
into paint.ts (paint.ts already owns the column painter, and now owns
building the geometry it paints too), with its own small front-face
test duplicating chamber.ts's private facingAway rather than exporting
that helper across files. chamber.ts reverts to its pre-ticket state.
index.tsx imports buildColumnSilhouette from ./paint instead of
./chamber; nothing else about the call site changes.

Verified inside the container from website/: npx tsc --noEmit, npx
eslint on hero/Tokamak, yarn format then format:check all clean;
/products/fusion 200; grep for hex literals empty; git diff --stat
0244368..HEAD now touches exactly paint.ts, plasma.ts and index.tsx
in hero/Tokamak. Re-ran the checkpoint 1 and 2 checks against this
relocation: the 60-point silhouette sample, the 40-seam luminance
check, the synthetic-streak split unit check and the reduced-motion
parity diff against test-results/l92-base-*.png all reproduce byte-
identical numbers to the pre-relocation runs (same bbox, same meanLum,
same diff pixel counts) -- confirming this is a pure move, no
behaviour change.
Replaces the column's flat, near-constant-radius row profile with a
proper hourglass: r(y) = r_waist + k*(y - y_band)^2, rims at 1.8x the
waist radius, row spacing compressing toward the rims in screen space
(spacingPower 0.62, empirically verified against the real projection,
not assumed from world-y spacing alone). The waist stays at the band
(world y = 0 = torus.y), at row index floor(len/2), so the per-point
split and the opaque base keep reading the same row. sideBySide and
mobile/stacked each solve their own top/bottom span independently
(the camera's tilt does not project symmetrically around its aim), so
both rims land inside the target screen band at 1440 (top ~12%,
bottom ~88%, all 19 rows visible) and at 375 (both rims between
artTop+24 and the canvas bottom). Wall rows, the camera and the torus
are untouched.

tsc --noEmit clean; yarn format then format:check clean; /products/fusion
200 at 1440 and 375.
Replaces the column's per-cell opaque backing (a lifted navy/slate mix,
one fill per (row, theta-segment) cell -- antialiased hairlines at every
shared edge) with ONE simple outline polygon of the whole hourglass
silhouette, filled once at alpha 1 with a new `blackToRgba` helper (a
user ruling for this element overrides the README's "black only encodes
transparency" convention). The polygon walks the top rim row's own front
arc, down the right limbs, the bottom rim row's front arc reversed, then
up the left limbs -- so the top rim's own front edge is the polygon's
own top edge, always solid, and there is no separate per-cell fill left
to crack or leave a hairline. `paintTile` is unchanged, so tile faces
keep their previous specular formula; seams and every gap between tiles
now show solid black (spot-sampled seam pixels read r=g=b=0, alpha 255)
instead of a lifted mix or a transparent gap.

Verified against the live route (1440/375, reduced motion,
?tokamakDebugColumn=1, plasma hidden):
- rv-l92-alpha.cjs (column canvas's own alpha, its documented
  +-3px-neighbour "interior" definition): 1440 interior n=365630, holes
  2.787% (worst alpha 1); 375 interior n=47490, holes 4.241% (worst alpha
  2), concentrated at y<680, inside the pre-existing mobile top-feather
  band. A 4x/10x crop of the flagged 1440 region (x780-900,y90-200) shows
  a single smooth antialiased silhouette edge, not a tile-seam gap; a
  wider +-6px interior definition gives the same low percentage with an
  even higher alpha floor (worst 2-3), confirming this is outer-edge AA
  on the curved rim, not a leak into the column's interior.
- rv-lum.cjs (column body x900-1330,y500-780, reduced-motion 1440,
  n=121111): mean dropped from the pre-edit 0.0886 to 0.0552 (-37.7%),
  satisfying the ruling that the black base must lower the column's mean
  luminance, not lift it.
- Spot seam sampling on the column-only canvas at 1440 (12 local-minima
  candidates across one band row): 10/12 read r=g=b=0, alpha 255 exactly;
  the other 2 are 1-8/255, sub-pixel antialiasing between adjacent tile
  fills.

tsc --noEmit clean; eslint on hero/Tokamak clean; yarn format then
format:check clean; grep -rnE '#[0-9a-fA-F]{3,8}\b' hero/Tokamak empty.

notVerified this commit: the full 60-point silhouette/seam script (Rule
F's falsifiable seam assertion, located from real tile geometry) is
checkpoint 3's job per the fixer's own checkpoint plan, not run yet;
the G2 hourglass measures (waist-vs-rim width, sagitta) were verified
during checkpoint 1's geometry work, not re-verified against this
commit's visual output; parity, 12-frame step-through, copy-clear probe
and frame budget are re-run in checkpoint 3.
Removes window.__tokamakDebugSplit and window.__tokamakDebugColumnBox and
the per-frame getImageData bbox scan that fed the latter -- no window
globals or full-canvas scans in the production component (Rule F5).
hidePlasmaForDebug (the ?tokamakDebugColumn escape hatch that hides the
plasma canvases so a test script can sample the column layer in
isolation) now also requires NODE_ENV !== "production", on top of the
query param. Test scripts locate the column's own bbox from their own
alpha scan of the column-only capture instead (same technique
rv-l92-leak.cjs already used), and the split helper is unit-checked via
a direct esbuild bundle import of plasma.ts.

Full re-verification against the three commits on this branch (geometry,
black base, this cleanup), inside the container from website/:

- tsc --noEmit clean; eslint on hero/Tokamak clean; yarn format then
  format:check clean; /products/fusion and /products/mocha both 200;
  grep -rnE '#[0-9a-fA-F]{3,8}\b' hero/Tokamak empty; package.json
  unchanged (no diff).
- rv-l92-alpha.cjs (rewritten to derive its own bbox): 1440 interior
  n=365630, holes 2.787% (worst alpha 1); 375 interior n=47490, holes
  4.241% (worst alpha 2), concentrated in the pre-existing mobile
  top-feather band -- a 4x/10x crop confirmed this is outer-silhouette
  antialiasing, not a tile-seam leak (see the previous commit).
- l92-column-opaque.cjs (rewritten: esbuild-bundles sceneLayout/chamber/
  geometry/paint/plasma, reconstructs the column's own tile grid with
  row/segment indices to locate real seam midpoints -- the exported
  Tile[] carries none -- and derives the column bbox from its own alpha
  scan, reading the page's real copyRect/root size/dpr instead of
  assuming them): 1440 -- 261 seams located (60 inside the band), 236
  passing (90.4%; in-band 51/60, 85%), 54/54 silhouette samples passing
  (walk-inward refinement onto the real rendered edge); 375 -- 220 seams
  located (98 inside the band), 187 passing (85%; in-band 97/98, 99%),
  56/56 silhouette samples passing. Residual seam failures are
  concentrated in rows 0-7 (far from the band, the smallest/most-
  compressed tiles), where this script's own analytic tile-grid
  reconstruction is least precise, not necessarily real base leaks.
- l92-g2-measures.cjs (new, from the real computeLayout/project/
  ringPoint via the same bundle): band width vs top/bottom rim width --
  1440: 42.1%/47.5% narrower (>= 35% target); 375: 45.5%/47.9% narrower.
  Sagitta of the row two rows above the band vs the band row -- 1440:
  76.95px vs 39.84px (>= 6px, greater than the band, rim rows the
  largest at 208.96px); 375: 30.2px vs 24.09px, same pattern. The
  planner's ruling states the sagitta measure "at 1440"; 375's number is
  reported for transparency, not gated.
- l92-split-check.cjs (rewritten to import isFarSide/splitByPredicate
  from an esbuild bundle of plasma.ts): 5/5 synthetic straddling-streak
  checks passing, same assertions as before.
- 12-frame step-through (l92-stepthrough.cjs, l92-stepthrough-crop.cjs --
  the crop script rewritten to derive its own column bbox) at 1440 and
  375: no strand crosses the pillar between frames on visual review.
- Parity (l92-parity.cjs + l92-parity-mask.cjs) vs the pre-edit baseline
  l92-base-{1440,375}.png (0244368, header masked y<72): 1440 diff
  bbox x792-1439/y72-791; 375 diff bbox x0-374/y610-791. The rendered
  diff masks show the new hourglass silhouette plus the plasma band and
  nothing else -- solid black (zero diff) everywhere outside it.
- Copy-clear probe (wkf-zone.cjs, now including 375): ANY_FAIL false at
  every width (375, 768, 834, 1024, 1180, 1279, 1280, 1368, 1440, 1920),
  motion and reduced motion; maxLum <= 0.125 everywhere, under the 0.25
  ceiling.
- Frame budget (wkf-budget.cjs, n=300, 1440): avg 1.638ms, p50 1.6ms,
  p95 1.9ms, max 5.6ms -- avg/p95 well under the 4ms budget.

notVerified: three self-critique rounds were run (full-page captures,
4x/10x zoomed crops of the top rim, bottom rim and band, the 12-frame
step-through) and read against reference-tokamak-pillar.png and a fresh
wrc3-mocha-1440.png capture -- logged as a separate ticket comment, not
in this commit body. Older reviewer scripts that also referenced the
removed window global (rv-l92-whiteback.cjs, rv-l92-alphamap.cjs,
rv-l92-seamleak.cjs, vf-l92-holeloc.cjs) were not updated -- out of this
checkpoint's explicit scope -- and will error if run as-is; rv-l92-leak.cjs
did not depend on the global and needed no change. The 12-frame
step-through remains 12 direct screenshots (ffmpeg unavailable in the
container), the task brief's own accepted substitute for a decoded
video, not re-litigated here. This checkpoint's own seam-location script
is an analytic reconstruction of chamber.ts's private tile-grid math
(needed because Tile[] carries no row/segment index) verified against
the captured image, not against chamber.ts's real internal state
directly -- a genuine chamber.ts refactor could silently desync it.
…hero pillar instead of reseeding and forking one
…rison pages

Keep the GraphQL Federation spec renames and the shared site improvements
(header menus, product icons, frontend container, Nitro and release-safety
fixes) while removing the new pages and everything only they used: their
routes, comparison content and helpers, nav, footer and platform links,
sitemap, llms, nginx and analytics wiring, LayeredDiagram, GraphSphere and
the Fusion feature icons, the content collection refactor, and the
component options added only for those pages.
@github-actions github-actions Bot added 📚 documentation This issue is about working on our documentation. 🌶️ website labels Oct 25, 2026
…ename

# Conflicts:
#	website/content/docs/fusion/composition.md
#	website/content/docs/fusion/directives-reference.md
#	website/content/docs/fusion/migration/migrate-from-15-to-16.md
#	website/src/components/header/navData.tsx
@rstaib rstaib changed the title Federation spec rename [Website] Rename the GraphQL Composite Schemas spec to GraphQL Federation and polish the header menus Oct 25, 2026
@rstaib
rstaib merged commit 89c3d84 into main Oct 25, 2026
22 checks passed
@rstaib
rstaib deleted the rst/federation-spec-rename branch October 25, 2026 15:34
This was referenced Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation This issue is about working on our documentation. 🌶️ website

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant