← Corpus / dididecks-ai / plan
Integrate reach-edu-hub into the shared @dididecks/shell — scroll-UI first, adopting the shell's /scroll/[deck]/[variant] URL convention
reach-edu-hub is the one client-site still standing fully outside the shell: it is not in the root pnpm-workspace, carries zero @dididecks/shell references, ships output: 'static' with no auth, and renders three bespoke scroll decks (/story, /story/version-2, /automation) on a locally-ported PageAsDeckWrapper with its own scroll-decks.ts variant registry and seo.ts. Every sibling (chroma-decks, humain-vc-decks, lossless-decks) already consumes the shell as a workspace integration. This plan onboards reach using chroma-decks as the canonical template: join the workspace, add the integration call, author the decks.ts / slides.ts / audits.json contract files, migrate the three decks to the shell's /scroll/[deck]/[variant] routing, and add slot-discovery annotations to the T01–T11 section components — incrementally (scroll-UI through the shell now; the rigid no-JS Play-UI per-slide conversion is deferred to a later pass). The session this plan opens also has two adjacent tracks: build a few new reach decks on the freshly-integrated shell, and push any shell friction the integration surfaces back into apps/deck-shell rather than working it around in reach.
- Path
- plans/Integrate-Reach-Edu-Hub-into-Dididecks-Shell.md
- Authors
- Michael Staton
- Augmented with
- Claude Code (Opus 4.8, 1M context)
- Tags
- Dididecks-Shell · Reach-Edu-Hub · Client-Site-Onboarding · Scroll-UI · Shell-Integration · Workspace-Wiring · Deck-Registry · Slot-Discovery · URL-Convention-Migration · Phase-B-Foundation
Integrate reach-edu-hub into the shared shell
Why this plan exists
reach-edu-hub is the last client-site under dididecks-ai/client-sites/ that has
never been wired to @dididecks/shell. It is:
- Not in the root
pnpm-workspace.yaml(which listschroma-decks,humain-vc-decks,lossless-decks), soworkspace:*cannot even resolve the shell for it today. - Shell-free — zero references to
@dididecks/shell,dididecksShell, ordeck-shellanywhere in its source. output: 'static', no auth, deliberately public (the sitemap decision is “Gating: none”).- Rendering three bespoke scroll decks on a locally-ported
PageAsDeckWrapper, with its ownsrc/lib/scroll-decks.tsvariant registry andsrc/lib/seo.ts, at custom URLs.
The integration follows the proven onboarding shape established by chroma-decks (the canonical template) and refined by humain-vc-decks. calmstorm-decks is the donor the shell was lifted from and is not an integration template (it does not consume the shell).
Decisions locked with the user (2026-06-29)
- Integration depth: scroll-UI through the shell first. Defer the rigid 16:9 no-JS Play-UI per-slide-file conversion (deck-iteration-workflow Phase 2) to a later pass.
- URL strategy: adopt the shell’s
/scroll/[deck]/[variant]convention (migrate off the bespoke/story,/automationURLs). - Decks to create today: for reach-edu-hub, on the freshly-integrated shell.
- Variant slugs:
baseline/editorial(notv1/v2). - distributionTier:
shared(the hub is public). - Old→new redirects: add them (keep any links already shared with Reach alive).
- ModeToggle: keep reach’s own for this pass (limit theme-token risk); revisit swapping to the shell’s later.
Deck / variant model (current → shell convention)
| Current route | New shell route | Deck slug | Variant slug | Slots |
|---|---|---|---|---|
/story | /scroll/story/baseline/ | story | baseline | T01–T09 |
/story/version-2 | /scroll/story/editorial/ | story | editorial | T01–T09 |
/automation | /scroll/automation/baseline/ | automation | baseline | T01–T11 |
Phase A — workspace + integration wiring
- A1. Added
client-sites/reach-edu-hubto rootpnpm-workspace.yaml. - A2. Added
"@dididecks/shell": "workspace:*"to reach’spackage.json. - A3. Added the
dididecksShell({...})integration to reach’sastro.config.mjs—client: "reach-edu-hub", four registry paths,distributionTier: "shared". Output flipped fromstatic→server(see D1 finding below): understatic, the shell’s injected-routegetStaticPathsrunsesbuild.transforminside Astro’s bundled prerender context, which has no__filenameand crashes the build.serverrenders the injected routes on-demand — the same reason every other consumer uses server output. - A4. Created reach’s standalone-Vercel guards: appended
ignore-workspace=true(+ hoist/peer settings) to.npmrc, kept the jsr registry line, and created a localpnpm-workspace.yamlwithonlyBuiltDependencies: [esbuild, sharp]. - A5.
pnpm installfrom the monorepo root — 6 workspace projects, shell linked.
Phase B — contract files
- B1. Authored
src/data/decks.ts(DECKS—story+automation). - B2. Authored
src/data/slides.ts(SLOTSforbaseline,editorial,pipeline). Variant slugs are globally unique to avoid the variant-keyed collision (automation’s variant ispipeline, NOTbaseline). - B3. Created
data/audits/slides.json={ "schema": 2, "ranks": {} }.
Phase C — migrate scroll pages to shell convention
- C1. Created the three pages at
src/pages/scroll/{deck}/{variant}/index.astrowith corrected import depths; deleted the old/story+/automationroute dirs. - C2. Converted reach’s local
PageAsDeckWrapperinto a thin shell re-export shim; added<DeckOverlay--Scroll-UI deckSlug variantSlug />to each page. - C3. Added
data-slot/data-variantto all 29T0xsection components. (For TOC/discovery the manualslides.tsmap is authoritative — the scanner can’t see through reach’s component imports; these annotations drive the in-scroll SlideRankPill IntersectionObserver.) - C4. Added
redirects(old URLs + bare/scroll,/scroll/story,/scroll/automationdeck roots) → canonical variant routes. - C5. Updated
src/lib/scroll-decks.tshrefs,src/lib/seo.tskeys, Header nav links + active-state detection, and the homepage showcase link.
Phase D — verify
- D1. Dev server:
/scroll/story/baseline,/scroll/story/editorial,/scroll/automation/pipeline,/toc/story,/toc/story/baselineall 200; old/story+/automation200 via redirect;/toc/story/baselineshows 9 slot rows; scroll overlay +ddd-deck-wrapper+SlideRankPill+ section annotations all render. Reach’stheme.csstokens satisfy the shell chrome (it reads--ddd-*with fallbacks). Finding:output: 'static'is incompatible with the shell’s esbuild-in-prerender path → flipped toserver. - D2.
pnpm --filter reach-edu-hub buildclean (server output, Vercel adapter).
Phase E — adjacent session tracks (post-integration)
- New reach decks built as new variants/decks on the integrated shell (content specced when we get there).
- Shell tooling iteration: any friction this integration surfaces (slot
discovery without annotations, token-contract gaps, static-vs-server tension)
gets pushed back into
apps/deck-shellrather than worked around in reach, and noted in [[Lift-Chroma-Decks-Generic-Code-into-Shared-Shell]].
Risks / watch-items
output: 'static'+ injectedprerender=falseroutes. Siblings useoutput: 'server'because of auth; reach has none. Astro’s static output with the Vercel adapter supports per-route on-demand rendering, so static should hold. Fallback: flip toserver(no DB/middleware needed, just SSR-capable).- Theme-token contract. Shell chrome reads
--color-*/--ddd-chrome-*tokens. Reach’s localPageAsDeckWrapperalready uses--color-surface,--color-border,--color-text-muted,--color-text, sotheme.csslikely satisfies the contract — audit during D1, don’t assume. - Slot discovery. Reach’s slides are separate
T0xcomponent files, not inline sections. They must carrydata-slot/data-variantor the TOC comes up empty even withslides.tspopulated.