Skip to content

W1: Astro scaffold + design system + homepage + manifesto#7

Merged
sunilp merged 54 commits into
masterfrom
astro-redesign-w1
May 13, 2026
Merged

W1: Astro scaffold + design system + homepage + manifesto#7
sunilp merged 54 commits into
masterfrom
astro-redesign-w1

Conversation

@sunilp
Copy link
Copy Markdown
Owner

@sunilp sunilp commented May 13, 2026

Summary

Week 1 of a 4-week migration from mkdocs to Astro 5. This PR introduces a new Astro 5 build that runs side-by-side with the existing mkdocs site. The mkdocs site continues to serve the live sunilprakash.com/agentic-ai/ URL — the new Astro astro-deploy.yml workflow is workflow_dispatch-only and will not auto-deploy until W4 cutover.

What's in:

  • Astro 5 scaffold at repo root, alongside docs/. pnpm + Vitest. base: '/agentic-ai' for the subpath deploy. redirects: config generates static HTML stubs for /proof/* → /evidence/* and /principles → /manifesto — meta-refresh + canonical + JS replace at the old paths, works on GitHub Pages without server support.
  • Design system: 5 token files (colors, typography, spacing, motion with prefers-reduced-motion override, surfaces) + reset + global stylesheet. Brick-red #9b4a3f single-accent discipline.
  • Component library: 6 layout primitives, 11 universal elements, 5 homepage components.
  • Houdini paint worklet for the brick-red ink stamp with inline SVG fallback for Firefox.
  • Astro 5 Content Layer with 7 Zod schemas, ready to receive ported content in W2-W4.
  • Two TDD'd utilities in src/lib/ with 8 tests: cross-link reverse-index builder, reading-time estimator.
  • Two pages live: / (new mental-model homepage with Build/Judge/Operate) and /manifesto/ (10 numbered principles).
  • View Transitions API wired via ClientRouter with astro:page-load re-fire for client scripts.
  • Accessibility: <main> landmark, skip-to-content link, ARIA-labelled nav/footer/breadcrumb, semantic HTML.

What's NOT in this PR, intentional:

  • mkdocs docs/, mkdocs.yml, and .github/workflows/deploy.yml are untouched. Live site is unaffected.
  • No content under src/content/* yet. W2 ports FN-001, FN-002, R-001. W3 ports projects + evidence. W4 ports the 18 chapters.
  • Banner images referenced by WhatsNewStrip will 404 in dev preview until W2 ports them.
  • The new astro-deploy.yml workflow is manual-only.

Test Plan

  • pnpm install && pnpm test — expect 8/8 passing
  • pnpm astro check — expect 0 errors, 0 warnings, 0 hints
  • pnpm build — expect clean build, 2 pages, 5 redirect stubs
  • pnpm dev and visit http://localhost:4321/agentic-ai/:
    • Hero text "Build agents that survive contact with reality." with brick-red ink stamp
    • Evidence strip counts up from 0 to 3.4 / 2.4 / 2.2 on first scroll
    • Three Build / Judge / Operate cards
    • "What's new" magazine strip — banner images will 404, expected until W2
    • Manifesto pullquote with brick stamp
  • Visit /agentic-ai/manifesto/ — 10 numbered principles + pullquote + brick stamp
  • Visit /agentic-ai/principles/ — soft-redirects via meta-refresh to manifesto
  • Visit /agentic-ai/proof/baseline-eval-report/ — soft-redirects to /agentic-ai/evidence/baseline-eval-report/, 404 target until W3
  • DevTools: emulate prefers-reduced-motion: reduce — confirm hero fades instantly, evidence numbers snap, no hover lifts
  • Firefox: confirm Houdini stamp falls back to SVG
  • Click between homepage and manifesto — confirm View Transitions cross-fade

Summary by CodeRabbit

  • New Features

    • Launched Agentic AI site: responsive homepage, manifesto page, new site sections, navigation, and footer.
    • Animated count-up stats and decorative textured stamp graphic.
    • New layout and UI components for cards, grids, and typographic headings.
  • Tests

    • Added automated tests for cross-linking and reading-time utilities.
  • Style

    • Introduced a comprehensive design system: colors, typography, spacing, motion, and global styles.
  • Chores

    • Added site build & deploy workflow and project tooling/configuration.

Review Change Stack

sunilp added 30 commits May 13, 2026 15:37
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d9ebbc65-374b-4343-97c3-aa219086b52d

📥 Commits

Reviewing files that changed from the base of the PR and between 1497f3e and 840292b.

📒 Files selected for processing (9)
  • src/components/client/count-up.ts
  • src/components/homepage/EvidenceInlineStrip.astro
  • src/components/homepage/HeroEditorial.astro
  • src/components/layout/FullBleed.astro
  • src/components/layout/MagazineGrid.astro
  • src/components/layout/Split.astro
  • src/components/universal/KineticHeading.astro
  • src/components/universal/TopNav.astro
  • src/styles/tokens/spacing.css

📝 Walkthrough

Walkthrough

Complete Astro website added: project config, design tokens, components/layouts, content schemas, client utilities, Pages deploy workflow, paint worklet, homepage and manifesto pages.

Changes

Astro Website Launch

Layer / File(s) Summary
Project configuration and deployment
package.json, .nvmrc, tsconfig.json, vitest.config.ts, astro.config.mjs, .github/workflows/astro-deploy.yml, .gitignore, src/env.d.ts
Node version, package scripts/dependencies, TypeScript strict config and path aliases, Vitest config, Astro site/integrations/redirect config, manual GitHub Actions Pages build-and-deploy workflow, and environment type refs.
Design tokens and global styles
src/styles/tokens/colors.css, src/styles/tokens/typography.css, src/styles/tokens/spacing.css, src/styles/tokens/motion.css, src/styles/tokens/surfaces.css, src/styles/reset.css, src/styles/global.css
CSS custom properties for colors/typography/spacing/motion/surfaces, a minimal reset, and global element baseline styles for headings, paragraphs, code blocks, and links.
Layout components and containers
src/components/layout/Container.astro, src/components/layout/Reader.astro, src/components/layout/Split.astro, src/components/layout/MagazineGrid.astro, src/components/layout/FullBleed.astro
Reusable layout primitives controlling widths, centering, responsive grids, two-column splits with ratio parsing, and full-bleed breakout sections.
Universal UI components and primitives
src/components/universal/Brand.astro, src/components/universal/TopNav.astro, src/components/universal/Breadcrumb.astro, src/components/universal/Cadence.astro, src/components/universal/Dek.astro, src/components/universal/KineticHeading.astro, src/components/universal/MetaStrip.astro, src/components/universal/PullQuote.astro, src/components/universal/SiteFooter.astro, src/components/universal/Stamp.astro, src/components/universal/Tag.astro
Branding and nav, breadcrumb, cadence pill, dek, kinetic headings with reduced-motion support, meta strip, pull quotes, site footer, stamp visual with paint-worklet fallback, and tag primitives.
Client-side utilities and visual effects
src/components/client/count-up.ts, src/lib/reading-time.ts, src/lib/reading-time.test.ts, src/lib/cross-links.ts, src/lib/cross-links.test.ts, public/paint/brick-stamp.js
Count-up animations with IntersectionObserver and reduced-motion handling; reading-time estimator and tests; cross-link reverse-index utilities and tests; and a CSS Houdini paint worklet for a brick-stamp effect.
Content collection schemas and configuration
src/content.config.ts
Seven MDX collections (chapters, fieldNotes, recipes, projects, evidence, labs, patterns) with Zod-validated schemas (fields, nested objects, URL/date validation, enums).
Base page layout and document structure
src/layouts/PageLayout.astro
HTML scaffold with metadata (title/description/OG/Twitter), canonical URL computation, client router, skip-nav, TopNav wiring, main slot and footer, and accessibility-focused styles.
Homepage section components
src/components/homepage/HeroEditorial.astro, src/components/homepage/EvidenceInlineStrip.astro, src/components/homepage/BuildJudgeOperateCards.astro, src/components/homepage/WhatsNewStrip.astro, src/components/homepage/ManifestoPullquote.astro
Hero band with stamp and kinetic H1, evidence stats strip with count-ups, three-card book parts grid, "What's new" publication strip with lazy images, and manifesto pullquote band.
Homepage and manifesto pages
src/pages/index.astro, src/pages/manifesto.astro
Homepage composed from sections inside Container; manifesto page with hero, ordered principles, pull quote, closing paragraph, and revision date styling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A tiny rabbit stamps the site with flair,
Tokens and headings dance in morning air,
Pages build and numbers climb up high,
Manifesto red as sunset sky —
Hop, deploy; the Agentic nest is fair.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main deliverables of this comprehensive PR: Astro scaffold, design system, homepage, and manifesto.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch astro-redesign-w1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (9)
src/components/homepage/HeroEditorial.astro (1)

20-22: ⚡ Quick win

Hardcoded base path may break portability.

The CTA hrefs contain /agentic-ai/ directly. According to the PR summary, the Astro build uses base: '/agentic-ai', but hardcoding this path prevents deployment to a different base (e.g., root / or a different subdirectory). Consider using import.meta.env.BASE_URL to construct paths dynamically.

♻️ Proposed fix

In the frontmatter:

 import KineticHeading from '~/components/universal/KineticHeading.astro';
 import Stamp from '~/components/universal/Stamp.astro';
+
+const base = import.meta.env.BASE_URL;
 ---

Then in the markup:

   <div class="hero__ctas">
-    <a href="/agentic-ai/start/" class="hero__cta hero__cta--primary">Start →</a>
-    <a href="/agentic-ai/book/" class="hero__cta">Read the book</a>
-    <a href="/agentic-ai/evidence/" class="hero__cta">See the evidence</a>
+    <a href={`${base}start/`} class="hero__cta hero__cta--primary">Start →</a>
+    <a href={`${base}book/`} class="hero__cta">Read the book</a>
+    <a href={`${base}evidence/`} class="hero__cta">See the evidence</a>
   </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/homepage/HeroEditorial.astro` around lines 20 - 22, The three
hardcoded CTA links in HeroEditorial.astro (the <a> elements with classes
hero__cta and hero__cta--primary) embed the base path "/agentic-ai" which breaks
portability; update the frontmatter to read import.meta.env.BASE_URL (or a small
helper like const base = import.meta.env.BASE_URL || "/") and then construct
each href by prefixing the route (e.g., "start/", "book/", "evidence/") with
that base variable so the anchors use dynamic paths instead of hardcoded
"/agentic-ai/".
src/components/client/count-up.ts (1)

37-54: ⚡ Quick win

Return the observer for cleanup to prevent accumulation.

observeCountUps creates a new IntersectionObserver on each call but never returns it or provides a way to disconnect it. When called multiple times (e.g., on each astro:page-load navigation in EvidenceInlineStrip.astro), old observers accumulate in memory even though they've unobserved all targets.

♻️ Proposed fix
-export function observeCountUps(selector = '[data-count-to]'): void {
+export function observeCountUps(selector = '[data-count-to]'): IntersectionObserver | null {
   const targets = document.querySelectorAll<HTMLElement>(selector);
-  if (!targets.length) return;
+  if (!targets.length) return null;
 
   const observer = new IntersectionObserver((entries) => {
     for (const entry of entries) {
       if (!entry.isIntersecting) continue;
       const el = entry.target as HTMLElement;
       const to = parseFloat(el.dataset.countTo ?? '0');
       const decimals = parseInt(el.dataset.countDecimals ?? '1', 10);
       const suffix = el.dataset.countSuffix ?? '';
       startCountUp(el, { to, decimals, suffix });
       observer.unobserve(el);
     }
   }, { threshold: 0.5 });
 
   targets.forEach((el) => observer.observe(el));
+  return observer;
 }

Then callers can optionally disconnect if needed:

const observer = observeCountUps();
// Later: observer?.disconnect();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/client/count-up.ts` around lines 37 - 54, The observeCountUps
function currently creates an IntersectionObserver but never returns it; change
observeCountUps (the exported function) to return the created
IntersectionObserver (or null) so callers can disconnect it for cleanup, i.e.,
create the observer as now (used to call startCountUp) and return it at the end,
and when no targets exist return null; update call sites to capture the returned
IntersectionObserver and call .disconnect() when appropriate.
public/paint/brick-stamp.js (2)

27-27: ⚡ Quick win

Add an upper bound to dot count for performance.

For very large elements (e.g., a 4000×3000px stamp), the formula (w * h) / 240 would create 50,000 dots, which could cause janky paint performance. Consider capping the dot count.

⚡ Proposed fix
-    const dots = Math.floor((w * h) / 240);
+    const dots = Math.min(Math.floor((w * h) / 240), 800);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/paint/brick-stamp.js` at line 27, The dot-count calculation uses const
dots = Math.floor((w * h) / 240) which can explode for very large stamps; add an
upper bound by introducing a MAX_DOTS constant (e.g., 5000 or 10000) and compute
dots as the min of the current formula and MAX_DOTS (or clamp it using
Math.min/Math.max), then use that bounded dots value throughout the stamping
logic (update references in brick-stamp.js where dots is used to ensure
performance on large canvases).

25-35: ⚡ Quick win

Consider a deterministic texture pattern to prevent repaint flicker.

Using Math.random() for dot placement means the texture pattern regenerates on every paint cycle, potentially causing visual flicker or inconsistency during browser repaints (resize, scroll, etc.). For a stamp texture, a deterministic pattern or seeded random would provide more stable rendering.

♻️ Example: Use geometry-based seeding
     // Texture noise: scatter darker dots
     ctx.fillStyle = `rgba(120, 50, 40, 0.18)`;
     const dots = Math.floor((w * h) / 240);
+    // Use width/height as seed for deterministic pattern
+    const seed = (w * 31 + h * 37) % 1000;
     for (let i = 0; i < dots; i++) {
-      const x = Math.random() * w;
-      const y = Math.random() * h;
-      const r = Math.random() * 1.6;
+      // Simple pseudo-random based on i and seed
+      const px = ((i * 73 + seed) % 1000) / 1000;
+      const py = ((i * 97 + seed) % 1000) / 1000;
+      const pr = ((i * 53 + seed) % 1000) / 1000;
+      const x = px * w;
+      const y = py * h;
+      const r = pr * 1.6;
       ctx.beginPath();
       ctx.arc(x, y, r, 0, Math.PI * 2);
       ctx.fill();
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/paint/brick-stamp.js` around lines 25 - 35, The noise-dot loop in
public/paint/brick-stamp.js uses Math.random() for x, y, r causing
non-deterministic repaint flicker; replace it with a deterministic PRNG seeded
from stable inputs (e.g., stamp coordinates, size, or an explicit seed) and use
that PRNG for x/y/r generation in the loop that computes dots (the
variables/operations to change are dots, x, y, r, ctx.arc and ctx.fill calls);
implement a small seeded generator (e.g., mulberry32/xorshift) and derive the
seed from stable properties so the same pattern is produced on every paint.
src/components/universal/Stamp.astro (1)

31-31: ⚡ Quick win

Use import.meta.env.BASE_URL for asset paths.

The SVG fallback and paint worklet paths are hardcoded with /agentic-ai/ prefix. These should use Astro's BASE_URL environment variable for portability.

♻️ Proposed refactor

Import the base URL in the frontmatter:

 /**
  * Stamp — brick-red textured stamp. Houdini paint worklet with SVG fallback.
  * Used on homepage hero, manifesto, pullquote corners as brand signature.
  */
+const base = import.meta.env.BASE_URL;
+
 interface Props {

Update the fallback background:

   `@supports` not (background: paint(brick-stamp)) {
     .stamp {
-      background-image: url('/agentic-ai/svg/brick-stamp.svg');
+      background-image: url(`${base}svg/brick-stamp.svg`);
       background-repeat: no-repeat;

Note: The script path at line 43 cannot use the frontmatter variable. Consider defining the base path in a shared constant or using a define:vars directive to pass it to the script, or document that this path requires manual updates if the base changes.

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/universal/Stamp.astro` at line 31, The CSS background-image
path in Stamp.astro is hardcoded to "/agentic-ai/..." which breaks portability;
change it to use Astro's base URL by referencing import.meta.env.BASE_URL (or a
shared constant) so the URL becomes BASE_URL + "svg/brick-stamp.svg"; likewise
update any other hardcoded asset references (the paint worklet/script path
mentioned around the paint worklet include) to use the same BASE_URL mechanism —
if the paint worklet path is used inside an inline script that cannot access
frontmatter vars, expose the base via a shared constant or use Astro's
define:vars to inject it into the client script so all asset URLs
(background-image, SVG fallback, paint worklet script) are built from the same
import.meta.env.BASE_URL.
src/components/universal/SiteFooter.astro (1)

22-24: ⚡ Quick win

Use import.meta.env.BASE_URL instead of hardcoded base path.

The footer links hardcode the /agentic-ai/ base path. If the site's base path changes in astro.config.mjs, these links will break.

♻️ Proposed refactor

Add to the frontmatter:

 /**
  * SiteFooter — bottom of every page.
  * Cadence strip, secondary nav, social, copyright.
  */
+const base = import.meta.env.BASE_URL;
 ---

Then update the links:

-        <a href="/agentic-ai/manifesto/">Manifesto</a>
-        <a href="/agentic-ai/patterns/">Patterns</a>
-        <a href="/agentic-ai/roadmap/">Roadmap</a>
+        <a href={`${base}manifesto/`}>Manifesto</a>
+        <a href={`${base}patterns/`}>Patterns</a>
+        <a href={`${base}roadmap/`}>Roadmap</a>
       </div>
       <div class="site-footer__col">
         <div class="site-footer__col-label">Build</div>
-        <a href="/agentic-ai/code/">Code reference</a>
+        <a href={`${base}code/`}>Code reference</a>

Also applies to: 28-28

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/universal/SiteFooter.astro` around lines 22 - 24, The footer
currently hardcodes the "/agentic-ai/" base path in the anchor hrefs inside
SiteFooter.astro; update the frontmatter to read import.meta.env.BASE_URL into a
constant (e.g. BASE_URL) and replace each hardcoded href (e.g.
"/agentic-ai/manifesto/", "/agentic-ai/patterns/", "/agentic-ai/roadmap/") with
a concatenation using that BASE_URL (BASE_URL + "agentic-ai/manifesto/" etc.),
and apply the same change to the other link at line 28 so all links use
import.meta.env.BASE_URL instead of literal paths.
src/layouts/PageLayout.astro (1)

31-31: ⚡ Quick win

Use import.meta.env.BASE_URL instead of hardcoding the base path.

The favicon path hardcodes /agentic-ai/, but Astro provides import.meta.env.BASE_URL for this purpose. This makes the code more maintainable and consistent with the base path configured in astro.config.mjs.

♻️ Proposed refactor
-    <link rel="icon" href="/agentic-ai/favicon.png" type="image/png" />
+    <link rel="icon" href={`${import.meta.env.BASE_URL}favicon.png`} type="image/png" />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/layouts/PageLayout.astro` at line 31, The favicon link in
PageLayout.astro currently hardcodes the base path
(href="/agentic-ai/favicon.png"); update the link element to build href using
import.meta.env.BASE_URL so the base path comes from config (e.g., use
import.meta.env.BASE_URL + "favicon.png"), keeping the rel and type attributes
the same and ensuring this change is applied in the <link ...> element in
PageLayout.astro.
src/content.config.ts (2)

27-27: 💤 Low value

Consider URL validation for the banner field.

The banner field is typed as z.string() without URL validation, while other URL fields in the schema (e.g., sources.url on line 36) use z.string().url(). If banner represents an image URL or path that should be validated, consider adding .url() validation for consistency. If it's intentionally a flexible string (e.g., supporting both local paths and URLs), the current type is acceptable.

🔧 Suggested validation if banner is always a URL
-    banner: z.string(),
+    banner: z.string().url(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/content.config.ts` at line 27, The banner field in the schema is
currently z.string() without URL validation; if banner is meant to be an image
URL enforce z.string().url() for consistency with other URL fields (e.g.,
sources.url) by replacing the banner definition with a URL-validated string,
otherwise leave as-is and add a comment explaining that local paths are allowed
so validators are not changed; reference the banner schema entry named "banner"
to locate and update it.

11-13: 💤 Low value

Consider extracting shared schema fragments for common cross-reference fields.

The references, cites, and patterns fields appear across multiple collections with identical structure (z.array(z.string()).optional()). Extracting these into shared schema fragments would reduce duplication and ensure consistency.

♻️ Optional DRY refactor
+// Shared schema fragments for cross-references
+const crossRefFields = {
+  references: z.array(z.string()).optional(),
+  cites: z.array(z.string()).optional(),
+  patterns: z.array(z.string()).optional(),
+};
+
 const chapters = defineCollection({
   loader: glob({ pattern: '**/*.mdx', base: './src/content/chapters' }),
   schema: z.object({
     title: z.string(),
     part: z.enum(['foundations', 'I-build', 'II-judge', 'III-operate', 'IV-advanced']),
     description: z.string(),
     readingTime: z.number(),
-    references: z.array(z.string()).optional(),
-    cites: z.array(z.string()).optional(),
-    patterns: z.array(z.string()).optional(),
+    ...crossRefFields,
     date: z.coerce.date(),
     status: z.enum(['draft', 'published']).default('published'),
   }),
 });

Apply similar changes to fieldNotes, recipes, projects, and labs collections.

Also applies to: 31-33, 56-57, 78-78, 118-118

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/content.config.ts` around lines 11 - 13, Extract a shared Zod fragment
for the repeated cross-reference schema and reuse it instead of duplicating
z.array(z.string()).optional(): create a constant like crossRefs =
z.array(z.string()).optional() (or named crossReferenceSchema) and replace the
inline definitions for references, cites, and patterns in the affected
collection schemas (e.g., the schemas containing fields references, cites,
patterns and the collections named fieldNotes, recipes, projects, labs) to
reference that constant; ensure any exported types or inferred schemas still
work (update any uses of the previous inline shape if needed) and run typechecks
to confirm no type regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/homepage/EvidenceInlineStrip.astro`:
- Around line 45-49: The current EvidenceInlineStrip.astro script repeatedly
calls observeCountUps() both immediately and on each astro:page-load, which
creates new IntersectionObservers without disconnecting old ones; update
observeCountUps (or its consumer) so it is idempotent or returns a cleanup
function: modify the observeCountUps function to store its created observer in a
module-scoped variable and return a disconnect() cleanup, then in
EvidenceInlineStrip.astro call the returned cleanup before creating a new
observer (or call the idempotent observeCountUps that reuses the existing
observer) and also call cleanup on astro:page-load before re-initializing to
prevent observer accumulation. Ensure references to observeCountUps and the
astro:page-load listener are updated accordingly.

In `@src/components/homepage/HeroEditorial.astro`:
- Around line 59-76: The CTA styles (.hero__cta and .hero__cta--primary) only
define :hover states and lack keyboard focus indicators; add :focus-visible
rules for both selectors to mirror the hover visual cues (e.g., change color and
border-bottom-color for .hero__cta:focus-visible and change background/color for
.hero__cta--primary:focus-visible), ensure outline is visible or use a clear
focus ring for accessibility, and keep transitions consistent with the existing
transition declarations.

In `@src/components/layout/FullBleed.astro`:
- Around line 19-23: The current .full-bleed rule uses margin-left: 50% +
transform: translateX(-50%), which can trigger horizontal overflow; replace the
translate centering with margin-based breakout by removing transform and
margin-left:50% and instead set margin-left: calc(50% - 50vw) and margin-right:
calc(50% - 50vw) (keeping width:100vw) so the .full-bleed element breaks out
without causing scroll; update the .full-bleed CSS accordingly.

In `@src/components/layout/MagazineGrid.astro`:
- Around line 12-15: Normalize the cols prop before injecting into CSS by
converting Astro.props.cols to a safe integer >= 1; for example, compute a
normalizedCols (e.g., Number(cols) -> Math.floor -> fallback to default ->
Math.max(1, ...)) and use normalizedCols in the style attribute instead of cols
so repeat(var(--cols), 1fr) always receives a positive integer; update
MagazineGrid's destructuring/templating to reference this normalizedCols
variable (cols, className remain the identifying symbols).

In `@src/components/layout/Split.astro`:
- Around line 15-20: The ratio parsing in Split (where
ratio.split(':').map(Number) produces a and b) is unguarded and can yield NaN
causing invalid CSS like "NaNfr"; update the parsing to validate and sanitize
values: split ratio by ':' (ratio.split(':')), coerce each part to Number,
ensure Number.isFinite and > 0 for both a and b, and if either is invalid
fallback to safe defaults (e.g., 1 and 1 or a single numeric fallback), then use
those sanitized a and b when building the style (--col-a, --col-b, --gap) so the
grid never receives NaN.

In `@src/components/universal/KineticHeading.astro`:
- Around line 20-42: The flash occurs because .kinetic-heading has no initial
opacity so it briefly shows before the script adds data-kinetic-ready which sets
opacity: 0 and animates; fix it by making the base .kinetic-heading start hidden
(e.g., set opacity: 0 in the .kinetic-heading rule) and let the
.kinetic-heading[data-kinetic-ready] rule drive the fade-in to opacity: 1 via
the existing kinetic-fade animation; also keep the prefers-reduced-motion rule
so .kinetic-heading[data-kinetic-ready] sets opacity: 1 and disables animation
for accessibility.

In `@src/components/universal/TopNav.astro`:
- Around line 29-32: The active link only gets a visual class; update the anchor
rendered in TopNav (the <a> with href={item.href}, class:list using
'topnav__link--current' and the current variable) to also set
aria-current="page" when current === item.label (otherwise omit the attribute)
so screen readers receive the active-page state.

In `@src/styles/tokens/spacing.css`:
- Line 1: Update the top comment to correctly state the base spacing unit as 4px
(not 8px) to match the CSS variables (e.g., --space-1: 4px) and the rest of the
scale; locate the file's header comment (the /* Spacing tokens (8px base) —
Section 8.3 of design spec */ line) and change its text to reflect a 4px base so
it aligns with the --space-* variables.

---

Nitpick comments:
In `@public/paint/brick-stamp.js`:
- Line 27: The dot-count calculation uses const dots = Math.floor((w * h) / 240)
which can explode for very large stamps; add an upper bound by introducing a
MAX_DOTS constant (e.g., 5000 or 10000) and compute dots as the min of the
current formula and MAX_DOTS (or clamp it using Math.min/Math.max), then use
that bounded dots value throughout the stamping logic (update references in
brick-stamp.js where dots is used to ensure performance on large canvases).
- Around line 25-35: The noise-dot loop in public/paint/brick-stamp.js uses
Math.random() for x, y, r causing non-deterministic repaint flicker; replace it
with a deterministic PRNG seeded from stable inputs (e.g., stamp coordinates,
size, or an explicit seed) and use that PRNG for x/y/r generation in the loop
that computes dots (the variables/operations to change are dots, x, y, r,
ctx.arc and ctx.fill calls); implement a small seeded generator (e.g.,
mulberry32/xorshift) and derive the seed from stable properties so the same
pattern is produced on every paint.

In `@src/components/client/count-up.ts`:
- Around line 37-54: The observeCountUps function currently creates an
IntersectionObserver but never returns it; change observeCountUps (the exported
function) to return the created IntersectionObserver (or null) so callers can
disconnect it for cleanup, i.e., create the observer as now (used to call
startCountUp) and return it at the end, and when no targets exist return null;
update call sites to capture the returned IntersectionObserver and call
.disconnect() when appropriate.

In `@src/components/homepage/HeroEditorial.astro`:
- Around line 20-22: The three hardcoded CTA links in HeroEditorial.astro (the
<a> elements with classes hero__cta and hero__cta--primary) embed the base path
"/agentic-ai" which breaks portability; update the frontmatter to read
import.meta.env.BASE_URL (or a small helper like const base =
import.meta.env.BASE_URL || "/") and then construct each href by prefixing the
route (e.g., "start/", "book/", "evidence/") with that base variable so the
anchors use dynamic paths instead of hardcoded "/agentic-ai/".

In `@src/components/universal/SiteFooter.astro`:
- Around line 22-24: The footer currently hardcodes the "/agentic-ai/" base path
in the anchor hrefs inside SiteFooter.astro; update the frontmatter to read
import.meta.env.BASE_URL into a constant (e.g. BASE_URL) and replace each
hardcoded href (e.g. "/agentic-ai/manifesto/", "/agentic-ai/patterns/",
"/agentic-ai/roadmap/") with a concatenation using that BASE_URL (BASE_URL +
"agentic-ai/manifesto/" etc.), and apply the same change to the other link at
line 28 so all links use import.meta.env.BASE_URL instead of literal paths.

In `@src/components/universal/Stamp.astro`:
- Line 31: The CSS background-image path in Stamp.astro is hardcoded to
"/agentic-ai/..." which breaks portability; change it to use Astro's base URL by
referencing import.meta.env.BASE_URL (or a shared constant) so the URL becomes
BASE_URL + "svg/brick-stamp.svg"; likewise update any other hardcoded asset
references (the paint worklet/script path mentioned around the paint worklet
include) to use the same BASE_URL mechanism — if the paint worklet path is used
inside an inline script that cannot access frontmatter vars, expose the base via
a shared constant or use Astro's define:vars to inject it into the client script
so all asset URLs (background-image, SVG fallback, paint worklet script) are
built from the same import.meta.env.BASE_URL.

In `@src/content.config.ts`:
- Line 27: The banner field in the schema is currently z.string() without URL
validation; if banner is meant to be an image URL enforce z.string().url() for
consistency with other URL fields (e.g., sources.url) by replacing the banner
definition with a URL-validated string, otherwise leave as-is and add a comment
explaining that local paths are allowed so validators are not changed; reference
the banner schema entry named "banner" to locate and update it.
- Around line 11-13: Extract a shared Zod fragment for the repeated
cross-reference schema and reuse it instead of duplicating
z.array(z.string()).optional(): create a constant like crossRefs =
z.array(z.string()).optional() (or named crossReferenceSchema) and replace the
inline definitions for references, cites, and patterns in the affected
collection schemas (e.g., the schemas containing fields references, cites,
patterns and the collections named fieldNotes, recipes, projects, labs) to
reference that constant; ensure any exported types or inferred schemas still
work (update any uses of the previous inline shape if needed) and run typechecks
to confirm no type regressions.

In `@src/layouts/PageLayout.astro`:
- Line 31: The favicon link in PageLayout.astro currently hardcodes the base
path (href="/agentic-ai/favicon.png"); update the link element to build href
using import.meta.env.BASE_URL so the base path comes from config (e.g., use
import.meta.env.BASE_URL + "favicon.png"), keeping the rel and type attributes
the same and ensuring this change is applied in the <link ...> element in
PageLayout.astro.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf551994-0542-4687-9b07-e83e5cdc0c2b

📥 Commits

Reviewing files that changed from the base of the PR and between 5587b4a and 1497f3e.

⛔ Files ignored due to path filters (4)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/favicon.png is excluded by !**/*.png
  • public/og-default.png is excluded by !**/*.png
  • public/svg/brick-stamp.svg is excluded by !**/*.svg
📒 Files selected for processing (53)
  • .github/workflows/astro-deploy.yml
  • .gitignore
  • .nvmrc
  • astro.config.mjs
  • package.json
  • public/paint/brick-stamp.js
  • src/components/client/count-up.ts
  • src/components/homepage/BuildJudgeOperateCards.astro
  • src/components/homepage/EvidenceInlineStrip.astro
  • src/components/homepage/HeroEditorial.astro
  • src/components/homepage/ManifestoPullquote.astro
  • src/components/homepage/WhatsNewStrip.astro
  • src/components/layout/Container.astro
  • src/components/layout/FullBleed.astro
  • src/components/layout/MagazineGrid.astro
  • src/components/layout/Reader.astro
  • src/components/layout/Split.astro
  • src/components/universal/Brand.astro
  • src/components/universal/Breadcrumb.astro
  • src/components/universal/Cadence.astro
  • src/components/universal/Dek.astro
  • src/components/universal/KineticHeading.astro
  • src/components/universal/MetaStrip.astro
  • src/components/universal/PullQuote.astro
  • src/components/universal/SiteFooter.astro
  • src/components/universal/Stamp.astro
  • src/components/universal/Tag.astro
  • src/components/universal/TopNav.astro
  • src/content.config.ts
  • src/content/chapters/.gitkeep
  • src/content/evidence/.gitkeep
  • src/content/fieldNotes/.gitkeep
  • src/content/labs/.gitkeep
  • src/content/patterns/.gitkeep
  • src/content/projects/.gitkeep
  • src/content/recipes/.gitkeep
  • src/env.d.ts
  • src/layouts/PageLayout.astro
  • src/lib/cross-links.test.ts
  • src/lib/cross-links.ts
  • src/lib/reading-time.test.ts
  • src/lib/reading-time.ts
  • src/pages/index.astro
  • src/pages/manifesto.astro
  • src/styles/global.css
  • src/styles/reset.css
  • src/styles/tokens/colors.css
  • src/styles/tokens/motion.css
  • src/styles/tokens/spacing.css
  • src/styles/tokens/surfaces.css
  • src/styles/tokens/typography.css
  • tsconfig.json
  • vitest.config.ts

Comment thread src/components/homepage/EvidenceInlineStrip.astro
Comment thread src/components/homepage/HeroEditorial.astro
Comment thread src/components/layout/FullBleed.astro
Comment thread src/components/layout/MagazineGrid.astro Outdated
Comment thread src/components/layout/Split.astro Outdated
Comment thread src/components/universal/KineticHeading.astro
Comment thread src/components/universal/TopNav.astro
Comment thread src/styles/tokens/spacing.css Outdated
@sunilp sunilp merged commit eb36318 into master May 13, 2026
2 of 3 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.

1 participant