diff --git a/apps/website/next-env.d.ts b/apps/website/next-env.d.ts index fdbfe5258..c4b7818fb 100644 --- a/apps/website/next-env.d.ts +++ b/apps/website/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./../../dist/apps/website/.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/website/src/app/about/page.spec.tsx b/apps/website/src/app/about/page.spec.tsx index ffa3d47ed..6e4b41964 100644 --- a/apps/website/src/app/about/page.spec.tsx +++ b/apps/website/src/app/about/page.spec.tsx @@ -5,6 +5,7 @@ import { describe, expect, it, vi } from 'vitest'; import { render, screen } from '@testing-library/react'; import AboutPage from './page'; import { getAuthor } from '../../lib/blog-authors'; +import { ABOUT_PARAGRAPHS } from '../../lib/about-content'; vi.mock('../../components/ui/Container', () => ({ Container: ({ children }: { children: React.ReactNode }) =>
{children}
, @@ -24,17 +25,36 @@ const author = getAuthor('brian'); * claims back to the author record rather than restating the layout. */ describe('AboutPage', () => { - it('renders the bio verbatim from the author record', () => { + it('renders each sourced paragraph verbatim', () => { render(); - // Not a substring match: any editorial embellishment around the sourced - // sentence would still be a claim nothing in the repo supports. - expect(screen.getByText(author.bio as string).textContent).toBe(author.bio); + // Not substring matches: editorial embellishment around a sourced sentence + // would still be a claim nothing citable supports. + for (const paragraph of ABOUT_PARAGRAPHS) { + expect(screen.getByText(paragraph).textContent).toBe(paragraph); + } }); - it('states the name and role the author record gives, and no other', () => { + it('adds no biographical prose beyond the sourced paragraphs', () => { + // The real guard. Every claim on this page has to trace to about-content.ts, + // whose provenance comment names the source; a paragraph appearing here that + // is not in that module is exactly the fabrication this page must not carry. + const { container } = render(); + const sourced = new Set(ABOUT_PARAGRAPHS); + + const biography = container.querySelectorAll('section')[0]; + const paragraphs = Array.from(biography.querySelectorAll('p')) + .map((node) => node.textContent ?? '') + .filter((text) => text.split(' ').length > 12); + + expect(paragraphs.length).toBe(ABOUT_PARAGRAPHS.length); + for (const text of paragraphs) { + expect(sourced.has(text), text).toBe(true); + } + }); + + it('names the author the record gives', () => { render(); - expect(screen.getByText(`Threadplane is written and maintained by ${author.name}, ${author.role}.`)) - .toBeTruthy(); + expect(screen.getByText(ABOUT_PARAGRAPHS[0]).textContent).toContain(author.name); }); it('links the GitHub profile the record names', () => { diff --git a/apps/website/src/app/about/page.tsx b/apps/website/src/app/about/page.tsx index 696662841..0f63937a9 100644 --- a/apps/website/src/app/about/page.tsx +++ b/apps/website/src/app/about/page.tsx @@ -9,6 +9,13 @@ import { aboutPageJsonLd, REPOSITORY_URL } from '../../lib/structured-data'; import { getAuthor } from '../../lib/blog-authors'; import { createPageMetadata } from '../../lib/site-metadata'; import { LONG_SUBHEAD } from '../../lib/positioning'; +import { + ABOUT_HISTORY, + ABOUT_HISTORY_HEADING, + ABOUT_INTRO, + ABOUT_PERSONAL, + ABOUT_PRINCIPLES, +} from '../../lib/about-content'; /** * The single author record the site already publishes (blog bylines read the @@ -68,10 +75,18 @@ export default function AboutPage() { > Who writes Threadplane -

- Threadplane is written and maintained by {author.name}, {author.role}. -

-

{author.bio}

+

{ABOUT_INTRO}

+ +

{ABOUT_HISTORY_HEADING}

+ {ABOUT_HISTORY.map((paragraph) => ( +

+ {paragraph} +

+ ))} + +

{ABOUT_PRINCIPLES}

+

{ABOUT_PERSONAL}

+

= { brian: { name: 'Brian Love', role: 'Founder, Threadplane', - bio: 'Angular consultant and open-source maintainer. Building agent UI for Angular teams.', + bio: 'Agentic software architect building developer tooling for fullstack AI-powered web applications.', knowsAbout: ['Angular', 'TypeScript', 'LangGraph', 'AG-UI', 'Generative UI', 'Agent user interfaces'], github: 'blove', },