azure-sdk-for-rust / generate_api
AGENTS.md887 starsChanged 29 days agoMITView on GitHub
eng/tools/generate_api is a Rust CLI that generates the following public API artifacts for a target crate: The tool exposes: Keep renderers independent from unstable rustdoc-types details. Prefer renderer option structs over threading booleans through helper stacks. The shared model is the boundary between extraction and rendering. It currently models: Workspace crate models are cached with Arc<T> to avoid repeated deep clones during workspace re-export expansion. Supported item kinds: Ordering is deterministic and shared by both output formats.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository22 other files it gives its agents.
- AGENTS.mdAGENTS.md
- samples/AGENTS.mdAGENTS.md
- sdk/core/AGENTS.mdAGENTS.md
- sdk/cosmos/AGENTS.mdAGENTS.md
- sdk/cosmos/azure_data_cosmos_emulator/AGENTS.mdAGENTS.md
- sdk/cosmos/docs/AGENTS.mdAGENTS.md
- agentic-workflowsSkill
- analyze-codeSkill
- check-spellingSkill
- create-crateSkill
- create-hotfixSkill
- create-perf-testSkill
- create-recorded-testSkill
- document-cratesSkill
- export-apiSkill
- lint-markdownSkill
- prepare-releaseSkill
- review-doc-commentsSkill
- update-emitterSkill
- update-rustSkill
- cosmos-design-structSkill
- cosmos-pre-commit-validationSkill
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
27 rules. About 0 are common, 14 are distinctive, and 13 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈14
Distinctive · estimate
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Run the CLI from the repo root, or pass --root <repo> when invoking it from elsewhere.
- Distinctive: line 17
Keep this file current as design behavior or integration points change.
- Distinctive: line 18
Keep this file concise for LLMs but still easy for humans to review.
- Very specific: line 19
Keep README.md focused on basic intent and usage: what the tool does, how to call it, what it writes, and where it is called from under …
- Very specific: line 34
default markdown writes api.md
- Very specific: line 37
check comparisons ignore line-ending differences and mismatches exit 1
- Distinctive: line 46
Keep rustdoc schema compatibility logic isolated from the tool-owned model and renderers
- Very specific: line 48
Add common dependencies to the eng/tools workspace using versions already used by the repository
- Very specific: line 50
Keep implementation and tests separate when practical.
- Very specific: line 62
Keep renderers independent from unstable rustdoc-types details.
- Distinctive: line 125
Keep separate source impl blocks separate even when their rendered headers match.
- Very specific: line 134
If the source path is already publicly reachable keep pub use ...
- Distinctive: line 135
If the source path is non-public or stripped lift the declaration to the public re-export site
- Distinctive: line 141
When a lifted type has sibling explicit trait impls or inherent impls lift those too so the public surface keeps the visible impl blocks
- Distinctive: line 146
Prefer the canonical external path when rustdoc provides one
- Distinctive: line 157
remove whitespace around path separators
- Very specific: line 158
remove extra spaces around clippy:: lint paths
- Very specific: line 160
do not synthesize workspace-defined derives such as SafeDebug
- Distinctive: line 161
keep synthesized derives on the same visible declaration surface after re-export lifting
- Distinctive: line 163
keep lifted explicit trait impls on the same visible surface as the lifted type
- Very specific: line 204
keep Self unchanged in non-receiver positions
- Distinctive: line 205
keep inherent impls in their original impl-header shape including generics and bounds
- Distinctive: line 216
remove empty generic parameter lists after elision
- Very specific: line 217
remove attributes generated by the async_trait expansion
- Distinctive: line 228
no doc comments means an empty patch file
- Distinctive: line 290
keep the crate root unwrapped and let APIView provide the root tree node
- Very specific: line 348
Keep ApiModel stable when updating rustdoc-types.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
generate_api agent guidance
Goal
eng/tools/generate_api is a Rust CLI that generates the following public API artifacts for a target crate:
api.md— one fencedrustblockapi.metadata.yml— YAML metadata forapi.mdapi.md.map— an ECMA-426 source map for declaration lines inapi.mdapi.documentation.patch— a unified diff that adds doc comments back toapi.mdapiview.json— an APIView tree-styleCodeFile
Scope
- This tool lives under
eng/tools/generate_api. - Run the CLI from the repo root, or pass
--root <repo>when invoking it from elsewhere. - Keep this file current as design behavior or integration points change.
- Keep this file concise for LLMs but still easy for humans to review. Prefer short bullets and only enough detail to preserve intent.
- Keep
README.mdfocused on basic intent and usage: what the tool does, how to call it, what it writes, and where it is called from undereng/pipelines/. Keep deeper extraction rendering and ordering rules here.
CLI
The tool exposes:
--manifest-path <path/to/Cargo.toml>--root <path/to/repo>optional; defaults to the current directory--format <markdown|apiview>defaultmarkdown--reviewemits Markdown review sidecars and is valid only withmarkdown--checkcompares generated content with existing files without writing; missing files pass--output-dir <directory>defaults to the target crate directory;--outputremains a CLI alias
Behavior:
- default
markdownwritesapi.md markdown --reviewalso writesapi.md.map,api.documentation.patch, andapi.metadata.yml--format apiviewwritesapiview.json- check comparisons ignore line-ending differences and mismatches exit
1 - progress goes to stdout
- fatal errors go to stderr and exit
1
Toolchain and workspace
- Standalone bin crate in the
eng/toolsworkspace - Uses
eng/tools/rust-toolchain.tomltoolchainnightly-2026-04-14 - API Review Hub uses the compiler and setup files from
languageToolsRefto keep base and target generation consistent; see README.md for toolchain setup. - Keep rustdoc schema compatibility logic isolated from the tool-owned model and renderers
- Current deps:
rustdoc-types,serde,serde_json,clap,sha2 - Add common dependencies to the
eng/toolsworkspace using versions already used by the repository rustc-devstays included because long-term direction remains closer to librustdoc/HIR- Keep implementation and tests separate when practical. Prefer sibling
tests.rsfiles over nestedmod testsblocks. Tiny local#[test]items may stay inline
Extraction design
Pipeline:
- run
cargo metadata - run
cargo rustdoc -Z unstable-options --output-format json - load rustdoc JSON
- normalize into a tool-owned model
- render Markdown or APIView from that model
Keep renderers independent from unstable rustdoc-types details. Prefer renderer option structs over threading booleans through helper stacks.
Shared intermediate model
The shared model is the boundary between extraction and rendering.
It currently models:
- package name, version, edition, rust-version, and features
- modules
- item doc comments
- item attributes
- public items
- inherent impl blocks
- explicit trait impl blocks
- associated members including trait methods associated types and associated consts
Workspace crate models are cached with Arc<T> to avoid repeated deep clones during workspace re-export expansion.
Supported item kinds:
- re-exports
- macros / proc macros
- functions
- structs
- enums
- traits
- trait aliases
- inherent impls
- explicit trait impls
- unions
- type aliases
- constants
- statics
Ordering rules
Ordering is deterministic and shared by both output formats.
- crate root renders first and is not wrapped in
mod - child modules recurse in lexical order
- within each module:
- re-exports first
- macros / proc macros
- free functions
- types and other kinds by stable item-kind order
- ties break alphabetically by item name
- inherent impl blocks sort immediately after their owning struct / enum / union
- inherent impl ordering is:
- generic type parameters first
- inferred
_type args next - explicit resolved types last
- ties break by rendered self type then declaration text
- associated members sort alphabetically within each impl or trait block
Module rendering
- Markdown output renders child modules as nested
pub mod name { ... } - APIView uses the same logical module tree with root unwrapped
- Module doc comments and attributes render above the module declaration
- Markdown renders crate-root docs above synthetic
#![crate_name = "..."]and#![crate_type = "..."]; real crate attrs follow those anchors - Trait members are extracted into
ApiItem.membersinstead of being embedded in the declaration string. Renderers handle the opening{and implied closing}separately so each member gets its own APIViewLineId - Inherent impl blocks on structs enums and unions are first-class items with
ApiItem.members. Do not flatten their members into the owning type. This preserves typestate surfaces such as multipleread()methods on differentSasBuilderimpls - Keep separate source impl blocks separate even when their rendered headers match. Preserve each block's own attrs docs and members
- Non-derived trait impls are also first-class items with
ApiItem.members
Re-export rules
Re-export handling is driven by public reachability and workspace membership.
Same-crate re-exports
- If the source path is already publicly reachable keep
pub use ... - If the source path is non-public or stripped lift the declaration to the public re-export site
Workspace-crate re-exports
- Re-exports from workspace crates expand into declarations at the re-export site
- Applies at crate root and inside public modules
- When a lifted type has sibling explicit trait impls or inherent impls lift those too so the public surface keeps the visible impl blocks
External-crate re-exports
- Re-exports from crates outside this workspace stay
pub use ... - Prefer the canonical external path when rustdoc provides one
Attribute and doc normalization
Attributes are normalized once in extraction before either renderer consumes them.
Current normalization:
- fix rustdoc pretty-printed
cfgandcfg_attr - rewrite
pin(__private(...))topin_project(...)orpin_project - flatten rustdoc whitespace and newlines inside attributes while preserving string literals
- remove whitespace around path separators
- remove extra spaces around
clippy::lint paths - synthesize
#[derive(...)]for known non-workspace derive traits discovered from#[automatically_derived]impls on structs enums and unions - do not synthesize workspace-defined derives such as
SafeDebug - keep synthesized derives on the same visible declaration surface after re-export lifting
- render non-derived trait impls as explicit
implblocks instead of folding them into derives - keep lifted explicit trait impls on the same visible surface as the lifted type
Known synthesized derives:
CloneCopyDebugincludingfmt::Debug,core::fmt::Debug,std::fmt::DebugDefaultEqHashOrdPartialEqPartialOrdserde::Serializeserde::Deserialize
Documentation handling:
- rustdoc docs stay separate from attrs in the shared model
- omit
cfg_attrwhose only attribute isdoc; rustdoc interleaves active conditional docs with ordinary docs, so preserve that text without reconstructing conditional attributes - handle
docanddocsrsconditions alike; preserve non-doc attributes and conservatively retain unsupported or incomplete attribute values - markdown output omits doc comments and emits them as a companion patch
- APIView renders comment tokens with documentation markers
Package metadata rendering:
- Markdown renders the crate name first; APIView uses only the top-level
PackageName - Markdown review output also writes
api.metadata.ymlwithapiHash,packageVersion,packageRelativePath,parserVersion, andrustVersion - missing description, edition, or rust-version values are omitted
- multiline descriptions render with the
Descriptionlabel on its own line - features use
defaultpluspackage.metadata.docs.rs.featureswhen present - without docs.rs feature metadata, all Cargo features render
- crates without defined features render an empty
defaultfeature defaultrenders first, followed by other visible features in lexical order- render feature names only, except for the
defaultfeature's lexically sorted children - Markdown renders the crate name as H1, metadata before features, and features under an H2
- APIView renders metadata and features as leading text-token lines followed by a blank text line
Signature normalization:
- render receivers spelled as
self: Self,self: &Self,self: &mut Selfasself,&self,&mut self - keep
Selfunchanged in non-receiver positions - keep inherent impls in their original impl-header shape including generics and bounds
- render functions in inherent impls as
pub; keep trait and trait-impl functions without visibility
Async-trait rendering
For traits and trait impls whose rustdoc-expanded methods carry synthetic async-trait lifetimes:
- synthesize
#[async_trait] - preserve
#[async_trait(?Send)]when the expanded future has noSendbound - restore the original
async fnsignature and future output type - elide synthetic
'lifeNand'async_traitlifetimes from signatures - remove empty generic parameter lists after elision
- remove attributes generated by the
async_traitexpansion
Comments patch output
render::markdown::render_linesrenders every line and marks doc comment linesrender::markdown::render_from_linesdrops the marked lines to produceapi.mdrender::patchturns the marked lines into a unified diff againstapi.md- the diff only contains insertions
- each contiguous doc-comment block becomes its own hunk
- each hunk includes the doc comments, all following attributes, and the first declaration line as context
- crate-root doc hunks anchor only to the synthetic
crate_nameandcrate_typelines, not to later real crate attrs - no doc comments means an empty patch file
Source map output
source_mapowns the generic ECMA-426 v3 schema and Base64 VLQ encoding- the shared model stores repo-relative zero-based declaration locations
sourceRootpoints from an in-repo output directory to the repository root- output outside the repository omits
sourceRoot;sourcesalways stay repo-relative - Markdown maps item, module, and member declaration lines only
- headings, metadata, fences, attributes, documentation, and structural closing lines are unmapped
namesis omitted
APIView output design
Targets:
- TypeSpec source: https://github.com/Azure/azure-sdk-tools/blob/main/tools/apiview/parsers/apiview-treestyle-parser-schema/codeFile.tsp
- JSON schema: https://github.com/Azure/azure-sdk-tools/blob/main/tools/apiview/parsers/apiview-treestyle-parser-schema/CodeFile.json
Top-level fields used:
PackageNamePackageVersionParserVersionLanguageReviewLines
Important nested structures:
ReviewLineLineId?TokensChildren?IsContextEndLine?RelatedToLine?
ReviewTokenKindValueHasPrefixSpace?HasSuffixSpace?IsDocumentation?NavigationDisplayName?NavigateToId?RenderClasses?
Token kinds used:
Text = 0Punctuation = 1Keyword = 2TypeName = 3MemberName = 4Comment = 7
Current APIView decisions:
LanguageisRust- stable
LineIdgeneration:- module:
module.{sanitized_path} - item:
{module_line_id}.{item_name}_{index} - member:
{item_line_id}.{member_name}_{index}
- module:
- reject duplicate
LineIds - keep the crate root unwrapped and let APIView provide the root tree node
- include
HasPrefixSpaceandHasSuffixSpace - doc comments use
CommentwithIsDocumentation = true - root/module inner attributes render at their scope with the original
#!text preserved - represent nested modules through
ReviewLine.Children - emit navigation metadata on modules and non-impl top-level items; impl blocks stay out of the tree
- re-export tree nodes navigate by each imported item's leaf name rather than a generic
useentry - APIView tree ordering within a module is:
- consts/statics
- type aliases/re-exports
- macros/proc macros
- free functions
- type-like items alphabetically
- type all declaration tokens:
- keywords use
Keyword - item names use
TypeNameexcept functions useMemberName - other identifiers default to
TypeName - punctuation uses
Punctuation
- keywords use
- tokenize synthesized derives like other attrs
- render trait members as child lines with their own
LineId - render inherent impl members as child lines of their own impl line so duplicate method names stay distinct across impl headers
- explicit trait impls use the same typed token rules as source-shaped declarations
Current pipeline integration
Current API review caller chain under eng/pipelines/:
eng/pipelines/pr.ymloreng/pipelines/pullrequest.ymleng/pipelines/templates/stages/archetype-sdk-client.ymleng/pipelines/templates/jobs/ci.ymleng/pipelines/templates/jobs/pack.ymleng/scripts/Pack-Crates.ps1
pack.yml also runs the shared create-apireview step. Pack-Crates.ps1 currently generates the artifact that step consumes. If pipeline adoption changes update this caller chain instead of adding a second path.
API Review Hub integration
eng/pipelines/templates/jobs/apireview-hub-build-tools-rust.ymlstages the executable and a few setup files fromlanguageToolsRefin$(BinOutputDirectory)so historical checkouts do not need current engineering helpers.eng/pipelines/templates/jobs/apireview-hub-generate-artifact-rust.ymlinstalls the staged manifest's channel using staged repo-local helpers and checkouteng/common, then invokes the executable against the requested package checkout.- Generation passes the checked-out repository as
--rootbecause$(BinDirectory)may be outside the repository. - The complete review bundle is written to
$(ArtifactOutputDirectory).
Rustdoc / librustdoc alignment
The design target remains librustdoc-like behavior not HTML scraping.
Preserved assumptions:
- rustdoc runs after HIR is available
- body type-checking is not required
- the important outputs are public API signatures attrs docs macros and module structure
The implementation still acquires data through rustdoc JSON but the architecture should keep future movement toward direct librustdoc/HIR possible without rewriting the renderers.
Rustdoc schema compatibility
rustdoc_compat.rsisolates schema-specific attribute conversion and source recovery- Validate
format_versionbefore deserializing the complete rustdoc JSON - Keep
ApiModelstable when updatingrustdoc-types; do not pass schema-specific types to renderers - Recover source attributes only when rustdoc omits information needed to preserve existing output
- Compare Markdown and APIView output against the previous nightly when updating the schema
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

