Contribute documentation
Orientation
Change the canonical page under website/docs; do not hand-edit immutable
version snapshots. Keep the page focused on one reader task and preserve its
selected-version context. C# is the canonical example language.
A user guide should orient the reader, show a runnable example, name a real pitfall, state version boundaries, and link to related guidance plus the same-version API root. Contributor-only detail belongs in this cluster rather than in the primary user path.
Example
Use front matter that identifies the page role and primary reader:
---
id: choose-a-culture
title: Choose a culture
diataxis: how-to
persona: application developer
example: illustrative
---
If an example is executable, add it to the documentation example harness and import the tested source. Mark non-executable fragments as illustrative and keep them syntactically honest.
Pitfall
Do not copy current behavior into a historical page without package or tagged source evidence. Do not link a historical guide to a current API route. Avoid hand-maintained language counts and lists; update locale YAML and rerun the one coverage generator.
Version notes
The canonical corpus includes main/preview as a first-class public preview.
Published snapshots remain version-honest and immutable except for reviewed,
targeted factual corrections through the snapshot workflow.
Validate a documentation change
Restore the pinned tools and site dependencies, then run the same gates used by the Pages workflow:
dotnet tool restore
pnpm --dir website install --frozen-lockfile
pwsh tools/docs/build.ps1 -Mode Validate
pwsh tools/docs/test.ps1 -RequireNativeAot
pnpm --dir website run check:content
pnpm --dir website run build
pnpm --dir website run check:links
pnpm --dir website run check:search-budget
pnpm --dir website run check:artifact
pnpm --dir website run test:unit
pnpm --dir website run typecheck
pnpm --dir website run test:ci
The build reads every published package and immutable snapshot from
website/humanizer-versions.json. It does not check out historical branches or
rewrite a published snapshot.
The repository-scoped Renovate GitHub App keeps Node, pnpm, and site
dependencies current from .github/renovate.json; its pull requests run these
same gates.
Publish a stable version
Before package publication, add one unpublished, NuGet-backed version entry,
its bounded compatibility overlay, and its upgrade boundary and guide in a
normal pull request. After the signed package is available from NuGet.org,
dispatch the release workflow from main:
gh workflow run docs-release.yml --ref main \
-f version=3.1.0 \
-f promote_latest=true
The workflow uses the existing atomic snapshot command, validates the package
hash and signature with bounded retries, runs the complete site gates, and
opens a pull request containing only the generated snapshot, sidebar, selector,
and manifest changes. A retry after the release branch is pushed resumes only
when that branch changes no paths outside the defined release outputs and those
outputs exactly match the newly validated result. Any conflict, or an already
published snapshot, fails without force-pushing or changing the site.
Production changes only after that pull request is merged and the ordinary
main deployment succeeds.
Use the explicit historical-correction mode for a factual correction to an already published page; never regenerate the complete version:
pwsh tools/docs/snapshot.ps1 -Version 3.0.10 \
-CorrectPage scenarios/strings-and-casing.mdx
Deploy and roll back
.github/workflows/docs.yml validates every pull request. A successful main
run uploads the complete Docusaurus output as one Pages artifact. After the
build passes, automation stages the archive, checksum, and deployment manifest
together in a draft docs-pages-candidate-v1-<run-id>-<attempt> prerelease.
After the production smoke test passes, automation creates and publishes a
separate immutable docs-pages-deployment-v1-<run-id>-<attempt> release.
Failed or abandoned attempts therefore cannot become the current rollback
source.
To restore a retained artifact, select its immutable deployment release and read the archive run ID and attempt from that release's manifest. These can differ from the deployment attempt after a failed-job rerun:
release_tag=docs-pages-deployment-v1-123456789-2
manifest=$(mktemp)
gh release download "$release_tag" \
--pattern 'deployment-*.json' \
--output "$manifest"
jq '{archiveRunId, archiveRunAttempt}' "$manifest"
gh workflow run docs.yml --ref main \
-f rollback_run_id="$(jq -r .archiveRunId "$manifest")" \
-f rollback_run_attempt="$(jq -r .archiveRunAttempt "$manifest")"
The rollback run verifies the retained artifact's custom domain, stable, historical, and preview roots, search data, redirects, sitemap, and link safety before redeploying it. It does not rebuild historical documentation.