ReviewOS

also looking at this

stacks/lastsoulultra

LSU's own coaching surface, and what auditing the site turned up

#12
Merged glennmichael123 wants to merge claude/last-soul-ultra-web-6e0718 into main
86 files +3943 -1190

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

resources/views/coaching.stxmodified+9-0
Changes to resources/views/coaching.stx
@@ -26,7 +26,11 @@ const canonicalPath = '/coaching'
2626 <span class="kicker">Last Soul Ultra / Coaching</span>
2727 <h1>Say how it felt.<br><span class="accent">The plan changes.</span></h1>
2828 <p>Send a voice message after your run. An AI rebuilds next week around what you said, inside a coach's method.</p>
29 @if(hubLive)
2930 <a class="btn btn-lg" href="#training">Open the portal</a>
31 @else
32 <a class="btn btn-lg" href="#training">See what is coming</a>
33 @endif
3034 </div>
3135 <div class="coaching-hero-shot">
3236 <img src="/assets/lsu/images/recap-06-810.webp" srcset="/assets/lsu/images/recap-06-540.webp 540w, /assets/lsu/images/recap-06-810.webp 810w, /assets/lsu/images/recap-06-1080.webp 1080w" sizes="(min-width: 900px) 42vw, 100vw" alt="A runner in a white sun hat and neck cover looks down at his watch." width="1080" height="1440" loading="eager" fetchpriority="high" decoding="async">
@@ -96,8 +100,13 @@ const canonicalPath = '/coaching'
96100
97101 <section id="training">
98102 <div class="coaching-head">
103 @if(hubLive)
99104 <h2 class="h2-sm">What is open.<br><span class="accent">And where it runs.</span></h2>
100105 <p>The tiers a coach has published, then the eight panels your training lives in once you are in one.</p>
106 @else
107 <h2 class="h2-sm">Not open yet.<br><span class="accent">It opens here.</span></h2>
108 <p>No coach has published a method and no price has been set. The first tier appears on this page, under the coach's own name.</p>
109 @endif
101110 </div>
102111 <div class="coaching-surface">
103112 <CoachingOffers />
tests/coaching-views.test.tsmodified+28-0
Changes to tests/coaching-views.test.ts
@@ -249,3 +249,31 @@ describe('the coaching hub', () => {
249249 expect(html).toContain('Nothing is open yet')
250250 })
251251})
252
253describe('what the hub promises', () => {
254 async function hubHtml(): Promise<string> {
255 const path = resolve('resources/views/coaching.stx')
256 return processDirectives(await Bun.file(path).text(), {}, path, {
257 enabled: true,
258 cache: false,
259 componentsDir: resolve('resources/components'),
260 layoutsDir: resolve('resources/layouts'),
261 partialsDir: resolve('resources/partials'),
262 }, new Set())
263 }
264
265 it('offers the portal only when there is one', async () => {
266 stubHq()
267 expect(await hubHtml()).toContain('Open the portal')
268 })
269
270 it('promises neither a portal nor eight panels it is not showing', async () => {
271 stubHq(null)
272 const html = await hubHtml()
273
274 expect(html).not.toContain('Open the portal')
275 expect(html).not.toContain('the eight panels your training lives in')
276 expect(html).toContain('See what is coming')
277 expect(html).toContain('Not open yet')
278 })
279})