documentation/dev/docs-overview.md
This is the map of SkiaSharp's documentation system: what gets generated, by which engine, driven by which skill, on which workflow, and in which repository. Start here for the big picture, then jump to a deep dive.
| If you want to… | Go to |
|---|---|
| Understand the whole system at a glance | This page (the map) — read on |
| Generate or edit docs by hand — prerequisites, local commands, editing API docs, the Cake target reference | writing-docs.md |
Change how release notes or API diffs behave — the versioning model, versions.json, file layout, per-engine rules | release-notes-and-api-diffs.md (the behavior spec; read before changing either generator) |
| Work on the docs website — build, preview, theming | site.md |
scripts/infra/docs/)SkiaSharp ships four distinct documentation artifacts. They are produced by different tools and live in different places, which is the main reason the system can feel sprawling — this table is the whole story on one screen:
| # | Artifact | What it is | Source of truth | Engine |
|---|---|---|---|---|
| 1 | Conceptual docs | Hand-written guides & tutorials on the docs site | documentation/docfx/guides/ (this repo) | docfx (site.md) |
| 2 | API reference docs | Per-type/per-member XML reference (→ learn.microsoft.com) | docs/SkiaSharpAPI/ (the docs submodule) | docs.cake (mdoc) |
| 3 | Release notes | Human "what's new" pages, AI-polished | documentation/docfx/releases/ (this repo) | release-notes skill (prepare.sh → render.sh) |
| 4 | API diffs | Machine-generated public-API diffs, no AI | documentation/docfx/releases/ (this repo) | api-diff.cake |
Artifacts 3 and 4 share one versioning model and one config file
(versions.json); that shared model is the subject of the
behavior spec. Artifact 2 is a separate
concern (mdoc), but it shares the same NuGet-diff plumbing
(api-diff-tools.cake) and lives next to the others.
flowchart LR
subgraph A["mono/SkiaSharp (this repo)"]
conceptual["documentation/docfx/guides/ — concept docs"]
releases["documentation/docfx/releases/ — release notes + API diffs"]
engines["scripts/infra/docs/ — the engines"]
submodule["docs/ (git submodule)"]
end
subgraph B["mono/SkiaSharp-API-docs (the docs repo)"]
xml["SkiaSharpAPI/ — mdoc XML reference docs"]
end
submodule -. "points at" .-> B
engines --> releases
engines -. "mdoc writes into" .-> xml
mono/SkiaSharp (this repo) holds the conceptual docs, the release
notes/api diffs tree, and all the generation engines
(scripts/infra/docs/).mono/SkiaSharp-API-docs (the docs repo) holds the large mdoc XML
reference docs and their images/examples. It is pulled into this repo as a git
submodule at docs/, so the XML lives at docs/SkiaSharpAPI/. The engines
in this repo run mdoc into that submodule.Because the engines live here but the XML output lives there, the API-reference path is inherently cross-repo — see the automation section below.
scripts/infra/docs/)All generation code lives together under scripts/infra/docs/ — the API-diff +
mdoc engines (Cake) and the release-notes engine (Python) side by side. The
release-notes skill keeps only the thin, stable entrypoints (prepare.sh,
render.sh) that redirect to that engine, plus the AI's instructions. Nothing can
drift because each path has a single canonical entry point.
| File | Role |
|---|---|
docs.cake | mdoc-based XML reference-doc generator (artifact 2) |
api-diff.cake | API-diff engine used by the top-level docs-api-diff Cake target (artifact 4) |
api-diff-tools.cake | shared NuGet-diff comparer + layout helpers, #loaded by both api-diff.cake and docs.cake |
versions.json | the only override surface — supersession + comparison baselines, honored identically by the Cake and Python engines |
generate-api-docs.sh | Path 3 runner → cake update-docs (mdoc under mono) |
release-notes-data.py | Path 2 release-notes engine (artifact 3): per-page _sources/<v>.data.json facts + Files-to-polish list |
release-notes-index.py | Path 2 index engine: _sources/index.json (Chrome schedule + live-head set) |
release-notes-render.py | Path 2 renderer: all Markdown (pages + TOC.yml + index.md) |
release-notes-schema/prose.schema.json | the prose contract the Polish agent fills |
docker/ | the local reproducibility image + run.sh wrapper; its api-diffs subcommand invokes docs-api-diff directly |
(in the release-notes skill) prepare.sh | Prepare entrypoint → docs-api-diff → release-notes-data.py → release-notes-index.py; accepts --force / --min-version / --max-version |
(in the skill) render.sh | Polish-Finalize entrypoint: offline release-notes-render.py --all (same three flags for a uniform interface) |
(committed under releases/_sources/) pr-authors.json | PR-author cache for the release-notes engine (offline author resolution) |
Each path's entry point is its single source of truth: a developer, the CI
workflows, and the Docker wrapper all invoke the same target or script, so a
command can never drift between local and CI. Path 1 is the docs-api-diff Cake
target, run directly by prepare.sh step 1 and by the Docker api-diffs
subcommand; Path 2 runs from the release-notes skill's prepare.sh and
render.sh; Path 3 runs from generate-api-docs.sh. The shared,
general-purpose Cake machinery (shared.cake, download.cake) stays under
scripts/infra/shared/.
| Path | Produces | Entry point | Needs |
|---|---|---|---|
| 1 — API diffs | artifact 4 | docs-api-diff Cake target | dotnet (+ NuGet feed) |
| 2 — Release notes | artifact 3 | .agents/skills/release-notes/scripts/prepare.sh + render.sh | dotnet, python3, git history, gh (PR authors); render is offline |
| 3 — API reference (mdoc) | artifact 2 | generate-api-docs.sh | dotnet, mono (to run mdoc.exe) |
mdoc.exe is a .NET Framework executable. docs.cake invokes it under mono when
not on Windows, so Path 3 runs on any host with mono-complete installed — a Linux CI
runner or the local Docker image, not only Windows.
Three Copilot skills drive or assist these paths (.agents/skills/):
| Skill | Owns | Phase it performs |
|---|---|---|
release-notes | artifacts 3 + 4 | Runs Prepare (the deterministic engines, via scripts/prepare.sh) then Polish and Finalize (AI rewrites only prose JSON, then scripts/render.sh renders offline). See spec §2.2/§4.4. |
api-docs | artifact 2 | Fills the "To be added." placeholders mdoc leaves in the XML with real prose, following the .NET doc guidelines. |
skia-analyst | analysis | Diffs versions / surfaces what changed and what's missing; used for release announcements and gap analysis, not for writing the committed artifacts. |
The release-notes skill exposes the whole Path 2 engine through two thin
entrypoints under its own scripts/: scripts/prepare.sh is the Prepare
orchestrator — it calls the docs-api-diff Cake target directly, then the engine's
release-notes-data.py and release-notes-index.py; scripts/render.sh is the
offline finalizer around release-notes-render.py --all. The engine those
entrypoints run — release-notes-data.py, release-notes-index.py,
release-notes-render.py — lives under scripts/infra/docs/ alongside the API-diff
and mdoc engines; the shared, general-purpose Cake machinery (shared.cake,
download.cake) stays under scripts/infra/shared/.
flowchart TB
subgraph this["mono/SkiaSharp"]
urn["update-release-notes\n(ubuntu-latest)"]
sync["auto-docs-submodule-sync\n(ubuntu-latest)"]
end
subgraph docsrepo["mono/SkiaSharp-API-docs"]
writer["auto-api-docs-writer\n(Linux + mono)"]
end
urn -->|"Prepare engines 3+4,\nthen AI Polish → one PR"| prRel["PR: release notes + api diffs\n(this repo)"]
writer -->|"mdoc stubs +\nAI placeholder fill → PR"| prDocs["PR: XML docs\n(docs repo)"]
prDocs -->|"merged"| sync
sync -->|"bump docs/ submodule → PR"| prBump["PR: update submodule\n(this repo)"]
| Workflow | Repo | Runner | Produces | Paths |
|---|---|---|---|---|
update-release-notes | this repo | ubuntu-latest (native dotnet + python) | one PR with release notes and API diffs | 1 + 2 |
auto-api-docs-writer | docs repo | Linux + mono | PR with regenerated + AI-filled XML docs | 3 |
auto-docs-submodule-sync | this repo | ubuntu-latest | PR bumping the docs/ submodule pointer | — |
Key points:
| Point | What it means |
|---|---|
| Paths 1 + 2 = one pipeline, one PR | update-release-notes runs the deterministic Prepare phase (Cake API diffs + Python notes) in its own job, hands off to the AI Polish phase via an artifact, and opens a single rolling PR with both. Triggers: pushes to main and a daily cron — main walks every release/* ref and reads v* tags itself, so release branches/tags need no trigger of their own. If nothing changed, no PR is opened (spec §2.3). |
| Path 3 is cross-repo | The engines live in this repo but the XML lives in the docs repo, so auto-api-docs-writer lives in the docs repo, checks SkiaSharp out to borrow the engines, regenerates the stubs, AI-fills placeholders, and opens its PR there. |
auto-docs-submodule-sync closes the loop | Once the docs-repo PR merges, it bumps this repo's docs/ submodule pointer so the new XML is picked up here. |
Everything CI does can be reproduced locally by calling the same entry points. The only host requirement beyond the .NET SDK is mono for Path 3, and gh for Path 2's PR-author lookup.
| Mode | What it is | When to use |
|---|---|---|
| Native | Install the deps yourself and call the entry points (dotnet cake --target=docs-api-diff --nugetDiffPrerelease=true, prepare.sh/render.sh, generate-api-docs.sh) or the related Cake targets in writing-docs.md. | When you've installed the deps yourself. |
Docker (scripts/infra/docs/docker/run.sh) | A local-only convenience image that pre-installs dotnet + mono + python + gh and runs the same targets/scripts against a bind-mounted checkout, so a local run reproduces what CI produces. Its api-diffs subcommand invokes docs-api-diff directly. | A one-command reproducible run without installing the deps yourself. |
Docker is for local reproducibility only — CI does not use it. The CI runners install the same dependencies natively on Linux and call the same entry points. The image simply mirrors that dependency surface so the two stay identical.