Foundatio
FoundatioFx/Foundatio
A skill your agent uses when working with Foundatio infrastructure abstractions for .NET -- caching, queuing, messaging, file storage, distributed locking, or background jobs.
The public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs.
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install aws/aws-sdk-net sdk-conventions --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .claude/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .claude/skills/sdk-conventions && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .claude/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventionsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install aws/aws-sdk-net sdk-conventions --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .agents/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .agents/skills/sdk-conventions && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .agents/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install aws/aws-sdk-net sdk-conventions --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .cursor/skills/sdk-conventions && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .cursor/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/aws/aws-sdk-net.git --path generator/SmithyDotNet/skills/sdk-conventions--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install aws/aws-sdk-net sdk-conventions --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .gemini/skills/sdk-conventions && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .gemini/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install aws/aws-sdk-net sdk-conventionsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .github/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .github/skills/sdk-conventions && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .github/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aws/aws-sdk-net --skill sdk-conventions -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install aws/aws-sdk-net sdk-conventions --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/generator/SmithyDotNet/skills/sdk-conventions .opencode/skills/sdk-conventions && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "sdk-conventions" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/sdk-conventions into .opencode/skills/sdk-conventions/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "sdk-conventions", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
sdk-conventionsThe public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs.
SDK Conventions is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization. The public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs. what can differ. Use before writing or changing any SmithyDotNet writer.
Its SKILL.md is about 6.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering API design. It works with Amazon Web Services and .NET. The repository describes itself as: The official AWS SDK for .NET. For more information on the AWS SDK for .NET, see our web site:. The licence is Apache-2.0.
6 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit f36df89. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are csharp).
From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
docs.aws.amazon.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
SDK Conventions loads about 6.3k tokens when it runs. Until then it costs about 53 tokens; SKILL.md has 2,883 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from aws/aws-sdk-net at commit f36df89, republished under its Apache-2.0 licence (© aws). 2,883 words, ~6,277 tokens.
.claude/skills/sdk-conventions/SKILL.md (or your agent's skills folder).When reviewing a service migration's generated output, open and diff every single generated file — every operation's request marshaller and response unmarshaller, every model, exception, and client file. No sampling. Reviewing one operation and generalizing "clean" to its neighbors is how regressions ship. Thousands of files is not a reason to skip any.
The public API surface can be identical while the wire behavior changes. AssemblyComparer and "the public surface is unchanged" only check the public contract; they cannot see marshaller/unmarshaller bodies — ResourcePath, query parameters, headers, serialization. A clean AssemblyComparer is necessary, not sufficient; it is not a substitute for reading the diff.
A removed line in generated output is a red flag — investigate it, do not wave it through. Real example: the generator dropping request.AddSubResource("aws_iam", "t") and folding the query literal into request.ResourcePath = "/token?aws_iam=t" left the public API identical while silently changing the request sent to the wire (? gets percent-encoded, dropping the flag).
@streaming union) implements Amazon.Runtime.EventStreams.IEventStreamEvent, fully qualified (no
using) so it can't clash with a per-stream {Namespace}.Model.IEventStreamEvent. A request event
stream's input member becomes a Func<Task<I{Union}Event>> {Member}Publisher property: it keeps any
modeled [AWSProperty]/[Obsolete] but has no IsSet (the marshaller wires the Func unconditionally).
Its doc content matches C2J.[AWSProperty] attributes on public members (Required, Min, Max)partial modifier on all generated types{Namespace}, {Namespace}.Model)internal bool IsSet{Property}() per member — the public AWSSDKUtils.IsPropertySet
reflection API and existing marshallers invoke these by name{TypeName}.g.cs to distinguish generated filesusing directive order#region blocks (purely cosmetic)The SDK builds with warnings-as-errors. Generated files must include #pragma warning disable for warnings that would otherwise break the build. The exact set doesn't need to match the current SDK file-for-file, but the output must compile cleanly. Common ones:
CS0612 / CS0618 — obsolete/deprecated member usage (generated code may reference deprecated shapes)CS1570 — malformed XML doc comments (common with complex HTML from @documentation traits)Every generated file starts with the full Apache 2.0 license block followed by the
"Do not modify this file. This file is generated from the {model-filename} service model." notice,
where {model-filename} is the Smithy model file name (e.g. cloudtrail-data-2021-08-11.normal.json).
The exact text lives in Writers/FileHeader.cs.
A service has two derived names, equal for most services:
BaseName (C2J ClassName, metadata.json's base-name) → the generated type names: client,
config, exception/request bases, endpoint types. Model classes go in {Namespace}.Model.ServiceName (C2J ServiceFolderName, the namespace minus Amazon.) → everything else:
AWSSDK.{X} package names, sdk/src|test/Services/{X} trees, _sdk-versions.json keys,
{X}.slnx, the endpoint tests [TestCategory], and the paginator factory types
(I{X}PaginatorFactory — C2J's templates use ServiceNameRoot there).They diverge when metadata.json overrides the namespace: sesv2 has class
AmazonSimpleEmailServiceV2Client but package/folder/paginators SimpleEmailV2. When adding a
name to a writer, check the shipping SDK for which of the two it follows.
rename map wins when it has an entry for the shape; error codes still use the shape nameeventData), .NET uses PascalCase (EventData)eventID → EventID (not EventId)ContentLength is omitted from the response class —
AmazonWebServiceResponse already declares it — but the response unmarshaller still assigns the
inherited property. Response-only, matching C2J (the MediaStoreData case).IAmazon{BaseName} (e.g. IAmazonCloudTrailData)Amazon{BaseName}Client (e.g. AmazonCloudTrailDataClient)Amazon{BaseName}ConfigAmazon{BaseName}ExceptionAmazon{BaseName}RequestGenerated files go under Generated/. Prefer .g.cs suffix:
Generated/
IAmazon{BaseName}.g.cs
Amazon{BaseName}Client.g.cs
Amazon{BaseName}Config.cs # plain .cs so CI's Amazon*Config.cs glob stages it
Amazon{BaseName}Exception.g.cs
Model/
Amazon{BaseName}Request.g.cs # empty service request base
{OperationName}Request.g.cs
{OperationName}Response.g.cs
{ShapeName}.g.cs
{ExceptionName}.g.csA structure that doubles as an operation input/output normally gets only its
{Op}Request/{Op}Response wrappers — no {ShapeName}.g.cs. Exception: when other generated
code references the shape through a member (directly or as a list/map element), the standalone
class is emitted too, because member properties are typed with the plain class name (C2J parity:
drs SourceServer has one, kinesis EnhancedMonitoringOutput does not).
DocSamplesWriter and DocSampleMetadataWriter emit
docgenerator/AWSSDKDocSamples/{ServiceName}/{ServiceName}.GeneratedSamples.cs and
{ServiceName}.GeneratedSamples.extra.xml from smithy.api#examples, rendering values as C2J's
Example.cs does but with the SDK's types, so a copied sample compiles once placeholders like <data> are
filled in. Both are written unformatted
(BatchGenerator passes format: false): the samples file is never compiled and can hold placeholders like
<binary data> that the Roslyn formatter would mangle. A service with no examples, or S3 (its samples are
hand-written), gets no files, so an existing sample file stays as it is; examples the model lacks but C2J
had are a model gap. Example keys are matched to members (TypeMapper.ResolveMembers) ignoring case after
CustomizationTransform, so, as in C2J, a member renamed by emitPropertyName (beyond case) drops out of
the sample. The samples are built before anything is written, so a bad example fails the
service before anything is deleted, and written only once its code has generated.
Differences from C2J:
{Operation}-{n} (the trait has no example id). They only link an .extra.xml entry to
its code. Each is unique, unlike C2J's repeated example-1, which made the docs build (it takes the
first matching #region) show the first sample's code for every later one.comments (C2J's // comment after an assignment) are lost: the trait has no such field.int?, Stream, the enum's class); C2J used
non-nullable scalars, MemoryStream and string. Locals that are C# keywords are escaped (@event).DateTime.UtcNow if it's out of range); C2J printed a 12-hour hour, dropped milliseconds and wrote
DateTime.UtcNow for any number. A fractional float gets f.Func publisher property can't be written from example data.global::Amazon.Runtime.Documents.Document with its content, using its
collection initializer; C2J rendered an empty initializer of a class that doesn't exist.&, < and > (titles also
") and drop characters XML 1.0 can't carry; C2J escaped only quotes, so a backslash, newline or &
produced broken code or XML.CodeWriter (platform newlines, 2-space XML indent); documentation text
is written as it is. The docs build parses the XML and left-justifies each region, so the rendered docs
don't change.Protocol-independent; only the per-event payload (un)marshalling and the response unmarshaller's body
differ (see marshalling). Each @streaming union is emitted once, however many operations share it.
Request (union sent as an operation input):
I{Union}Event plus a {Event} : I{Union}Event partial per event. The name
comes from the union, never the operation (shipped: Lex V2 IStartConversationRequestEventStreamEvent),
and is emitted once per union even when several operations send it (the protocol test client shares one
union across four).@error members get no partial: a client never sends an error event.Response (union returned as an operation output):
{Union} is emitted as the EnumerableEventOutputStream<RuntimeEvent, {BaseName}EventStreamException>
subclass (C2J parity; RuntimeEvent is the alias described under Both). Each union member is a mapping entry keyed on the member name verbatim (the wire
:event-type; the dict is OrdinalIgnoreCase): @error members feed ExceptionMapping, the rest
EventMapping plus a PascalCase {Name}Received handler. The union gets no plain model class and no
structure unmarshaller (the response unmarshaller does new {Union}(context.Stream)); its events keep theirs.{Op}Response implements IDisposable and its dispose pattern releases the event stream member.
Only when the operation also sends an event stream (bidi, or input-only) does it implement
Amazon.Runtime.EventStreams.IEventInputStreamContextOwner (explicit SetEventInputStreamContext under a
CA1033 suppression) and dispose the context first (C2J parity: Bedrock ConverseStreamResponse is
AmazonWebServiceResponse, IDisposable, Lex V2 StartConversationResponse adds the owner interface).{BaseName}EventStreamException.Both:
EventStream, so its
marker is IEventStreamEvent, same simple name as the runtime's (the only runtime type an I{Union}Event
can shadow). Anywhere under the .Model namespace (model classes, MarshallTransformations) the service's
marker silently wins over the import; files outside it that import both namespaces (the client) hit CS0104.
Every writer that needs the runtime's emits using RuntimeEvent = Amazon.Runtime.EventStreams.IEventStreamEvent;
and uses RuntimeEvent. An alias named IEventStreamEvent would not help (a type in an enclosing
namespace beats it too); one no shape can be named after does.| Generated class | Inherits from |
|---|---|
| Client interface | IAmazonService, IDisposable |
| Client class | AmazonServiceClient, IAmazon{BaseName} |
| Service exception base | AmazonServiceException |
| Service request base | AmazonWebServiceRequest |
| Request classes | Amazon{BaseName}Request (the service request base) |
| Response classes | AmazonWebServiceResponse, plus , IDisposable when an output member is @streaming (emits a #region Dispose Pattern that disposes each streaming member's stream) |
| Structure classes | No base type (plain class) |
| Exception classes | Amazon{BaseName}Exception (the service exception base) |
Config class (Amazon{BaseName}Config) | ClientConfig |
partialEvery generated class and interface uses the partial modifier.
The public surface must match. Internal implementation can vary.
Required public surface:
/// <summary>
/// Gets and sets the property EventData.
/// <para>
/// The content of an audit event...
/// </para>
/// </summary>
[AWSProperty(Required=true)]
public string EventData { get; set; }
/// <summary>
/// Checks to see if the EventData property is set.
/// </summary>
internal bool IsSetEventData() => this.EventData != null;The generator emits auto-properties ({ get; set; }) plus an internal IsSet{Property}()
method per member. The current SDK uses explicit backing fields, but the public surface (and
the reflection API) only needs the property and the IsSet method — a backing field is not
required.
Exception — emitIsSetProperties customization. A listed member (shape name → modeled member
names) also gets a public bool Is{Property}Set { get; set; } whose accessors call
InternalSDKUtils.GetIsSet/SetIsSet(value, ref field). A property can't be passed by ref, so the
member gets a private _{Property} backing field (collections keep the InitializeCollections
initializer) instead of an auto-property. IsSet{Property}() returns Is{Property}Set. Only nullable
value types and collections have SetIsSet overloads; any other listed member throws. Doc text
matches C2J's StructureGenerator.tt.
[AWSProperty] attribute rules:
Required=true when member has @required trait, unless it also carries @idempotencyToken (the SDK fills it)Min=N when member has @length trait with min, or @range trait with minMax=N when member has @length trait with max, or @range trait with maxCollections use the AWSConfigs.InitializeCollections initializer to support both V4 (null
default) and V3-compat (empty list default) modes. The matching IsSet encodes the V3/V4
"empty counts as set?" rule so callers see consistent behavior in both modes:
[AWSProperty(Required=true, Min=1, Max=100)]
public List<AuditEvent> AuditEvents { get; set; } = AWSConfigs.InitializeCollections ? new List<AuditEvent>() : null;
internal bool IsSetAuditEvents() => this.AuditEvents != null && (this.AuditEvents.Count > 0 || !AWSConfigs.InitializeCollections);*.customizations.json)The .NET-owned override layer (not part of the shared, upstream Smithy model). A hook with a Smithy
trait equivalent (a rename pins @jsonName), or a structural edit (a member-keyed renameShape), is folded into the model in-memory by
CustomizationTransform.Apply before the ServiceIndex is built; every other hook is checked by
CustomizationTransform.Validate and read from GenerationContext.Customizations by shape and member
name where the member is resolved (TypeMapper.ResolveMembers), never stored on a shape. An unknown
hook key fails deserialization (fail-closed) rather than silently diverging from C2J.
shapeSubstitutions.renameShape is applied first, as an entry in the service's rename map (the shape ID
is unchanged, so targets still resolve). Every hook keys a renamed shape by its modeled name. C2J keys a renamed
structure by its new name, so S3's entries for its renamed structures must be rekeyed on migration. Wire error codes
keep the modeled name. A model that already renames the shape fails.
A Smithy-only "Structure$member" key instead copies that member's target under the new name (QApps: one
Action enum is C2J's PermissionInputActionEnum and PermissionOutputActionEnum); the original is dropped once
nothing targets it.
Generation/Customizations/CustomizationsModel.cs parses;
each hook's per-level behavior lives on that record, its Apply/Validate step, and its lookup.generator/customization-hooks.md.When implementing transformation logic (HTML sanitization, naming rules, type mapping, etc.), consult the existing C2J generator at generator/ServiceClientGeneratorLib/ to understand the correct behavior. Key files:
GeneratorHelpers.cs / Utils.cs — HTML processing, naming transformsMember.cs — property naming, type resolutionShape.cs / ExceptionShape.cs — shape naming conventionsGenerators/SourceFiles/StructureGenerator.tt, Generators/SourceFiles/Exceptions/ExceptionSerialization.t4 — model and exception class outputGenerators/Marshallers/*.tt — T4 templates showing exact output patternsThe new generator is a clean reimplementation, not a port — but the existing generator defines what "correct" looks like.
The @documentation trait contains HTML. DocumentationFormatter.Cleanup ports the existing
generator's CleanupDocumentation (ServiceClientGeneratorLib/Generators/BaseGenerator.cs).
The transform, in order:
<para> line breaks are inserted afterward.<code>...</code> → <c>...</c><p>...</p> → <para>...</para> (including <p> tags carrying attributes)<br>, <fullname>, <function>, <p/> (bare and attribute-carrying forms)<i>...</i> → keep as-is<examples>...</examples> and <!-- ... --> snippets<para>...</para> wrapper (the summary's first paragraph is unwrapped)Note: HTML entities are NOT decoded (& stays &) — the existing generator does not
decode them, so neither do we.
<para>Interface for accessing {BaseName}</para>, a blank /// line, then the service @documentationContainer for the parameters to the {OperationName} operation. then the operation @documentationThis is the response object from the {OperationName} operation.@documentationEach operation method includes an <exception cref="{full exception type}"> (body = the error shape's
@documentation) per error, plus
<seealso href="http://docs.aws.amazon.com/goto/WebAPI/{serviceId}-{apiVersion}/{OperationName}">REST API Reference for {OperationName} Operation</seealso>.
Operation exceptions inherit from Amazon{BaseName}Exception (not directly from AmazonServiceException).
Must expose these public constructors:
(string message)(string message, Exception innerException)(Exception innerException)(string message, Exception innerException, ErrorType, string errorCode, string requestId, HttpStatusCode)(string message, ErrorType, string errorCode, string requestId, HttpStatusCode)Operation exceptions also include a #if !NETSTANDARD block containing:
[Serializable] attribute on the classprotected serialization constructor (SerializationInfo, StreamingContext) — deserializes each serialized exception member via info.GetValue, then calls base(info, context)public override void GetObjectData(SerializationInfo, StreamingContext) carrying all three attributes as a unit: [System.Security.SecurityCritical] plus the CA2123 and CA2134 SuppressMessage attributes; body is base.GetObjectData(info, context) then info.AddValue(...) per additional member.
The serialization constructor and GetObjectData are symmetric: both loop over the same member set, every modeled member except message (C2J parity), so base-owned RequestId/ErrorCode are serialized here even though they get no property (see "Exception Member Property Names"). Both are keyed on the .NET property name. For exceptions whose only member is message (e.g. all CloudTrail Data exceptions), both bodies contain only the base call.The service-level exception base (Amazon{BaseName}Exception) inherits from AmazonServiceException, exposes the same six public constructors as operation exceptions, and includes [Serializable] plus the protected serialization constructor, but does not need its own GetObjectData override unless it adds serialized fields.
Canonical treatment lives in type-mapping's "Error Shape Members". Summary: errorType → property
RequestErrorType (wire name unchanged); Retryable emitted with new; Equals gets new on any
structure (not exception-specific); RequestId/ErrorCode get no property but stay in serialization
and unmarshalling; every other member is emitted as-is even when it shadows an inherited property.
An error shape carrying the @retryable trait emits a public override that marks it retryable:
public override RetryableDetails Retryable { get; } = new RetryableDetails(<throttling>);<throttling> is the trait's throttling value (true/false); an empty @retryable ({}) is retryable but not throttling (false). The base AmazonServiceException.Retryable returns null, so emitting a non-null RetryableDetails is what marks the exception retryable — errors without the trait emit no override. RetryableDetails resolves via Amazon.Runtime (already in the model file's usings).
Must expose:
{Op}Response {Op}({Op}Request request) per operationTask<{Op}Response> {Op}Async({Op}Request request, CancellationToken cancellationToken = default)#if NET8_0_OR_GREATER, since C2J omits h2 operations on .NET Framework and pre-net8 netstandard.Endpoint DetermineServiceOperationEndpoint(AmazonWebServiceRequest request)#if NET8_0_OR_GREATER): CreateDefaultClientConfig() and CreateDefaultServiceClient(AWSCredentials, ClientConfig)Use #if directives to include sync methods only for .NET Framework targets (#if NETFRAMEWORK).
Must expose:
public virtual {Op}Response {Op}({Op}Request request) per operationpublic virtual Task<{Op}Response> {Op}Async(...) per operation#if NET8_0_OR_GREATER, since C2J omits h2 operations on .NET Framework and pre-net8 netstandard.DetermineServiceOperationEndpoint implementationCustomizeRuntimePipeline overrideServiceMetadata property overrideUse #if NETFRAMEWORK directives to include sync methods only for .NET Framework targets. Both sync and async methods are public virtual on the client class.
aws.api#clientEndpointDiscovery is legacy: only dynamodb, timestream-query and timestream-write use it, and new
services use endpoint rule sets (Endpoints 2.0) instead. Matching C2J, each operation with
aws.api#clientDiscoveredEndpoint (except the discovery operation itself) gets an
{Op}EndpointDiscoveryMarshaller.g.cs and sets options.EndpointDiscoveryMarshaller and options.EndpointOperation;
the client overrides EndpointOperation to call the discovery operation.
Anything those three services don't model fails generation (discovery ids, discovery input, renamed or swapped
endpoint members, ...); see EndpointDiscoveryResolver and ClientClassWriter.WriteEndpointOperation.
C2J's {BaseName}EndpointDiscoveryMarshallingTests.cs is not generated: with no discovery ids it only checks the
required flag against the C2J model, which the Smithy trait replaces.
© aws, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in generator/SmithyDotNet/skills/sdk-conventions of aws/aws-sdk-net.
Open the folder on GitHubat commit f36df89
SDK Conventions next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| SDK Conventions this skillaws/aws-sdk-net | 145 | — | ~6.3k | Automated safety check: Pass | Apache-2.0 | |
| FoundatioFoundatioFx/Foundatio | 2.1k | — | ~3.9k | Automated safety check: Pass | Apache-2.0 | |
| .NET API Compatibility DesignAaronontheweb/dotnet-skills | 1.2k | 2 repos | ~2.7k | Automated safety check: Pass | MIT | |
| .NET Aspire Explicit ConfigurationAaronontheweb/dotnet-skills | 1.2k | 1 repos | ~1.3k | Automated safety check: Pass | MIT | |
| Csharp Nullable Reference TypesAaronontheweb/dotnet-skills | 1.2k | 1 repos | ~2.9k | Automated safety check: Pass | MIT | |
| Generate Testability Wrappersdotnet/skills | 5.6k | 1 repos | ~3.9k | Automated safety check: Pass | MIT |
FoundatioFx/Foundatio
A skill your agent uses when working with Foundatio infrastructure abstractions for .NET -- caching, queuing, messaging, file storage, distributed locking, or background jobs.
Aaronontheweb/dotnet-skills
Applies extend-only design rules to NuGet packages and distributed systems, covering source, binary and wire compatibility and how to deprecate members safely.
Aaronontheweb/dotnet-skills
Wires .NET Aspire AppHost resources into explicit environment-variable configuration, keeping application code free of Aspire client packages and service discovery.
Aaronontheweb/dotnet-skills
Guidelines for introducing and using nullable reference types (NRT) and System.Diagnostics.CodeAnalysis nullable attributes in C / .NET codebases.
dotnet/skills
DO NOT USE when the target already consumes an injected interface or built-in abstraction such as IFileSystem or TimeProvider, even if the request says "generate a wrapper"; no new wrapper is needed.
Nethereum/Nethereum
Sign Ethereum transactions with AWS KMS or Azure Key Vault HSMs using Nethereum.
aws/aws-sdk-net
A skill your agent uses when validating that a C2J-to-Smithy migrated AWS SDK service builds, packages, and stays API-compatible with the shipping SDK, or before reporting such a migration as done…
aws/aws-sdk-net
A skill your agent uses when working on the AWS SDK for .NET source code itself, including Core runtime changes, service client implementations, generator or model changes, repo-specific build and…
aws/aws-sdk-net
The Smithy JSON AST facts and model invariants the SmithyDotNet generator relies on.
aws/aws-sdk-net
Smithy shape to .NET type mapping, nullability, collection element rules, and error-shape naming/member rules.
aws/aws-sdk-net
What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.
Works with
Categories
The public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs. SDK Conventions is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization.NET - what must match vs.
SDK Conventions fits situations like: tasks that involve API design.
Run `npx skills add aws/aws-sdk-net --skill sdk-conventions -a claude-code`. Or copy the skill folder (generator/SmithyDotNet/skills/sdk-conventions in aws/aws-sdk-net) into .claude/skills/sdk-conventions in your project. Claude Code loads it when a task matches its description.
Run `npx skills add aws/aws-sdk-net --skill sdk-conventions -a codex`. Or copy the skill folder (generator/SmithyDotNet/skills/sdk-conventions in aws/aws-sdk-net) into .agents/skills/sdk-conventions in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add aws/aws-sdk-net --skill sdk-conventions -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/sdk-conventions, .gemini/skills/sdk-conventions, .github/skills/sdk-conventions and .opencode/skills/sdk-conventions in your project.
SKILL.md names no scripts, command-line tools or credentials: SDK Conventions is instructions for the agent only.
SKILL.md names 1 domain. In commands or code: docs.aws.amazon.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
SDK Conventions is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.3k tokens (SKILL.md is roughly 25k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with SDK Conventions: Foundatio (FoundatioFx/Foundatio, 2.1k stars), .NET API Compatibility Design (Aaronontheweb/dotnet-skills, 1.2k stars), .NET Aspire Explicit Configuration (Aaronontheweb/dotnet-skills, 1.2k stars) and Csharp Nullable Reference Types (Aaronontheweb/dotnet-skills, 1.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
aws (a GitHub organization, an official publisher) maintains it in aws/aws-sdk-net, which has 145 GitHub stars. The repository holds 6 skills in this directory. The repository was last updated on October 8, 2026.
Source: aws/aws-sdk-net on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.