diff --git a/website/AGENTS.md b/website/AGENTS.md index 9d6e68c0b..cb56d354a 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -24,7 +24,15 @@ A static documentation site published to GitHub Pages. ## Where things live - `src/content/docs/` — all user-facing pages (`.md` and `.mdx`) - - `introduction/`, `getting-started/`, `guides/`, `reference/`, `resources/` + - `introduction/`, `getting-started/`, `guides/`, `reference/`, + `resources/`, `community/` +- `src/content/articles/`, `src/content/talks/` — one YAML file per + published article / talk, schema-validated by the `articles` and + `talks` collections in `src/content.config.ts` (see + "Articles & talks" below) +- `src/components/community/` — `ArticleList`, `TalkList`, + `TalkCard`, and `CommunityTeaser` (the landing-page section), all + reading those collections - Sidebar order is defined in `astro.config.mjs`, not by directory order - `src/openapi/scope-openapi.json` — committed artifact generated from the Scope API's OpenAPI registry; drives the auto-generated REST @@ -152,6 +160,50 @@ Sidebar order is set in `astro.config.mjs`. Adding a new page requires updating the sidebar array. The auto-generated REST API groups are spread via `...openAPISidebarGroups`. +### Articles & talks + +The `community/articles-and-talks` page and the "From the community" +section on the landing page are generated from two content +collections. To add an entry, add one YAML file. No code changes are +needed. + +- **Article**: `src/content/articles/.yaml` + ```yaml + title: Building AX evals that actually work + url: https://developer.microsoft.com/blog/building-ax-evals-that-actually-work/ + publication: Microsoft for Developers # blog name + authors: # as credited, byline order + - firstName: Waldek + lastName: Mastykarz + position: Principal Developer Advocate + date: 2026-07-15 # publish date (optional) + ``` +- **Talk**: `src/content/talks/-.yaml` + ```yaml + title: "From Findings to Fixes: ..." + speakers: # same shape as authors + - firstName: Jay + lastName: Gordon + position: Senior Program Manager, Azure Cosmos DB + event: Global AI New York + venue: Microsoft Lafayette, New York City + date: 2026-09-21 + eventUrl: https://globalai.community/e/783bfa20 # GAIC event page + youtubeId: SxaKOmqX-rk # omit while pending + ``` + +Take title, blog name, authors (name and position from the article's +author section), and publish date from the article page itself. +Take speaker positions from the event page or the speaker's event +profile (e.g. their Luma bio). +If a date can't be confirmed, leave `date` out; undated articles +sort last. A talk without `youtubeId` shows "Video coming soon". +Both lists sort newest first. A missing field, bad URL, or bad date +fails `pnpm run build`. Article and event links are external, so the +components open them in a new tab (`target="_blank" +rel="noopener noreferrer"`) with a screen-reader "(opens in a new +tab)" hint; keep that pattern for any new external link. + ### Style - Hard-wrap prose at ~70–80 columns for readable diffs. @@ -177,7 +229,7 @@ pnpm run refresh:openapi # generate the OpenAPI snapshot from scope-core Both `pnpm test` and `pnpm run build` must pass. The public build uses `SITE=https://microsoft.github.io BASE_PATH=/scope pnpm run build`; exercise that configuration when changing links or deployment settings, -not just the local `/` default. The current snapshot produces **200 +not just the local `/` default. The current snapshot produces **203 pages**, including the generated API reference. An unexpected drop in page count can indicate a content collection file failed to parse. diff --git a/website/README.md b/website/README.md index a54ad0414..d5f4e5824 100644 --- a/website/README.md +++ b/website/README.md @@ -16,12 +16,17 @@ The product and this documentation site live in ├── public/ # static assets ├── src/ │ ├── assets/ -│ ├── content/docs/ # all user-facing pages (.md / .mdx) -│ │ ├── introduction/ -│ │ ├── getting-started/ -│ │ ├── guides/ -│ │ ├── reference/ -│ │ └── resources/ +│ ├── components/community/ # article/talk lists + landing teaser +│ ├── content/ +│ │ ├── docs/ # all user-facing pages (.md / .mdx) +│ │ │ ├── introduction/ +│ │ │ ├── getting-started/ +│ │ │ ├── guides/ +│ │ │ ├── reference/ +│ │ │ ├── resources/ +│ │ │ └── community/ +│ │ ├── articles/ # one YAML per published article +│ │ └── talks/ # one YAML per talk │ ├── openapi/scope-openapi.json # artifact generated from the API registry │ ├── plugins/ │ │ ├── remark-base-path.mjs # applies the deployment base to internal links @@ -72,6 +77,10 @@ Sidebar order is defined in `astro.config.mjs`, not by directory order. `scope-core` checkout, so root workspace dependencies must be installed first. +- To list a new article or talk on the Community page, add one YAML + file under `src/content/articles/` or `src/content/talks/`. See + "Articles & talks" in [AGENTS.md](AGENTS.md) for the fields. + See [AGENTS.md](AGENTS.md) for conventions, the source-of-truth policy (everything factual must be grounded in scope-core), and where to look in scope-core for any given topic. diff --git a/website/astro.config.mjs b/website/astro.config.mjs index e6d7d4179..3f662e009 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -86,6 +86,10 @@ export default defineConfig({ { label: 'Concepts', slug: 'introduction/concepts' }, ], }, + { + label: 'Community', + items: [{ label: 'Articles & talks', slug: 'community/articles-and-talks' }], + }, { label: 'Getting Started', items: [ diff --git a/website/src/components/community/ArticleList.astro b/website/src/components/community/ArticleList.astro new file mode 100644 index 000000000..cd2ff166a --- /dev/null +++ b/website/src/components/community/ArticleList.astro @@ -0,0 +1,80 @@ +--- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Byline from './Byline.astro'; +import { formatDate, getArticles, isoDate } from './community'; + +interface Props { + /** Show only the N most recent articles. */ + limit?: number; +} + +const { limit } = Astro.props; +const all = await getArticles(); +const articles = limit === undefined ? all : all.slice(0, limit); +--- + + + + diff --git a/website/src/components/community/Byline.astro b/website/src/components/community/Byline.astro new file mode 100644 index 000000000..265ebd3fa --- /dev/null +++ b/website/src/components/community/Byline.astro @@ -0,0 +1,36 @@ +--- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { Person } from './community'; + +interface Props { + people: Person[]; +} + +const { people } = Astro.props; +--- + + + + diff --git a/website/src/components/community/CommunityTeaser.astro b/website/src/components/community/CommunityTeaser.astro new file mode 100644 index 000000000..7a7624e03 --- /dev/null +++ b/website/src/components/community/CommunityTeaser.astro @@ -0,0 +1,37 @@ +--- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import ArticleList from './ArticleList.astro'; +import TalkCard from './TalkCard.astro'; +import { getTalks } from './community'; + +const talks = await getTalks(); +const featured = talks.find((t) => t.data.youtubeId) ?? talks[0]; +const allHref = `${import.meta.env.BASE_URL.replace(/\/$/, '')}/community/articles-and-talks/`; +--- + +
+
+ From the community +

Articles & talks

+

What the community has learned evaluating agentic product experiences with Scope, from the blog and the stage.

+
+ +
+
+

Latest articles

+ +
+ { + featured && ( +
+

Featured talk

+ +
+ ) + } +
+ + See all articles & talks → +
diff --git a/website/src/components/community/TalkCard.astro b/website/src/components/community/TalkCard.astro new file mode 100644 index 000000000..b4edff40a --- /dev/null +++ b/website/src/components/community/TalkCard.astro @@ -0,0 +1,109 @@ +--- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Byline from './Byline.astro'; +import { formatDate, isoDate, type Talk } from './community'; + +interface Props { + talk: Talk; +} + +const { data } = Astro.props.talk; +--- + +
+
+ { + data.youtubeId ? ( +