For each target file listed in the CSV, skip index.md (the header landing page is handled separately in step 8).
For each remaining target file:
a. Get the stub or existing file
Use the ADO REST API to check if the file already exists on main:
# List files on main branch
$mainFiles = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
If the target filename appears in the listing, retrieve it from main and use its content as the starting point — preserve existing text, but always update ms.date to today's date when making any content changes. Only write the file to the output folder if you make changes. Do not copy unchanged files.
$content = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{filename.md}&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
$content | Out-File -FilePath (Join-Path $outputDir "{filename.md}") -Encoding utf8
If it does not exist on main, retrieve the stub from the stubs branch:
$content = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{filename.md}&versionDescriptor.version=stubs/main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
$content | Out-File -FilePath (Join-Path $outputDir "{filename.md}") -Encoding utf8
b. Retrieve source declaration from the OS source tree
Use the substrate-mcp MCP server to find the API's source declaration:
- Extract the API name from the filename (e.g.
nf-soundwireclass-somefunc.md → SomeFunc)
- Search for the definition:
search_code(query="def:{ApiName}", ext="h")
- If
def: doesn't find it, try keyword search:
search_code(query="{ApiName}", ext="h", path="**/{header}.h")
- Once found, use
get_file_content with a line range to retrieve the full declaration including:
- SAL annotations (
_In_, _Out_, _Inout_, _In_opt_, _Out_opt_, _Reserved_)
- Return type
- All parameter names and types
- Any adjacent comments describing the function
- For structures/enums, retrieve the full type definition with all fields/values
c. Gather supplemental information
Use the microsoft.docs.mcp server's microsoft_docs_search tool to search for any existing published docs or related conceptual content for the API:
microsoft_docs_search(query="{ApiName} WDK driver")
d. Find formatting models
Find 1-2 completed reference pages in the same header folder to use as formatting models. Use the ADO REST API to list files, then retrieve them:
# List existing completed docs of the same entity type in the {header} folder
$mainFiles = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
# Filter for files matching the same prefix (e.g. "nf-") and pick 1-2
Then read the model files:
$modelContent = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{model-filename.md}&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
If no completed pages exist in the same folder, look in a related header folder. Read these model files to match their style.
e. Write the completed documentation page
Write the completed file to {outputDir}\{filename.md}. Follow these rules:
YAML Frontmatter
- Preserve all existing metadata from the stub
- Add
ai-usage: ai-assisted if not already present
- Set
techroot to the same value as existing files in the same header folder. If no existing files, prompt user for value.
- Always set
ms.date to today's date in MM/DD/YYYY format — both for new files and when updating existing files with changes
- Verify
req.header, f1_keywords, api_name, topic_type are correct
req.header uses lowercase (e.g. classpnp.h). req.include-header uses sentence capitalization (e.g. Classpnp.h). Note that req.include-header does not always require a value — omit it or leave it empty when an include header is not applicable.
req.construct-type for macros: If the source declaration is a preprocessor macro (#define), set req.construct-type: macro, not function. The file prefix remains nf- and the title should say "macro" (e.g. CLEAR_FLAG_NOFENCE macro (classpnp.h)).
- Leading underscores in UIDs: If the stub file's UID uses a leading underscore (e.g.
NE:wdbgexts._POOL_HEADER_FIELD_NAME), preserve that underscore in the UID of the generated document. However, all customer-facing content (title, description, headings, prose) must use the version without the leading underscore (e.g. POOL_HEADER_FIELD_NAME). For api_name: and f1_keywords:, include both versions — the underscored name and the public name — so the page is discoverable by either form.
Content Sections (in order)
## -description
- Write a concise one-paragraph summary of what the API does
- Focus on the public API behavior, not internal implementation
- Use bold for API names referenced in text
## -parameters (functions/callbacks/macros)
- One
### -param {Name} [{direction}] subsection per parameter
- Map SAL annotations to direction labels:
_In_ → [in]
_Out_ → [out]
_Inout_ → [in, out]
_In_opt_ → [in, optional]
_Out_opt_ → [out, optional]
_Reserved_ → [in] with note "Reserved. Must be zero/NULL."
- Describe each parameter concisely
- Macro parameters requiring address-of (
&): If the macro implementation takes the address of a parameter (e.g. &(Flags)), describe the parameter as "An addressable {type} storage location (such as a variable or structure field)" rather than "A {type} value." A macro that takes &(param) requires an lvalue, not an arbitrary expression.
## -struct-fields (structures only)
- One
### -field {FieldName} subsection per field
- Union types use the structure classification. If the C source declares a
union, it is still documented with the ns- file prefix and req.construct-type: structure. All prose (description, field descriptions, remarks, and cross-references from other pages) must call it a "structure", never a "union." The heading should read # {NAME} structure, not # {NAME} union.
## -enum-fields (enumerations only)
- One
### -field {ValueName}:{NumericValue} subsection per value (if numeric value is known)
## -ioctlparameters (IOCTLs only)
- Document input buffer, output buffer, and status block
## -returns (functions with return values)
- Document the return type and possible return values
- For NTSTATUS returns, list common status codes
## -remarks
- Add usage notes, IRQL requirements, important caveats
- Include code examples when helpful
- If the entity is an extended version (ends in
Ex, or a number like 2, 3, or V2), reference the base entity and document only the extensions
- Don't generate repetitive information
- Don't include internal implementation details
## -see-also
- List related APIs as relative Markdown links, one per line
- Write entries in alphabetical order
- Use relative paths:
[**OtherFunc**](nf-{header}-otherfunc.md) for same-header APIs
- Use
../ paths for cross-header: [**CrossFunc**](../otherheader/nf-otherheader-crossfunc.md)
Formatting Rules
- Use bold for API names in prose:
**IoCreateFile**
- Use italic for parameter name references in prose:
*DesiredAccess*
- Use site-relative links for learn.microsoft.com content:
[title](/windows-hardware/drivers/...)
- Code blocks use
cpp language tag
- Ensure exactly one blank line at the end of the file