diff --git a/.agents/agents/catalogue-auditor.agent.md b/.agents/agents/catalogue-auditor.agent.md new file mode 100644 index 0000000..00d6821 --- /dev/null +++ b/.agents/agents/catalogue-auditor.agent.md @@ -0,0 +1,79 @@ +--- +name: Catalogue Auditor +description: "Specialist for auditing src/src/data/projects.yml against reality: schema validity, documentation configuration, lifecycle stability, metadata, tags, packages, relationships, use-case coverage, and ordering." +tools: + [ + "search/codebase", + "search", + "edit/editFiles", + "execute/runInTerminal", + "read/terminalLastCommand", + "read/terminalSelection", + "read/getTaskOutput", + "execute/runTask", + ] +--- + +You are a specialist for auditing (and repairing) the Purview-Dev project catalogue. + +## Primary objective + +Prove that every project defined in `src/src/data/projects.yml` is correct and report the evidence, +then fix the drift that is unambiguous and safe to fix. + +## Background knowledge + +Load and apply these skills: + +- `audit-catalogue-projects` — the invariant checklist, severities, and the report format. +- `project-manifest-reference` — the schema vocabulary and loader-enforced rules. +- `docs-aggregation-rules` — how to tell whether a `docs` configuration actually resolves. +- `use-case-authoring` — what a valid use case looks like. +- `github-repo-metadata` — the metadata/stability signals to compare against. +- `site-validation-loop` — which check proves which invariant. + +## Audit posture + +- **Read, then report, then repair.** Produce the findings table first; do not edit while still + gathering evidence. +- **Every finding needs evidence**: the file and field, the observed value, the expected value, and + the command that produced the evidence. +- **Distinguish drift from a deliberate state.** An archived project with no packages and no stable + release is correct; a `stable` project with a prerelease-only NuGet package is not. +- **Never invent data.** If the repository has no useful description or topics, report it as an + upstream action item instead of writing marketing copy into the manifest. +- **Never weaken a check.** If `just check-projects` fails, fix the data or the check's contract + (with an ADR), never the assertion. + +## Checklist (in order) + +1. **Schema and loader** — `loadProjects()` succeeds (org ownership, id/repository shape, unique + packages, relationships, `order` sorting). +2. **Deterministic guard** — `just check-projects` passes. +3. **Documentation** — each `docs` config points at a path that exists, a root page that resolves, + exclusions that cover `_Sidebar.md`/`index.md` stubs, and a non-zero aggregated page count. +4. **Stability** — `status` matches the repository `archived` flag and the NuGet release channel. +5. **Metadata and tags** — repository description present and useful, topics non-empty (they are the + site's tag chips), homepage/link targets correct, `discussions` matches the repository flag. +6. **Packages** — every declared package id exists on NuGet with published versions, exactly one + `primary`, and `targetFrameworks` are plausible for the repository. +7. **Use cases** — at least one per non-archived project, valid audiences, `code` paired with + `language`, `evidence` that is a fact, `docsPage` slugs that resolve. +8. **Relationships** — `related`/`supersedes`/`supersededBy` are meaningful and symmetric where they + should be. +9. **Ordering and presentation** — unique `order` values, sensible `featured` usage, categories + consistent with what the project actually is. +10. **Collaborations** — `externalProjects` never point at a `purview-dev` repository and always + carry a working `url`. + +## Report format + +Return a markdown table with one row per finding: + +| Project | Field | Observed | Expected | Severity | Evidence | Fix | +| --- | --- | --- | --- | --- | --- | --- | + +- Severity: `blocker` (schema/build failure), `drift` (incorrect data), `polish` (quality), + `upstream` (must be fixed in the product repository, not here). +- Finish with: the number of projects audited, the checks run, and the fixes applied. +- Apply only `blocker` and `drift` fixes; list `polish`/`upstream` items for a human decision. diff --git a/.agents/agents/catalogue-onboarder.agent.md b/.agents/agents/catalogue-onboarder.agent.md new file mode 100644 index 0000000..e0ee261 --- /dev/null +++ b/.agents/agents/catalogue-onboarder.agent.md @@ -0,0 +1,72 @@ +--- +name: Catalogue Onboarder +description: "Specialist for adding a purview-dev (or collaboration) repository to the website catalogue: recon the repository, author the projects.yml record (docs, tags, packages, use cases), integrate it across the site, and prove it with the validation pipeline." +tools: + [ + "search/codebase", + "search", + "edit/editFiles", + "execute/runInTerminal", + "read/terminalLastCommand", + "read/terminalSelection", + "read/getTaskOutput", + "execute/runTask", + ] +--- + +You are a specialist for onboarding repositories into the Purview-Dev website catalogue. + +## Primary objective + +Take a repository (`purview-dev/` or a collaboration repository) and deliver a complete, +validated integration: a correct `src/src/data/projects.yml` record, a working documentation +configuration, concrete use cases, tags/metadata, and a green `just validate`. + +## Background knowledge + +Load and apply these skills before writing anything: + +- `add-catalogue-project` — the ordered onboarding procedure and the record skeleton. +- `project-manifest-reference` — every field, the allowed vocabularies, and the hard rules. +- `docs-aggregation-rules` — how `github-path`, `wiki`, and `readme` sources are aggregated. +- `use-case-authoring` — the ADR 0002 rules for concrete, audience-tagged use cases. +- `github-repo-metadata` — where topics/description/stability signals come from and how to fix them. +- `site-validation-loop` — the checks to run and how to interpret failures. + +The most important rules are: + +- **Recon first.** Never guess a repository's docs layout, package ids, or release channel; read the + repository with `gh` and NuGet before authoring the record. +- **Documentation stays in the product repository.** This site aggregates it; do not copy docs here. +- **Tags are GitHub topics**, not manifest fields. +- **`name` must slugify to `id`** for any project with `docs` (the per-project LLM bundle path). +- **Every non-archived project needs at least one use case with real code and evidence.** +- **`status` must match reality** (`stable` / `preview` / `archived`). +- **Never hand-edit generated content** or weaken a check. + +## Workflow + +1. **Recon** — repository metadata, docs tree, packages, releases (skills list the exact commands). +2. **Classify** — Purview-owned (`projects:`) or collaboration (`externalProjects:`); choose + `category` and `status` from the schema vocabulary (never invent values). +3. **Author** — add the record in the correct `order`, with `docs`, `packages`, `targetFrameworks`, + `install`, `related`/`acknowledgments` where they apply, and at least one use case. +4. **Fix upstream metadata** — set GitHub topics (tags), make the description useful, enable + discussions only when `discussions: true` is declared. +5. **Integrate and prove** — run `just data-sync`, then `just check-projects`, `bun run typecheck`, + `bun run test`, `just check-generated`, and finally `just validate`. Confirm the docs page count, + the sidebar topic, the `/_llms-txt/.txt` bundle, and the catalogue/use-case cards. +6. **Report** — list the record added, the evidence gathered (docs pages, packages, release + channel), the checks run, and anything that still needs a human (for example, no stable release + yet, or docs that should be moved into a `docs/` folder upstream). + +## Constraints + +- Only edit the manifest, tests, and documentation prose. Do not change Astro components, the theme, + or the schema vocabulary as part of onboarding. +- Do not add dependencies. +- Do not bump the root `package.json` version for a content-only change. +- If documentation does not exist upstream, say so and either configure `docs` for what does exist + (for example `readme`) or leave `docs` unset — never fabricate pages. +- If a use case cannot be evidenced from the repository, stop and ask rather than writing marketing + prose. diff --git a/.agents/prompts/add-project.prompt.md b/.agents/prompts/add-project.prompt.md new file mode 100644 index 0000000..d04cd72 --- /dev/null +++ b/.agents/prompts/add-project.prompt.md @@ -0,0 +1,49 @@ +--- +mode: agent +description: Add a repository to the Purview-Dev website catalogue and integrate it end-to-end. +--- + +# Add a project to the catalogue + +Inputs (fill in before running): + +- Repository: `/` (required) +- Owned by purview-dev? `yes` (catalogue project) / `no` (collaboration) +- Category (optional — let the recon decide): `` +- Status (optional — let the release channel decide): `stable | preview | archived` +- Notes: anything known about docs location, package ids, or the story to tell + +## Instructions + +Load and follow these skills, in order: + +1. `.agents/skills/add-catalogue-project/SKILL.md` — the end-to-end procedure. +2. `.agents/skills/project-manifest-reference/SKILL.md` — fields and rules. +3. `.agents/skills/docs-aggregation-rules/SKILL.md` — the `docs` configuration. +4. `.agents/skills/use-case-authoring/SKILL.md` — the use cases. +5. `.agents/skills/github-repo-metadata/SKILL.md` — topics and stability signals. +6. `.agents/skills/site-validation-loop/SKILL.md` — the checks to run. + +Use the `catalogue-onboarder` agent if it is available. + +## Deliverables + +1. Recon evidence: repository metadata, docs tree/landing page, package ids and channels, release + channel. Show the commands and their output. +2. The `projects.yml` record (or `externalProjects` record) added at an unused `order`, with `docs`, + `packages`, `targetFrameworks`, `install` (only when not plain NuGet), relationships and + acknowledgments where they genuinely apply. +3. At least one concrete use case with `code` + `language`, an `evidence` fact, and a `docsPage` that + resolves. +4. Repository topic fixes where tags are missing or poor. +5. Validation output: `just data-sync`, `just check-projects`, `bun run typecheck`, `bun run test`, + `just check-generated`, `just validate`. + +## Constraints + +- Do not fabricate documentation pages, use-case evidence, or release data. +- Do not invent schema values; use only the vocabularies in `project-manifest-reference`. +- Do not hand-edit `src/src/content/docs/**`, `src/.cache/**`, or `src/dist/**`. +- Do not bump the root `package.json` version. +- Finish with a short report: record added, docs page count, tag/topic changes, checks run, and + outstanding `upstream` follow-ups. diff --git a/.agents/prompts/audit-projects.prompt.md b/.agents/prompts/audit-projects.prompt.md new file mode 100644 index 0000000..b90e947 --- /dev/null +++ b/.agents/prompts/audit-projects.prompt.md @@ -0,0 +1,41 @@ +--- +mode: agent +description: Audit every project in projects.yml for correctness (documentation, stability, metadata) and repair the clear drift. +--- + +# Audit the project catalogue + +Scope (fill in before running): + +- Projects: `all` (default) or a comma-separated list of ids / repositories +- Live checks: `yes` (query GitHub + NuGet per project) / `no` (use the local cache only) +- Repair: `report-only` / `fix` (default: fix blockers and drift, report the rest) + +## Instructions + +1. Read `.agents/skills/audit-catalogue-projects/SKILL.md` and follow its method and report format. +2. Load `.agents/skills/project-manifest-reference/SKILL.md`, + `.agents/skills/docs-aggregation-rules/SKILL.md`, + `.agents/skills/use-case-authoring/SKILL.md`, and + `.agents/skills/github-repo-metadata/SKILL.md` as the reference for the checks. +3. Load `.agents/skills/site-validation-loop/SKILL.md` for the commands and their meaning. + +Use the `catalogue-auditor` agent if it is available. + +## Deliverables + +1. The deterministic results first: `just check-projects` and `bun run test` output. +2. A findings table (one row per issue) with Project, Field, Observed, Expected, Severity, Evidence, + Fix — as specified in the audit skill. +3. The applied fixes (blockers and drift only), each with the command that proves it. +4. Re-validation after fixing: `just check-projects`, `bun run test`, and `just validate`. +5. A closing summary: projects audited, issues by severity, fixes applied, and the `upstream` items + that must be resolved in the product repositories. + +## Constraints + +- Report before repairing, and never edit while still gathering evidence. +- Every finding needs a reproducible command and its observed output. +- Do not invent metadata, use cases, or evidence to silence a check. +- Do not change the schema vocabulary, the Astro components, or the validation logic. +- Do not hand-edit generated content or caches. diff --git a/.agents/skills/add-catalogue-project/SKILL.md b/.agents/skills/add-catalogue-project/SKILL.md new file mode 100644 index 0000000..ee878de --- /dev/null +++ b/.agents/skills/add-catalogue-project/SKILL.md @@ -0,0 +1,170 @@ +--- +name: add-catalogue-project +description: Use when adding a repository to the Purview-Dev website catalogue — recon the repo, author the projects.yml record (docs, packages, tags, use cases), integrate it across the catalogue/docs/releases surfaces, and prove it with the validation pipeline. +category: purview-dev-website +roles: + - catalogue + - docs + - integration +tags: + - projects-yml + - astro + - starlight + - documentation +--- + +# add-catalogue-project Skill + +Use this skill when a repository must be added to (or promoted within) the Purview-Dev website. + +Related: `project-manifest-reference`, `docs-aggregation-rules`, `use-case-authoring`, +`github-repo-metadata`, `site-validation-loop`. + +## Step 0 — decide the target collection + +| Situation | Where it goes | +| --- | --- | +| Repository is owned by the `purview-dev` organisation | `projects:` | +| Project the organisation contributes to but does not own (for example `likec4/likec4`) | `externalProjects:` | + +Only `projects:` entries get a `/projects//` page, documentation aggregation, LLM bundles and +release data. `externalProjects:` entries are link-out cards on `/projects/` and must never point at +a `purview-dev` repository. + +## Step 1 — recon the repository (never guess) + +```shell +# Metadata: description, topics, archived, default branch, discussions, homepage +gh api repos// --jq '{description, topics, archived, default_branch, has_discussions, homepage}' + +# Docs layout: does docs/ exist, is it a mirror of the wiki, is there a _Sidebar.md? +gh api "repos///git/trees/main?recursive=1" --jq '.tree[].path' +gh api "repos///contents/README.md" -H 'Accept: application/vnd.github.raw' + +# Project conventions: build type, agent guidance, licensing +gh api "repos///contents/purview-build.json" -H 'Accept: application/vnd.github.raw' + +# Release channel and tag shape +gh api "repos///releases?per_page=10" --jq '.[] | {tag_name, prerelease, published_at}' + +# Packages: confirm each id exists and see its channel (replace the id) +curl.exe -s "https://api.nuget.org/v3-flatcontainer//index.json" +``` + +Record, as evidence for the record you are about to write: where the docs live, the exact markdown +file that should be the landing page, the NuGet package ids, the release channel, and the repository +description/topics. + +## Step 2 — classify + +- `category`: exactly one of `application-framework`, `validation`, `observability`, + `source-generation`, `aspire`, `build-tooling`, `developer-tooling`. +- `status`: + - `archived` when the repository is archived (GitHub `archived: true`). + - `preview` when there is no stable NuGet release, or the tooling is explicitly experimental. + - `stable` only when a stable (non-prerelease) release exists and is recommended for use. +- `id`: lowercase `[a-z0-9-]` slug, normally the repository name. +- `name`: display name. **It must slugify to `id` when the project has `docs`** (the LLM bundle is + emitted at `/_llms-txt/.txt` while the UI links `/_llms-txt/.txt`). +- `order`: pick an unused value (10–80 in tens is the existing convention). + +## Step 3 — author the record + +Add the entry to `src/src/data/projects.yml` (see `project-manifest-reference` for every field): + +```yaml + - id: + name: + shortDescription: + description: >- + + origin: >- + + useWhen: >- + + avoidWhen: >- + + repository: purview-dev/ + category: + status: + order: + docs: + source: github-path | wiki | readme + path: docs # github-path only + rootPage: Getting-Started.md # optional; see docs-aggregation-rules + exclude: [ _Sidebar.md, index.md ] + targetFrameworks: [ net8.0, net9.0 ] + install: nuget | msbuild-sdk | dotnet-tool # only when not a plain NuGet package + packages: + - id: + description: + primary: true + related: [ ] + acknowledgments: + - name: + url: https://github.com// + description: + useCases: + - audience: developer + title: + scenario: >- + + code: | + + language: csharp + outcome: >- + + evidence: >- + + docsPage: + discussions: false +``` + +Every non-archived project needs at least **one** use case with `code` + `language` and an +`evidence` fact (ADR 0002). See `use-case-authoring`. + +## Step 4 — fix the upstream metadata (this is where tags come from) + +- Set GitHub **topics** on the repository (they become the site's tag chips). See + `github-repo-metadata` for the exact command and the taxonomy guidance. +- The repository description must be genuinely descriptive: it backs the `shortDescription` + fallback and social/`og:` metadata. +- Only set `discussions: true` once GitHub Discussions is actually enabled on the repository. + +## Step 5 — integrate and verify + +```shell +just data-sync # aggregate docs + refresh release data (cache-first) +just check-projects # deterministic catalogue guard (part of `just validate`) +bun run typecheck && bun run test +just check-generated +just validate # full chain: format, lint, build, link crawl, dist tests +``` + +Confirm each surface actually lights up: + +1. `/projects/` shows the card with the right category/status/tags (tags come from topics). +2. `/projects//` renders description, use cases, install panel, packages, and related links. +3. `/docs//` exists with the expected page count and a sidebar topic named after the project. +4. `/_llms-txt/.txt` is emitted and linked from `/llms.txt`. +5. `/use-cases/` lists the new use cases under the right audiences. +6. `/releases/` shows GitHub/NuGet versions for the declared packages. + +## Step 6 — report + +State: the record added, the recon evidence, the package ids and channels, the docs source and page +count, the checks run with their result, and any `upstream` follow-ups (for example: topics missing, +docs not yet in a `docs/` folder, no stable release yet). + +## Pitfalls + +- `docs.path` must be a **repository path** (`docs`, `docs/wiki`), never a URL. +- A repository whose wiki is mirrored into `docs/wiki` usually needs + `exclude: [_Sidebar.md, Home.md, index.md]` plus `rootPage: Getting-Started.md`, otherwise the + aggregation raises `DocsValidationError`. See `docs-aggregation-rules`. +- Aggregation reads branch `main`. If the repository's default branch is `master`/`develop`, docs + aggregation silently yields nothing — rename or report it. +- The `README.md`-as-index mode (`readme:`) publishes exactly one page and cannot be combined with + `rootPage`. +- Adding a project does not require editing tests: the manifest tests assert `>=` counts, not exact + membership. But keep their expectations true (for example the `>= 8` use-case coverage). diff --git a/.agents/skills/audit-catalogue-projects/SKILL.md b/.agents/skills/audit-catalogue-projects/SKILL.md new file mode 100644 index 0000000..b122c71 --- /dev/null +++ b/.agents/skills/audit-catalogue-projects/SKILL.md @@ -0,0 +1,136 @@ +--- +name: audit-catalogue-projects +description: Use when auditing or repairing src/src/data/projects.yml — the invariant checklist for documentation, stability, metadata, tags, packages, relationships, use cases and ordering, plus the findings report format. +category: purview-dev-website +roles: + - catalogue + - audit +tags: + - projects-yml + - validation + - metadata +--- + +# audit-catalogue-projects Skill + +Use this skill to answer "is every project in the catalogue correct?" with evidence, and to repair +the unambiguous problems. + +Related: `project-manifest-reference` (fields and vocabularies), `docs-aggregation-rules`, +`use-case-authoring`, `github-repo-metadata`, `site-validation-loop`. + +## Method + +1. **Deterministic first.** Run `just check-projects` and `bun run test`. These already enforce the + structural invariants; start from their output rather than re-deriving it. +2. **Then the judgement checks** that no script can make: is the description truthful, is the + category right, is the stability honest, is the use-case evidence real. +3. **Gather live evidence per project** with `gh` and NuGet (commands in `github-repo-metadata`). +4. **Report before repairing.** Produce the findings table, then apply `blocker`/`drift` fixes and + re-run the guards. + +## Invariant checklist + +### Structure (enforced by `loadProjects()` / `just check-projects`) + +- `id` is a lowercase `[a-z0-9-]` slug; `repository` is `purview-dev/` for `projects:`. +- `related`, `supersedes`, `supersededBy` reference known ids; a superseded project names its + successor and vice versa where the pairing exists. +- Every NuGet package id appears exactly once across the catalogue. +- `order` values are unique integers and the sorted order is the intended presentation order. +- For every project with `docs`, `name` slugifies to exactly `id`. +- Every non-archived project has ≥1 use case; every use case with `code` has `language`. +- Every `docsPage` resolves to a page in `src/.cache/docs/index.json` for that project. +- `externalProjects` never point at a `purview-dev` repository and always have a working `url`. + +### Documentation + +- `docs.source` matches reality: `github-path` when the repository has a docs folder, + `wiki` when the content lives in the wiki, `readme` only for single-page projects. +- `docs.path` exists **on branch `main`** and contains `.md` files. +- A `rootPage` either names the conventional index or is paired with an `exclude` that removes it. +- `exclude` covers `_Sidebar.md` (and `Home.md`/`index.md` stubs) so they do not appear as pages. +- The aggregated page count is non-zero and the landing page is the page a reader should land on. +- Archived projects that still declare `docs` are intentional (they are excluded from the portal). +- Documentation is not duplicated here: this repository only holds the mirror. + +### Stability + +- `archived` repository ⇒ `status: archived`. +- No stable NuGet release ⇒ not `stable` (`preview`). +- `stable` ⇒ a stable, listed NuGet release exists and the repository is not archived. +- Deprecated NuGet packages are reported, not silently accepted. +- A project with `packages: []` is intentional: build tooling consumed as a tool/SDK still declares + its package id when one exists. + +### Metadata and tags + +- Repository description is present, factual, and consistent with `shortDescription`/`description`. +- **Topics are non-empty** — they are the only source of site tags — and follow the lowercase, + hyphenated taxonomy. +- `discussions: true` only when the repository has discussions enabled. +- `homepage`/`url` targets resolve. +- `featured` is used deliberately (a handful of headline projects, not most of the catalogue). + +### Packages, frameworks and install + +- Each declared package id exists on NuGet and has published versions (the guard checks the release + cache). +- Exactly one `primary` when a project has multiple packages. +- `targetFrameworks` match the package's actual TFMs (`netstandard2.0` for generators/analyzers is + normal). +- `install` is set only when the package is not a plain NuGet reference: `msbuild-sdk` for + `Purview.BuildSdk`, `dotnet-tool` for `Purview.Build`. + +### Use cases + +- Coverage across audiences is meaningful for the project's shape (adopter, approver, extender). +- `scenario` describes a situation; `outcome` describes the result; `evidence` is a fact. +- Snippets are short, compile-plausible, and use the right `language`. +- `docsPage` points at the page that actually carries the depth. + +## Evidence commands + +```shell +# Deterministic guards +just check-projects +bun run test + +# Per-project live evidence +gh api repos/purview-dev/ --jq '{description, topics, archived, default_branch, has_discussions, homepage}' +gh api "repos/purview-dev//git/trees/main?recursive=1" --jq '[.tree[].path | select(endswith(".md"))]' +gh api "repos/purview-dev//releases?per_page=10" --jq '.[] | {tag_name, prerelease}' +curl.exe -s "https://api.nuget.org/v3-flatcontainer//index.json" + +# Aggregated result +node -e "const m=require('./src/.cache/docs/index.json');for(const p of m.projects)console.log(p.projectId,p.pages.length)" +``` + +If `.cache/docs/index.json` or the release cache is stale, run `just data-sync` (live) first so the +evidence and the guards agree. + +## Report format + +```markdown +| Project | Field | Observed | Expected | Severity | Evidence | Fix | +| --- | --- | --- | --- | --- | --- | --- | +| example-tool | status | stable | preview | drift | no stable NuGet version (flat container: only 1.2.0-prerelease.3) | set status: preview | +| example-tool | — | topics: [] | 6–15 topics | drift | `gh api repos/...` shows an empty topics array | set repository topics | +``` + +- Severity: `blocker` (schema/build failure or a broken page), `drift` (data contradicts reality), + `polish` (quality), `upstream` (must be fixed in the product repository: missing docs, missing + topics you cannot set, no stable release). +- Close the report with: number of projects audited, checks run, fixes applied, and the items left + for a human. +- Apply only `blocker` and `drift` fixes; never restructure a record or change a category to make a + check pass. + +## Guardrails + +- Do not add use cases or evidence you cannot trace to the repository or the aggregated docs; report + the gap instead. +- Do not change the schema vocabulary; adding a `status`/`category` value needs an ADR. +- Do not hand-edit the docs mirror or caches to make the audit pass. +- If an audit fix changes `projects.yml`, re-run `just check-projects`, `bun run test`, and + `just validate` before reporting completion. diff --git a/.agents/skills/docs-aggregation-rules/SKILL.md b/.agents/skills/docs-aggregation-rules/SKILL.md new file mode 100644 index 0000000..3dd0b5f --- /dev/null +++ b/.agents/skills/docs-aggregation-rules/SKILL.md @@ -0,0 +1,94 @@ +--- +name: docs-aggregation-rules +description: How the website aggregates documentation from product repositories into /docs// — sources, root-page and exclusion rules, ordering, injected front matter, tags from repository topics, staleness, and the failure modes with their remediations. +category: purview-dev-website +roles: + - docs + - reference +tags: + - starlight + - documentation + - aggregation +--- + +# docs-aggregation-rules Skill + +`just data-sync` runs `src/scripts/sync-docs.ts` → `aggregateDocs()` in +`src/src/lib/docs/aggregate.ts`. It reads each catalogue project's `docs` configuration, fetches +the markdown from the product repository, writes the mirror to `src/src/content/docs/docs//` +and records the page list in `src/.cache/docs/index.json`. + +**Never edit the mirror by hand** — it is gitignored and regenerated. + +## Where content comes from + +| `docs.source` | Fetch mechanism | Notes | +| --- | --- | --- | +| `github-path` | Repository tree (`git/trees/main?recursive=1`) filtered to `/**/*.md` | Each file fetched from `raw.githubusercontent.com` on branch `main` | +| `wiki` | `git clone --depth 1 https://github.com/.wiki.git` | `_Sidebar.md` and `_Footer.md` are never rendered | +| `readme` | Raw `README.md` | Exactly one page (`index`) | + +Branch is always **`main`**. `lastReviewed` is the last commit date for the file (Atom feed for +`github-path`/`readme`, wiki HEAD commit for `wiki`). + +## Root page selection (the most common onboarding failure) + +1. A conventional index file wins by default: `index.md`, `Home.md`, or `readme.md` (case-insensitive + basename). +2. `docs.rootPage` may override it **only** when that conventional file is removed with + `docs.exclude`. Otherwise aggregation raises `DocsValidationError`: + *"selects a non-index page while a conventional root page already exists"*. +3. If no index file and no `rootPage` exist, aggregation raises + *"docs: no root page exists"* — add `index.md`/`Home.md`/`readme.md` or set `rootPage`. +4. If `rootPage` names a file that does not exist, aggregation raises + *"docs.rootPage: ... does not exist"*. + +The wikis of several product repositories are mirrored into `docs/wiki`, so the working pattern is: + +```yaml + docs: + source: github-path + path: docs/wiki + rootPage: Getting-Started.md + exclude: [ _Sidebar.md, Home.md, index.md ] +``` + +## Exclusions and ordering + +- `exclude` matches the **base file name** (`index.md`, `_Sidebar.md`) with `*` wildcards; it is + applied **before** the root page is selected (that is what makes rule 2 work). +- Page order comes from, in priority: `docs.order` (explicit slug list) → a `_Sidebar.md` link order + (both for `wiki` and for a mirrored wiki under `github-path`) → `1000`. +- The landing page is always `order: -1`, so it heads the sidebar as "Overview". +- Page slugs are slugified file names (`Getting-Started.md` → `getting-started`) and the URLs are + `/docs///`. A `docsPage` in a use case must use that slug form. + +## Injected front matter (per page) + +`title` (first `# H1`, else the slug), `description` (first prose paragraph, ≤160 chars), +`owners: [purview-dev]`, `status` (the project's status), `lastReviewed`, `sourceProject`, +`sourceRepo`, `projectName`, `sourcePath`, `editUrl`, `tags` (the repository's GitHub **topics**, +absent when there are none), and `sidebar.label`/`sidebar.order`. + +Additional transformations: GitHub alert blockquotes become Starlight asides; relative links between +documents are rewritten to portal URLs; images are rewritten to GitHub raw URLs; duplicate top-level +H1s are demoted to keep one title per document. + +## What the site does with it + +- Each documented project becomes a `starlight-sidebar-topics` topic whose items come from + `src/.cache/docs/index.json` (so the mirror and the manifest must come from the same sync). +- Archived projects are excluded from the docs portal, the sidebar topics, and per-project LLM + bundles, even if they declare `docs`. +- Every documented project gets `/_llms-txt/.txt` (built from `rawContent`), linked from + `/llms.txt` and from the project page. +- Staleness: a page is flagged when `lastReviewed` is older than 365 days + (`src/src/lib/docs/staleness.ts`). + +## Verifying + +```shell +just data-sync # expect "docs : pages" per project +node -e "console.log(require('./src/.cache/docs/index.json').projects)" +just build && just check-generated # asserts the mirror matches the manifest +``` diff --git a/.agents/skills/github-repo-metadata/SKILL.md b/.agents/skills/github-repo-metadata/SKILL.md new file mode 100644 index 0000000..259991b --- /dev/null +++ b/.agents/skills/github-repo-metadata/SKILL.md @@ -0,0 +1,85 @@ +--- +name: github-repo-metadata +description: How GitHub repository metadata feeds the website (topics become tags, description backs shortDescription, archived/description/discussions drive status and links) and how to inspect and correct it with gh. +category: purview-dev-website +roles: + - catalogue + - metadata +tags: + - github + - topics + - tags + - nuget +--- + +# github-repo-metadata Skill + +The manifest is curated, but several site surfaces read **live repository metadata** that is fetched +at build time into `src/.cache/releases/releases.json` (`fetchGitHubRepo`, `fetchNuGetIndex`, +`fetchNuGetSearch` in `src/src/lib/releases/github.ts`). + +## What comes from where + +| Site surface | Source | Notes | +| --- | --- | --- | +| Tag chips (cards, docs portal, docs-page front matter `tags`) | GitHub **topics** | `projectRepoEnrichment()` in `src/src/lib/releases/repo.ts` | +| `shortDescription` fallback | GitHub **description** | Used only when the manifest value is empty | +| "supplemental" description on the project page | GitHub description | Shown when it differs from the manifest description | +| Successor/archived messaging | GitHub **archived** flag + `status`/`supersededBy` | | +| Version numbers, release dates, badges | GitHub releases + NuGet flat-container index | | +| Downloads / deprecated / listed | NuGet search entry | Rendered on the release tables | +| Discussions link | manifest `discussions` **and** repository discussion settings | The loader only builds the URL when `discussions: true` | + +`search` corpora on `/projects/` deliberately include the topics, so missing topics make a project +undiscoverable by its domain keywords. + +## Inspecting + +```shell +gh api repos/purview-dev/ --jq '{description, topics, archived, default_branch, has_discussions, homepage, visibility}' +gh api "repos/purview-dev//releases?per_page=10" --jq '.[] | {tag_name, prerelease, draft}' +curl.exe -s "https://api.nuget.org/v3-flatcontainer//index.json" +curl.exe -s "https://azuresearch-usnc.nuget.org/query?q=packageid:&take=1" +``` + +## Correcting + +Topics are replaced wholesale (PUT), so send the complete set: + +```shell +echo '{"names":["csharp","dotnet","roslyn","source-generators","telemetry"]}' > topics.json +gh api repos/purview-dev//topics --method PUT --input topics.json +rm topics.json +``` + +`gh` also accepts repeated array fields: `gh api repos///topics --method PUT -f 'names[]=csharp' -f 'names[]=dotnet'`. + +Other metadata (`description`, `homepage`, `has_discussions`) is set in the repository settings or +with `gh api repos// --method PATCH -f description='...' -f homepage='...'`. Enabling +Discussions on an organisation repository requires organisation permission; report it as an +`upstream` item when you cannot do it. + +## Tag taxonomy guidance + +- Lowercase, hyphenated, and specific: `event-sourcing`, `source-generators`, `zero-allocation`, + `msbuild-sdk`, `dotnet-aspire`, `csharp`, `roslyn`. +- 6–15 topics is the useful range: enough to describe the domain, few enough that the card's first + four chips are meaningful (cards render `tags.slice(0, 4)`). +- Prefer ecosystem-standard names (`dotnet`, `csharp`, `nuget`, `opentelemetry`) so cross-repo + searches behave predictably. +- Topics are the **only** way tags reach the site. There is no `tags` field in `projects.yml` — do + not add one. + +## Stability signals to check against `status` + +| Signal | Implication | +| --- | --- | +| `archived: true` | `status` must be `archived` (and `supersededBy` when a successor exists) | +| No stable release, only `-prerelease.N` tags | `preview` at most | +| Latest NuGet version is stable and listed | `stable` is allowed | +| NuGet `deprecated: true` | Report as `drift`/`upstream`; a deprecated package should not front a `stable` project without explanation | +| `default_branch` is not `main` | Docs aggregation produces nothing — a blocker for any project with `docs` | + +Note that the organisation's Changesets automation publishes `vX.Y.Z-prerelease.N` tags **without** +setting GitHub's `prerelease` flag, so version selection is semver-based; do not judge the channel +from the GitHub `prerelease` boolean alone. diff --git a/.agents/skills/project-manifest-reference/SKILL.md b/.agents/skills/project-manifest-reference/SKILL.md new file mode 100644 index 0000000..0bfb0c1 --- /dev/null +++ b/.agents/skills/project-manifest-reference/SKILL.md @@ -0,0 +1,93 @@ +--- +name: project-manifest-reference +description: Field-by-field reference for src/src/data/projects.yml (schema vocabulary, defaults, loader-enforced rules, and the invariants the site build depends on). Use whenever authoring or reviewing a catalogue record. +category: purview-dev-website +roles: + - catalogue + - reference +tags: + - projects-yml + - zod + - schema +--- + +# project-manifest-reference Skill + +The catalogue is `src/src/data/projects.yml`, typed by `src/src/lib/manifest/schema.ts` and validated +by `src/src/lib/manifest/load.ts`. `$schema: ../lib/manifest/schema.ts` is the first line of the file. + +## Vocabularies (do not invent values) + +| Field | Allowed values | +| --- | --- | +| `category` | `application-framework`, `validation`, `observability`, `source-generation`, `aspire`, `build-tooling`, `developer-tooling` | +| `status` | `stable`, `preview`, `archived` | +| `install` | `nuget` (default), `msbuild-sdk`, `dotnet-tool` | +| `useCases[].audience` | `developer`, `team-lead`, `architect`, `contributor` (labels: Developer, Team lead, Architect, Contributor) | + +Adding a value to any of these lists is a schema change and requires an ADR. + +## `projects[]` fields + +| Field | Required | Notes | +| --- | --- | --- | +| `id` | yes | Lowercase `[a-z0-9-]` slug; also the URL segment and the LLM bundle name | +| `name` | yes | Display name; **must slugify to `id` when `docs` is set**; must not start with `Purview ` | +| `shortDescription` | yes | One sentence; falls back to the GitHub description if empty, then to `name` | +| `description` | yes | Long description (2–4 sentences) | +| `origin` | yes | Why the project became reusable tooling | +| `useWhen` | yes | When it is the right choice | +| `avoidWhen` | yes | When it is the wrong choice | +| `repository` | yes | `owner/repository`; **`owner` must be `purview-dev`** for `projects[]` | +| `category` | yes | See vocabulary | +| `status` | yes | See vocabulary | +| `featured` | no | Default `false`; shown prominently on the home page | +| `order` | no | Default `1000`; projects are sorted ascending; keep values unique | +| `docs` | no | See below | +| `install` | no | Default `nuget` | +| `targetFrameworks` | no | e.g. `net8.0`, `net9.0`, `net10.0`, `netstandard2.0` | +| `packages` | no | `{ id, description?, primary?, targetFrameworks? }[]`; defaults to `[]` | +| `related` | no | Known project ids; rendered in the project page aside | +| `useCases` | no | Default `[]`; at least one required for non-archived projects | +| `acknowledgments` | no | `{ name, url, description? }[]`; upstream work the project credits | +| `supersedes` / `supersededBy` | no | Known project ids; used for archived/successor messaging | +| `discussions` | no | Default `false`; when true the page links to repository Discussions (the repository must have them enabled) | + +## `docs` (a discriminated union on `source`) + +| `source` | Fields | Behaviour | +| --- | --- | --- | +| `github-path` | `path` (required), plus `rootPage`, `readmeAsIndex`, `order`, `exclude` | Walks `/**/*.md` on branch `main` | +| `wiki` | `rootPage`, `readmeAsIndex`, `order`, `exclude` | Shallow-clones `.wiki.git`; `_Sidebar.md` orders pages, `_Footer.md` is skipped | +| `readme` | none | Publishes `README.md` alone as `/docs//` | + +- `rootPage` names the markdown file that becomes the landing page (`index`). It may only select a + non-index page when the conventional root page is removed via `exclude` (the aggregation otherwise + raises `DocsValidationError`). +- `exclude` matches the **base file name** (not the repository path) and supports `*` wildcards. +- `readmeAsIndex` uses the repository `README.md` as the landing page and cannot be combined with + `rootPage`; it aliases `readme` to `index`. +- `order` is an explicit list of page slugs, applied before the `_Sidebar.md` order. +- If `docs` is set but the aggregation yields no pages, `just data-sync` fails loudly. + +## Loader-enforced rules (`loadProjects()`) + +1. Schema validation with remediation text (unknown keys are rejected). +2. `projects[].repository.split('/')[0]` must equal `purview-dev`. +3. `related`, `supersedes`, and `supersededBy` must all reference existing project ids. +4. A NuGet package id may be declared by **exactly one** project. +5. The returned list is sorted by `order` ascending. + +## Defaults applied by the loader + +`featured: false`, `order: 1000`, `packages: []`, `related: []`, `useCases: []`, +`acknowledgments: []`, `discussions: false`, `install: 'nuget'`. Derived values (`repoOwner`, +`repoName`, `sourceUrl`, `issuesUrl`, `discussionsUrl`, `changelogUrl`, `releasesUrl`) are computed, +never authored. + +## `externalProjects[]` fields + +Required: `id` (slug), `name`, `shortDescription`, `description`, `url` (valid URL), `category`. +Optional: `repository` (`owner/repo`, link target), `status`, `featured`, `order`. +Loader-enforced: a collaboration entry must **not** use the `purview-dev` owner (the manifest test +asserts that no `externalProjects` id starts with `purview-`). diff --git a/.agents/skills/site-validation-loop/SKILL.md b/.agents/skills/site-validation-loop/SKILL.md new file mode 100644 index 0000000..822b852 --- /dev/null +++ b/.agents/skills/site-validation-loop/SKILL.md @@ -0,0 +1,90 @@ +--- +name: site-validation-loop +description: Use when validating a change to the Purview-Dev website — what each just recipe and script checks, the data-sync modes, how to reproduce failures offline, and the rules for generated artefacts. +category: purview-dev-website +roles: + - validation + - tooling +tags: + - just + - bun + - ci + - astro +--- + +# site-validation-loop Skill + +`just validate` is the authoritative chain and the same pipeline runs in CI +(`purview-build.json` → `bun run ci:build`; deploy.yml runs `just validate` first). Treat a green +`just validate` as the definition of done. + +## The chain, in order + +| Step | Command | Catches | +| --- | --- | --- | +| Format check | `just format-check` (`oxfmt --check`) | Formatting drift in `src/**` (not `.astro`) | +| Lint | `just lint` (`oxlint`, `maxWarnings: 0`) | Type-aware + a11y issues in `src/**` | +| Typecheck | `just typecheck` (`astro check` + strict TS) | Component/frontmatter/content-schema type errors | +| Unit tests | `just test` (`bun test tests/unit`) | Manifest/schema rules, catalogue guard, docs aggregation rules, release transform, urls, staleness, branding | +| Assets | `just check-assets` | Branding sources vs generated public assets | +| Catalogue | `just check-projects` | Catalogue invariants (see below) | +| Build | `just build` (runs live data sync first) | Astro/Starlight build, sidebar/llms plugin wiring | +| Links | `just check-links` | Broken internal links **and missing anchors** across `dist/**/*.html` | +| Generated output | `just check-generated` | Docs cache/mirror agreement, release cache shape, `llms*.txt`, sitemap, robots, no secrets/local paths | +| Dist tests | `just test:dist` | Per-project LLM bundles, rendered project/use-case surfaces, footer version, SEO outputs | + +## The catalogue guard (`just check-projects`) + +`src/scripts/check-projects.ts` validates `projects.yml` against the generated data (offline, +deterministic). It fails the build on: + +- ids/orders duplicated, `order` not a unique integer, `name` not slugifying to `id` for documented + projects; +- a documented project with no aggregated pages, or a `docsPage` that does not resolve; +- a non-archived project with no use case, or a use case with `code` but no `language`; +- a package declared more than once, more than one `primary`, or a package with no release data; +- `status: archived` on a non-archived repository, or `stable` on a project whose packages have no + stable version; +- missing repository topics (tags), a non-`main` default branch for a documented project, or + `discussions: true` without repository discussions; +- `externalProjects` pointing at a `purview-dev` repository. + +Run it alone while iterating: `just check-projects`. + +## Data-sync modes + +`DATA_MODE` (`src/.env.example`) controls how docs/release data is obtained: + +- `auto` (default) — use the cache if present, else fetch live, else fall back to committed fixtures. +- `live` — always fetch from GitHub/NuGet (`just live-data-sync`). +- `cache` — only use the existing cache (fails if missing). +- `fixture` — only use `src/fixtures/**` (fully offline, deterministic). + +`DATA_FALLBACK_TO_FIXTURES=false` makes `auto` fail loudly instead of silently using fixtures. Set +`GITHUB_TOKEN` (or `gh auth token`) to avoid unauthenticated rate limits. + +## Rules + +- **Never edit generated files** (`src/src/content/docs/**`, `src/.cache/**`, `src/dist/**`). + Regenerate with `just data-sync`, `just fetch-releases`, `just build`, or `just refresh-fixtures`. +- **Never weaken a check** to make a change pass. Fix the data or the implementation; changing the + contract needs an ADR. +- Keep `src/fixtures/**` in sync when the release data shape changes (`just refresh-fixtures`, then + review the diff). +- Do not run `just clean` to "fix" a failure: a fresh checkout then needs a live data sync. + +## Reproducing failures + +```shell +# Docs aggregation / sidebar problems +just live-data-sync && just check-generated + +# Link or anchor failures: rebuild then re-crawl +just build && just check-links + +# Catalogue drift in isolation +just check-projects + +# Release-data problems without network +DATA_MODE=fixture just build && just test-dist +``` diff --git a/.agents/skills/use-case-authoring/SKILL.md b/.agents/skills/use-case-authoring/SKILL.md new file mode 100644 index 0000000..d634c97 --- /dev/null +++ b/.agents/skills/use-case-authoring/SKILL.md @@ -0,0 +1,84 @@ +--- +name: use-case-authoring +description: Use when writing or reviewing useCases in projects.yml — the ADR 0002 rules for audience-tagged, concrete, evidence-backed examples with short code snippets and resolvable documentation deep links. +category: purview-dev-website +roles: + - catalogue + - content +tags: + - use-cases + - adr-0002 + - documentation +--- + +# use-case-authoring Skill + +Use cases are the "what does it actually do for me" evidence on the home page, on each project page, +and on `/use-cases/`. They are authored in `src/src/data/projects.yml` and validated by the manifest +schema. The governing decision is `docs/decisions/0002-concrete-use-cases.md`. + +## Shape + +```yaml + useCases: + - audience: developer # developer | team-lead | architect | contributor + title: Tracing, logging, and metrics from a single interface + scenario: >- # the situation the reader is in + You want OpenTelemetry activities, structured logs, and metrics around an order service, + but you do not want to hand-write the ActivitySource, ILogger, and Meter wiring. + code: | # short snippet; `language` becomes required + [ActivitySource] + [Logger] + [Meter] + public interface IOrderServiceTelemetry + { + [Activity] + [Info] + [AutoCounter] + Activity? PlacingOrder(int orderId, [Baggage] string region); + } + language: csharp + outcome: >- # what the reader gets back + The generator emits the implementation and an AddOrderServiceTelemetry() DI extension. + evidence: >- # a fact, not a claim + One interface replaces the ActivitySource, ILogger, and Meter boilerplate repeated per service. + docsPage: getting-started # slug of an aggregated page under /docs// +``` + +## Rules + +1. **Audience is from a fixed vocabulary** — `developer`, `team-lead`, `architect`, `contributor`. + Pick the person who would actually read this example; do not tag everything `developer`. +2. **At least one use case per non-archived project**, and no project should have a single + `developer` example when the project also has an adoption/architecture story worth telling + (an audit `polish` finding). Do not pad: two or three strong examples beat six weak ones. +3. **`code` requires `language`.** The snippet is highlighted at build time by Shiki; use a real + language id (`csharp`, `json`, `xml`, `yaml`, `bash`, `typescript`). +4. **Snippets are short and point at the docs for depth.** Long walkthroughs belong in the + aggregated documentation. The card links to `docsPage`. +5. **`docsPage` must be a lowercase `[a-z0-9-]` slug** and must resolve to a real aggregated page + (`/docs///`). The schema rejects other shapes, the unit tests reject a + `docsPage` on a project without a `docs` configuration, and the post-build link crawl + (`just check-links`) fails on a dead link. Slug the source file name: `SQL Server Guide.md` → + `sql-server-guide`. +6. **`evidence` must be a fact.** Good: a before/after, a count of generated members, a measured + allocation property, a generated-output excerpt. Bad: "very fast", "easy to use", "saves time". +7. **`scenario` is a situation, `outcome` is the result.** The pair should let a reader decide in ten + seconds whether the example matches their problem. +8. **Keep the language factual** — no marketing superlatives, no invented benchmarks, no claims that + cannot be traced to the repository or the aggregated docs. + +## Where they render + +- Home page ("What it looks like in practice") — server-rendered, no island. +- Project page (`/projects//`) — the use-case section with highlighted code. +- `/use-cases/` — the full index, filterable by audience via one Preact island; the page is usable + without JavaScript and each card carries `data-usecase-audience` and a search corpus. + +## Verification + +```shell +bun run test # manifest/use-case unit tests (audience, language, docsPage rules) +just check-projects # deterministic coverage and docsPage-resolution checks +just build && just check-links # every rendered docs deep link resolves in dist/ +``` diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..decce35 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,44 @@ +# Copilot instructions — Purview-Dev website + +Read `AGENTS.md` first; it is the authoritative repository context. This file is the condensed +always-on subset. + +## What this repository is + +The Astro + Starlight website for the Purview-Dev organisation: project catalogue, release browser, +and unified documentation portal aggregated from the product repositories. Bun workspace; the only +package is `src/`. + +## Always + +- Run commands from the repository root (`bun run