Skip to content

How to Add a New Docs Page

This guide covers everything you need to add a new .mdx page to xrmghost-docs and have it appear on the published documentation site.

Before writing anything, confirm your change falls within your team’s ownership area:

  1. Open the relevant architecture and section landing pages, starting with Source of Truth.
  2. Locate the section that matches the content you want to add (e.g. src/content/docs/cli/, src/content/docs/attributes/).
  3. Check the existing section docs, reviewers, and repo conventions to confirm who owns that slice.
  4. If the section does not exist yet or the owner is unclear, contact the Documentation team as the interim owner.

If you are adding a net-new section (a new product area), align with the Documentation team first so ownership and navigation are agreed before the page lands.

The src/content/docs/ folder hierarchy mirrors the site navigation. Follow these naming rules:

RuleExample
Use lowercase, hyphen-separated folder namessrc/content/docs/my-product/
Each section must have an index.mdx as its landing pagesrc/content/docs/my-product/index.mdx
Sub-sections live in nested folderssrc/content/docs/my-product/advanced/index.mdx
Do not invent deeply nested hierarchies speculatively — start flatsrc/content/docs/my-product/getting-started.mdx

If a folder already exists for your product area, add your page there. Do not create a parallel folder.

Create a new .mdx file in the appropriate folder. The file name should be lowercase and hyphen-separated.

src/content/docs/my-product/my-new-page.mdx

Every .mdx file must begin with a YAML frontmatter block. Two fields are required:

---
title: My New Page
description: A one-sentence summary of what this page covers.
---
FieldRequiredNotes
titleYesShown in navigation and browser tab. Keep it short (≤ 60 chars).
descriptionYesOne sentence. Used in SEO meta tags and link previews.
Additional fieldsNoOnly add fields that Starlight’s content schema explicitly supports. Do not invent fields.

The build will reject any .mdx file that is missing title or description.

  • If the content is not ready, use an explicit TODO marker rather than inventing placeholder content:

    > **TODO:** This section will be expanded in [story/ticket reference].
  • Fake or hallucinated content is harder to fix than an honest placeholder and misleads readers.

  • Cite the ADO story ID when the TODO has a known follow-up owner.

5.1 Template Learnings from the First Real Docs Slice

Section titled “5.1 Template Learnings from the First Real Docs Slice”

The first repo-level public docs slice for src/content/docs/attributes/ exposed a few reusable lessons for future work:

  • Start from a real package surface, not the template headings. Pull examples and terminology from the source package README or code so the first draft is anchored in what actually exists.
  • Separate narrative guidance from generated reference early. If exhaustive schema pages are not available yet, document the package purpose and usage shape now, and reserve a future location such as src/content/docs/<area>/schema/ for generated content later.
  • Use bounded TODOs only for missing reference material. Narrative basics such as purpose, audience, and a starter example should be written as soon as they are known; TODO markers should remain limited to honestly missing generated or machine-derived content.
  • Record template gaps when they appear. If a placeholder page required extra explanation about source-of-truth boundaries or future generated content, add that guidance here so the next team does not rediscover it from scratch.

When making CLI-related documentation changes, choose your path based on what you are changing:

Path A — Narrative-only change (stays in xrmghost-docs)

Section titled “Path A — Narrative-only change (stays in xrmghost-docs)”

You are editing the conceptual overview, getting-started guide, or any human-written explanation of the CLI.

  • Files: src/content/docs/cli/index.mdx, src/content/docs/cli/getting-started.mdx (or other narrative files outside src/content/docs/cli/reference/).
  • Where to work: This repository (xrmghost-docs).
  • PR target: xrmghost-docs dev.
  • Reviewer: Documentation team or CLI team.
  • Do not touch src/content/docs/cli/reference/ files in this PR.

Path B — Generator or source-driven change (starts in xrmghost-cli)

Section titled “Path B — Generator or source-driven change (starts in xrmghost-cli)”

You are changing a CLI command, its --help text, its flags, or the command manifest that feeds the reference generator.

  • Where to work: xrmghost-cli repository.
  • PR target: xrmghost-cli (its own review and merge process).
  • Effect on xrmghost-docs: After the xrmghost-cli change merges and the generator runs, updated files land under src/content/docs/cli/reference/ automatically. Do not manually replicate those changes in xrmghost-docs.
  • Reviewer: CLI team in xrmghost-cli. Approval in xrmghost-docs does not substitute for approval in xrmghost-cli.

Path C — Coordinated docs-plus-generator change (linked workstream or single shared PR)

Section titled “Path C — Coordinated docs-plus-generator change (linked workstream or single shared PR)”

You need to update both the CLI source/generator in xrmghost-cli and narrative or contract documentation in xrmghost-docs as part of the same logical change (for example, a new command that also needs a narrative walkthrough, or a generator contract change that also updates architecture docs).

  • Acceptable approach: A single shared PR or coordinated workstream that touches both repositories is acceptable when both sides must move together.
  • Required: The PR or workstream description must clearly identify which changes are narrative (owned by xrmghost-docs) and which are source/generator changes (owned by xrmghost-cli), with appropriate reviewers from each team.
  • Not acceptable: Using a shared PR as a bypass to skip xrmghost-cli review for generator or source changes, or to manually edit generated files in src/content/docs/cli/reference/ without running the generator.
  • Do not let the shared PR scope drift into editing generated reference files directly. Generated content under src/content/docs/cli/reference/ must always originate from the generator run, not from hand edits.

For the current public rules, use this guide together with Source of Truth. If the CLI ownership split or generation contract is unclear, stop and confirm it with the Documentation and CLI teams instead of guessing.

5.3 QueryEngine Operator Reference Maintenance

Section titled “5.3 QueryEngine Operator Reference Maintenance”

The QueryEngine operator-support reference (src/content/docs/query-engine/operator-support.mdx) is transcribed by hand from frozen snapshots of xrmghost-framework-host. It is the one content path in this repository with no generator behind it. This section is the authoritative contributor procedure for that rule; docs/query-engine/sources/README.md documents the snapshots themselves and their provenance. Keep the two in agreement instead of restating the procedure anywhere else.

  • The snapshots live under docs/query-engine/ — the generated matrix docs/query-engine/operator-support-matrix.generated.md and seven .cs.txt source copies under docs/query-engine/sources/.
  • They sit outside src/content/, so they are never published. The .txt extension is there so nobody tries to compile them.
  • They are read-only copies. Never hand-edit a snapshot, not even to fix a typo or to make a line agree with the published page: an edit here would make the engine say something the engine does not say, and the whole point of keeping the copies next to the page is that the derivation can be checked by diffing two files that sit side by side.
  • Current provenance: xrmghost-framework-host@ff32df7 (branch dev), taken on 18 September 2026. Coverage is closed at 88 of the SDK’s 89 condition operators — childof is the only deliberate refusal, and masksselect is supported without having a FetchXML spelling — and continuous evolution is not expected, which is why a dated, signed copy was chosen over permanent cross-repo infrastructure.
  • FetchXML spellings and the SDK pin come from OperatorContractTests.cs.txt (field FetchXmlSpellings), which carries 91 spellings covering 88 of the 89 SDK condition operators — more spellings than operators because Equal and NotEqual each accept more than one FetchXML spelling, and one operator fewer than the inventory because MasksSelect has no FetchXML spelling at all. It is the single entry of OperatorsWithoutAFetchXmlSpelling in the same file.
  • FetchXmlOperatorMap.cs.txt is not enough. It holds only the 24 spellings that OperatorIdentity.Normalize cannot reach on its own; every spelling that differs from the SDK member name by separators alone is absent by design. Deriving the full list from it loses most of it.
  • Resolving a spelling is not supporting an operator. A spelling resolves to an operator identity; whether the engine can evaluate it is IConditionEvaluatorRegistry.IsSupported’s answer. Never use the presence of a spelling as evidence of support.
  • The SDK pin is Microsoft.CrmSdk.CoreAssemblies 9.0.2.45. AssemblyVersion 9.0.0.0 is not the pin: that package ships an assembly whose AssemblyVersion is 9.0.0.0 and whose FileVersion is 9.2.47.9489, and 9.0.0.0 stays 9.0.0.0 whichever package version delivered it. Citing it as the pin would make the public reference non-reproducible.

Nothing notices automatically, and nothing should. For this flow only there is no CI comparison between the two repositories, no generator, no verification tool and no automatic cross-repo link — unlike the CLI reference in Path B above, which is populated by a real cross-repo generator from xrmghost-cli. Pick a commit of xrmghost-framework-host and replace things by hand, in this order:

  1. docs/query-engine/operator-support-matrix.generated.md, regenerated from the new xrmghost-framework-host commit. It is the inventory the page transcribes dispositions, required context and refusal reasons from, so it comes first.
  2. The seven .cs.txt copies under docs/query-engine/sources/, replaced whole from the same commit.
  3. The provenance line — commit and date — in docs/query-engine/sources/README.md, in the header of the generated matrix, and in the provenance block of the published page.
  4. Re-transcribe and re-review src/content/docs/query-engine/operator-support.mdx against the matrix and the seven copies. That comparison against the sources is the review; no tool performs it, and it is a one-off per engine change, not a standing job.

Replacing only the .cs.txt copies while leaving the old matrix published is the failure to avoid: dispositions and required context come from the matrix, not from the contract copies, so the page would describe an engine that no longer exists while its provenance claims it is current. Nothing catches that — for this flow the only automated gate is npm run build, which proves the site compiles, not that it tells the truth.

xrmghost-docs is a standalone site: there is no portal submodule to advance and no sync step to run. Once the re-transcription merges to dev, .github/workflows/deploy.yml runs npm ci and npm run build and publishes the result to GitHub Pages on that push, so an updated QueryEngine reference is live as soon as it lands on dev (see 7. Publishing). The build is the only thing standing between the merge and the public page — and it proves the site compiles, not that the page still matches the engine.

Periodic surveillance of new SDK operators does not belong to this flow; it is a dedicated reconnaissance, ADO-2349.

  1. Branch off dev using the naming convention docs/<short-description> (or the swarm branch convention if operating in a swarm run).
  2. Add only the files you own or have been given permission to touch.
  3. Request a review from at least one person on the owning team. If the owning team is unclear, ask the Documentation team to route the review.
  4. Run npm run build (astro build) locally and make sure it passes. No workflow runs on a pull request in this repository: .github/workflows/deploy.yml is the only workflow and it triggers on pushes to dev, so the one automated check — npm run build — runs after your merge, not before it. There is no separate frontmatter linter either: a missing title or description fails the build itself.
  5. Merge to dev after approval. If the build then fails on dev, nothing is published until it is fixed (see 7. Publishing).

xrmghost-docs is a standalone site. It is built with Astro/Starlight and published to GitHub Pages by .github/workflows/deploy.yml, which runs npm ci and npm run build and deploys the result on every push to dev.

  • Once your PR merges to dev, publication is automatic. There is no submodule pointer to advance and no sync script to run.
  • You do not need to take any action in xrmghost-user-portal.
  • If the build fails, the deploy does not happen: the merged change is live only once npm run build succeeds in the workflow.
  • Confirmed the correct product owner or reviewer path before editing
  • File placed in the correct src/content/docs/ subfolder
  • File name is lowercase and hyphen-separated
  • Frontmatter includes title and description
  • No fake content — used > **TODO**: markers where content is missing
  • PR targets dev, reviewer from owning team assigned
  • npm run build passes locally
  • If the change touches the QueryEngine operator reference, followed 5.3 — snapshots replaced whole, provenance updated, page re-transcribed and reviewed by hand