This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
# Build entire solution
dotnet build
# Build specific projects
dotnet build src/libs/AutoSDK/AutoSDK.csproj
dotnet build src/libs/AutoSDK.SourceGenerators/AutoSDK.SourceGenerators.csproj
dotnet build src/libs/AutoSDK.CLI/AutoSDK.CLI.csproj# Run all tests
dotnet test
# Run specific test projects
dotnet test src/tests/AutoSDK.UnitTests/AutoSDK.UnitTests.csproj
dotnet test src/tests/AutoSDK.SnapshotTests/AutoSDK.SnapshotTests.csproj
# Run with filter
dotnet test --filter "FullyQualifiedName~DataTests"
dotnet test --filter "FullyQualifiedName~NamingTests"
dotnet test --filter "FullyQualifiedName~JsonTests"# Run CLI locally (from src/libs/AutoSDK.CLI/)
dotnet run -- generate openapi.yaml --namespace MyApi --output Generated
# Install CLI globally for testing
dotnet pack src/libs/AutoSDK.CLI/AutoSDK.CLI.csproj
dotnet tool install --global --add-source ./src/libs/AutoSDK.CLI/bin/Debug autosdk.cli --prerelease
# Uninstall
dotnet tool uninstall --global autosdk.cliAutoSDK (src/libs/AutoSDK/)
- Core library with data models, naming strategies, and code generation logic
- Key directories:
Models/: OpenAPI representation (EndPoint, ModelData, Client, etc.)Naming/: Name generation strategies (ModelNameGenerator, PropertyNameGenerator, etc.)Sources/: Code generation templates (partial methods that emit C# code)Extensions/: OpenAPI parsing and manipulation helpersSerialization/: JSON/form serialization strategies
AutoSDK.SourceGenerators (src/libs/AutoSDK.SourceGenerators/)
- Roslyn incremental source generators (IIncrementalGenerator)
- Two generators:
SdkGenerator: Generates models, methods, clients, auth, JSON serializationCliGenerator: Generates CLI commands from API operations
- Reads configuration from
.csprojproperties andAdditionalFilesmetadata
AutoSDK.CLI (src/libs/AutoSDK.CLI/)
- Standalone CLI tool using System.CommandLine
- Commands in
Commands/:generate: Main SDK generationinitialize: Scaffold new SDK project structurecli: Generate CLI commandsconvert: Convert OpenAPI formatssimplify: Simplify OpenAPI specsai: AI-powered spec generation/modificationhttp: HTTP utility
OpenAPI Spec → Data.Prepare() → SchemaContext tree → Name generation →
Data structure (Classes/Enums/EndPoints/Clients) → Sources.* methods → C# files
Key classes:
Data.Prepare(): Main entry point, processes OpenAPI → Models.DataSchemaContext: Internal tree representing schema relationshipsSettings: Configuration container (parsed from .csproj properties)Sources: Static partial methods for code templates
All settings defined in AutoSDK.SourceGenerators.props as CompilerVisibleProperty:
- Prefix:
AutoSDK_* - Examples:
AutoSDK_Namespace,AutoSDK_ClassName,AutoSDK_GenerateMethods - Set in
.csproj<PropertyGroup>or per-file viaAdditionalFilesmetadata - Full list: See
src/libs/AutoSDK.SourceGenerators/AutoSDK.SourceGenerators.props
Important settings:
AutoSDK_GenerateSdk: Generate full SDK (default: true)AutoSDK_GenerateModels: Generate only modelsAutoSDK_GenerateMethods: Generate only methodsAutoSDK_GenerateConstructors: Generate client constructorsAutoSDK_JsonSerializerContext: Enable trimming supportAutoSDK_ModelStyle: Class/Record/ReadonlyRecordStructAutoSDK_MethodNamingConvention: SimpleOperationId/MethodAndPath/OperationIdWithDotsAutoSDK_SplitByTags: CLI-only. Emit a per-tag package family instead of one project (default: false)
autosdk generate --split-by-tags --package-id <id> emits <id>.Core (shared models, converters,
serializer context, runtime support), one package per OpenAPI tag, a base package holding the root
client plus any untagged operations, a .slnx, and a deterministic autosdk-packages.json.
Reference graph is base -> tag* -> Core.
A model, enum or named union reached by exactly one tag ships in that tag's package;
ModelOwnershipResolver decides that and both PackagePlanner (routing files) and CSharpPipeline
(emitting a matching serializer context per package) must get the same answer, which is why the
decision lives in one place and is recomputed rather than carried on Data.
TagOwnershipAnalyzer proposes ownership from schema reachability; the resolver then verifies it
against the generated C# reference graph and demotes anything that would cross a package boundary,
because the schema graph is depth- and cycle-limited and under-reports on large specs
(specs/github.yaml is the case that proved it).
Three things only this mode changes, all gated on Settings.SplitByTags:
- Shared runtime members (
EndPointSecurityResolver,AutoSDKRequestOptionsSupport, polling helpers,AutoSDKHttpResponse.CreateHeaders,AutoSDKServerConfiguration,ResponseStream,JsonSerializerContextProvider) becomepublic+[EditorBrowsable(Never)]viaSources.SharedMemberModifier/SharedNestedMemberModifier. With--strong-name-public-keythey stayinternaland the generated projects emitInternalsVisibleToinstead. - Each package emits its own JSON serializer context (
CorekeepsSourceGenerationContext, a tag gets{Ns}.{Tag}SourceGenerationContext, the facade{Ns}.{RootClient}SourceGenerationContext), chained through a generatedTypeInfoResolverproperty. Chain through that, never through a context'sDefault: a context is bound to the options it was built with and throws when a resolver hands it different ones. Every context also registers the converters for everything it can be asked to resolve, not just what it owns. - The OAuth2 support types are hoisted out of the root client class into a namespace-level
{Ns}.AutoSDKOAuth2.g.csin Core (Sources.OAuth2SupportTypes). Left nested they would sit in the base package while tag clients and tag operation bodies need them, so every tag assembly would reference the base package that already references it.
A Roslyn generator cannot create projects, so SdkGenerator forces SplitByTags off and reports
OAG004. The guarantee that the family actually compiles as separate assemblies is
CliSplitByTagsTests in AutoSDK.IntegrationTests.Cli, which builds the generated .slnx plus a
tag-only and a base-package consumer — not a hand-maintained list of members.
Tests use a two-project pattern for trimming/NativeAOT support:
<ServiceName>.Models/ (Step 1: Generate models)
├── .csproj
│ ├── AutoSDK_GenerateSdk=false
│ ├── AutoSDK_GenerateModels=true
│ └── AutoSDK_GenerateJsonSerializerContextTypes=true
└── Reference: AutoSDK.SourceGenerators
<ServiceName>/ (Step 2: Generate methods)
├── Tests.cs
├── SourceGenerationContext.cs
├── .csproj
│ ├── AutoSDK_GenerateSdk=false
│ ├── AutoSDK_GenerateMethods=true
│ ├── AutoSDK_GenerateConstructors=true
│ └── AutoSDK_JsonSerializerContext=<Namespace>.SourceGenerationContext
└── Reference: <ServiceName>.Models
Why two projects?
- Proper source generator dependency ordering
- Enables JsonSerializerContext for trimming/NativeAOT
- Models project generates types for serialization context
- Main project uses the serialization context
specs/ contains OpenAPI specifications:
- Real APIs:
openai.yaml,github.yaml,twitch.json, etc. - Test fixtures:
petstore.yaml,special-cases.yaml - Used by integration tests via
<AdditionalFiles Include="specs/foo.yaml" />
Incremental Generators: Uses IIncrementalGenerator for efficient caching and O(n) performance
Partial Class Generation: Each model generates multiple partials:
.g.cs: Main definition.Json.g.cs: Serialization extensions.IValidatableObject.g.cs: Validation (optional)
Naming Collision Resolution: ModelNameGenerator handles identifier conflicts via suffixes
Polymorphism Support: Computes discriminator mappings from oneOf/anyOf schemas
Two JSON Serializers: SystemTextJson (default, trimming-compatible) or NewtonsoftJson
Model Styles:
Class: Traditional reference types (default)Record: Immutable recordsReadonlyRecordStruct: Value types for performance
- Naming: PascalCase for types/methods, camelCase for parameters/variables
- Structure: Using directives at top, followed by namespace
- Patterns: Nullable reference types, expression-bodied members, pattern matching
- Error Handling: Validate parameters with explicit null checks
- Formatting: 4 spaces indentation, braces on new lines
- Testing: MSTest with FluentAssertions (unit), Verify (snapshot)
- Add partial method signature to
Sources.cs - Implement in appropriate file under
Sources/ - Call from
Data.csor generator class - Add snapshot test in
AutoSDK.SnapshotTests
- Add/modify spec in
specs/ - Create snapshot test in
AutoSDK.SnapshotTests - Run test to generate baseline:
dotnet test --filter "TestName" - Verify output in
_snapshots/directory
- Add
CompilerVisiblePropertyinAutoSDK.SourceGenerators.props - Add property to
Settingsrecord inSettings.cs - Parse in
Settings.GetSettings()method - Use in generation pipeline
- Add
<IsRoslynComponent>true</IsRoslynComponent>to generator.csproj - Set breakpoint in generator code
- Launch debugger on test project build
- Alternative: Use
System.Diagnostics.Debugger.Launch()in generator
- Runtime: Microsoft.OpenApi, Microsoft.OpenApi.Readers
- Build: Microsoft.CodeAnalysis.CSharp (Roslyn)
- Testing: MSTest, FluentAssertions, Verify
- CLI: System.CommandLine
- Target Frameworks: net4.6.2, netstandard2.0, net8.0, net9.0