diff --git a/src/pages/index.astro b/src/pages/index.astro index 72a1769..56c9b5f 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -16,6 +16,15 @@ const ragePosts: Record = { 'TypeScript 7 Makes the Build Harness More Important': 'typescript-7-makes-the-build-harness-more-important', }; +const rageDescriptions = [ + 'The silent stale cache problem. Every JS monorepo build cache shares this flaw.', + 'Weak fingerprints find candidates. Strong fingerprints verify them. The pathset is the bridge.', + 'Three operating systems. Three interception mechanisms. macOS interpose, Linux eBPF, Windows Detours.', + 'Counter-intuitive: tsgo\u2019s 10x speedup makes distributed coordination more important, not less.', + 'Per-package content-addressed storage. The unsexy problem that kills CI build times.', + 'Personal narrative. From lage maintainer to Rust rewrite. The architecture\u2019s original sin.', +]; + const rageSeries = [ 'Your Build Cache Is Lying to You', 'Two-Phase Fingerprinting: BuildXL\u2019s Deep Magic', @@ -23,7 +32,7 @@ const rageSeries = [ 'TypeScript 7 Makes the Build Harness More Important', 'node_modules in a Content-Addressed Store', 'What I Learned Maintaining Lage and Why I Rewrote It', -].map(title => { +].map((title, i) => { const slug = ragePosts[title]; const post = slug ? posts.find(p => p.id === slug) : null; return { @@ -31,6 +40,8 @@ const rageSeries = [ slug: slug || null, published: !!post, readingTime: post?.body ? getReadingTime(post.body) : null, + number: i + 1, + description: rageDescriptions[i], }; }); --- @@ -38,93 +49,185 @@ const rageSeries = [ +
-

Crash Test Dev

-

Engineering judgment, tested against reality.

- +
+

Crash Test Dev

+

Engineering judgment, tested against reality.

+ +
+
-
- + -
- +

Rage: Rebuilding the Build System

A six-part investigation into rage, a Rust monorepo build system that fuses BuildXL's provable correctness with lage's ergonomics. From syscall sandboxing to content-addressed storage.

-
    +
    {rageSeries.map((item) => ( -
  1. - {item.published ? ( - - {item.title} - {item.readingTime} min - - ) : ( - <> - {item.title} - Coming soon - - )} -
  2. +
    + {String(item.number).padStart(2, '0')} +
    + {item.title} + {item.description} + {item.published ? ( + + Read · {item.readingTime} min + + ) : ( + Coming soon + )} +
    +
    ))} -
+
-
- +

All Posts

- {sortedPosts.map((post) => ( - - ))} +
diff --git a/tests/index-page.test.mjs b/tests/index-page.test.mjs index fcd1ae3..41f27f5 100644 --- a/tests/index-page.test.mjs +++ b/tests/index-page.test.mjs @@ -63,6 +63,24 @@ describe('Index page', () => { const content = readPage(); assert.match(content, /class="byline"/, 'should have a byline element'); }); + + it('should contain an inline SVG hero illustration', () => { + const content = readPage(); + // The hero section should contain an SVG element + assert.match(content, /class="hero-illustration"/, 'should have a hero illustration'); + assert.match(content, /]*viewBox/, 'should have an inline SVG with viewBox'); + }); + + it('should use CSS grid for hero layout', () => { + const content = readPage(); + assert.match(content, /\.hero\s*\{[^}]*display:\s*grid/s, 'hero should use CSS grid'); + assert.match(content, /\.hero\s*\{[^}]*grid-template-columns/s, 'hero should define grid-template-columns'); + }); + + it('should be full viewport width', () => { + const content = readPage(); + assert.match(content, /\.hero\s*\{[^}]*max-width:\s*100%/s, 'hero should be full viewport width'); + }); }); describe('Featured excerpt section', () => { @@ -78,12 +96,22 @@ describe('Index page', () => { it('should have a link to the full post', () => { const content = readPage(); - // The featured section should contain a link to read the full post assert.match(content, /class="featured-link"/, 'should have a featured-link to the full post'); }); + + it('should have a terracotta left border on the featured section', () => { + const content = readPage(); + assert.match(content, /\.featured[\s\S]*?border-left.*accent-primary/s, 'should have terracotta left border'); + }); + + it('should include an inline SVG diagram from a post', () => { + const content = readPage(); + // The featured section should contain a small SVG from one of the posts + assert.match(content, /class="featured-diagram"/, 'should have a featured-diagram element'); + }); }); - describe('Series section', () => { + describe('Series section - multi-column grid', () => { it('should have a series section', () => { const content = readPage(); assert.match(content, /class="series"/, 'should have a section with class series'); @@ -94,33 +122,77 @@ describe('Index page', () => { assert.match(content, /Rage/, 'should mention Rage in the series section'); }); - it('should list planned posts', () => { + it('should use a CSS grid for series cards', () => { const content = readPage(); - assert.match(content, /class="series-item"/, 'should have series items'); + assert.match(content, /\.series-grid\s*\{[^}]*display:\s*grid/s, 'should use CSS grid for series cards'); + assert.match(content, /\.series-grid\s*\{[^}]*grid-template-columns:\s*1fr\s+1fr/s, 'should have 2-column grid'); + }); + + it('should have series card elements', () => { + const content = readPage(); + assert.match(content, /series-card/, 'should have series-card elements'); + }); + + it('should show card numbers in mono font with accent color', () => { + const content = readPage(); + assert.match(content, /class="card-number"/, 'should have card-number elements'); + assert.match(content, /\.card-number\s*\{[^}]*font-family:\s*var\(--font-evidence\)/s, 'card number should use mono font'); + assert.match(content, /\.card-number\s*\{[^}]*color:\s*var\(--accent-primary\)/s, 'card number should use accent color'); + }); + + it('should show card descriptions', () => { + const content = readPage(); + assert.match(content, /class="card-description"/, 'should have card-description elements'); }); it('should indicate coming soon items', () => { const content = readPage(); assert.match(content, /coming soon/i, 'should indicate coming soon posts'); }); + + it('should use --width-container for series section width', () => { + const content = readPage(); + assert.match(content, /\.series\s*\{[^}]*max-width:\s*var\(--width-container\)/s, 'series should use container width'); + }); + + it('should collapse to single column on mobile', () => { + const content = readPage(); + // Check for media query that changes grid to 1 column + assert.match(content, /@media.*768px[\s\S]*?grid-template-columns:\s*1fr\s*[;}]/s, 'should collapse to 1 column on mobile'); + }); + + it('should have card surface background', () => { + const content = readPage(); + assert.match(content, /\.series-card\s*\{[^}]*background[^:]*:\s*var\(--surface-card\)/s, 'cards should have card surface bg'); + }); }); - describe('All posts section', () => { - it('should have a posts listing section below the pitch', () => { + describe('All posts section - 2-column grid', () => { + it('should have a posts listing section', () => { const content = readPage(); assert.match(content, /class="all-posts"/, 'should have a section with class all-posts'); }); - it('should map posts to compact entries', () => { + it('should map posts to entries', () => { const content = readPage(); assert.match(content, /sortedPosts\.map/, 'should map sortedPosts'); }); it('should show post titles as links', () => { const content = readPage(); - // Each post entry should link to its page assert.match(content, /posts\/\$\{post\.id\}/, 'should link to post pages'); }); + + it('should use --width-prose for all-posts width', () => { + const content = readPage(); + assert.match(content, /\.all-posts\s*\{[^}]*max-width:\s*var\(--width-prose\)/s, 'all-posts should use prose width'); + }); + + it('should use CSS grid for posts on desktop', () => { + const content = readPage(); + assert.match(content, /\.posts-grid\s*\{[^}]*display:\s*grid/s, 'should use CSS grid for posts'); + assert.match(content, /\.posts-grid\s*\{[^}]*grid-template-columns:\s*1fr\s+1fr/s, 'should have 2-column posts grid'); + }); }); describe('Layout', () => { @@ -136,25 +208,49 @@ describe('Index page', () => { }); describe('Style', () => { - it('should use --width-prose for content width', () => { - const content = readPage(); - assert.match(content, /max-width\s*:\s*var\(--width-prose\)/, 'should constrain content to prose width'); - }); - it('should use --font-structure for hero heading', () => { const content = readPage(); - assert.match(content, /\.hero[\s\S]*?font-family\s*:\s*var\(--font-structure\)/, 'should use structure font in hero'); + assert.match(content, /\.hero[\s\S]*?font-family:\s*var\(--font-structure\)/, 'should use structure font in hero'); }); it('should use --font-prose for excerpt', () => { const content = readPage(); - assert.match(content, /\.excerpt[\s\S]*?font-family\s*:\s*var\(--font-prose\)/, 'should use prose font for excerpt'); + assert.match(content, /\.excerpt[\s\S]*?font-family:\s*var\(--font-prose\)/, 'should use prose font for excerpt'); }); it('should not have the old .post-list as the main layout', () => { const content = readPage(); - // The old flat list structure should be gone assert.doesNotMatch(content, / { + const content = readPage(); + // Hero: full width (100%) + assert.match(content, /\.hero[\s\S]*?max-width:\s*100%/s, 'hero should be full width'); + // Series: container width + assert.match(content, /\.series[\s\S]*?max-width:\s*var\(--width-container\)/s, 'series should use container width'); + // All posts: prose width + assert.match(content, /\.all-posts[\s\S]*?max-width:\s*var\(--width-prose\)/s, 'all-posts should use prose width'); + }); + }); + + describe('Rage series data', () => { + it('should have descriptions for each series item', () => { + const content = readPage(); + // Check for at least a couple of the descriptions + assert.match(content, /silent stale cache/i, 'should have description for post 1'); + assert.match(content, /Weak fingerprints find candidates/i, 'should have description for post 2'); + assert.match(content, /content-addressed storage/i, 'should have description for post 5'); + }); + + it('should include all 6 rage series items', () => { + const content = readPage(); + assert.match(content, /Your Build Cache Is Lying to You/, 'should have post 1'); + assert.match(content, /Two-Phase Fingerprinting/, 'should have post 2'); + assert.match(content, /Hooking Syscalls/, 'should have post 3'); + assert.match(content, /TypeScript 7/, 'should have post 4'); + assert.match(content, /node_modules/, 'should have post 5'); + assert.match(content, /Maintaining Lage/, 'should have post 6'); + }); }); });