Table of Contents

Codec and Asset Framework Internals

This appendix is the contributor reference for the codec compiler, generated ABI, and asset transaction.

Dependency Direction

Shard.Codecs                         runtime value programs and annotations
     ^
     | generated calls
     |
Shard.Assets                         asset identity, materialization, registry, publication
     ^
     | generated calls
     |
Shard.Util.Codegen.Codecs            analyzer and compiler plans, never a runtime dependency

Compiler Inputs and Plans

Annotation and schema inputs both produce immutable compiler plans before source emission.

C# symbols -> annotation compiler --+
                                    +-> CodecGenerationPlan -> JSON, BSON, validation source
schema + overlay -> schema compiler +-> AssetGenerationPlan -> materialization and definitions

Compiler plans do not retain Roslyn symbols, runtime objects, or executable delegates. The generator emits direct C# for JSON reads, BSON reads and writes, recursive validation, codec map dispatch, parent aware asset reads, and containment edges.

The compiler plans disappear after source emission, so runtime code does not carry schema documents, reflection metadata, expression trees, or field descriptors. The generated calls remain reachable under trimming and NativeAOT without a runtime interpreter.

Generated Public API

Ordinary generated types expose Type.Codec. The __Shard* members are hidden from editor completion and form the compiler ABI used by generated code, so application code should not depend on their names or layout.

An imported asset root exposes one of two registration members:

  • AssetDefinition is emitted when the consuming assembly owns the asset runtime contract.
  • AssetBinding is emitted for a separate assembly that references the public generated definition API.

Both members preserve one static family identity. A binding can either wrap a complete direct definition or resolve a definition published by an AssetBindingHost. Generated containment uses exact dependency slots rather than store name lookup.

Codec Annotations

Annotation Declaration Meaning
[GenerateCodec] type Requests validation and the selected JSON or BSON endpoints.
[CodecName] field or property Replaces the encoded member name.
[CodecRequired] field or property Requires wire presence even when construction has a default.
[CodecIgnore] field or property Removes the member from the generated value shape.
[CodecVersion] type or member Limits the declaration to an inclusive codec version range.
[CodecAlias] field or property Accepts an older decode name for a selected version range.
[CodecValidator<T>] type or member Runs a reusable ICodecValidator<T>.
[CodecNormalize] method Marks the type's one local normalization hook.
[CodecValidate] method Marks the type's one local validation hook.
[CodecMap] class or interface Defines discriminator and extension policy for a polymorphic value.
[CodecCase<T>] concrete type Adds one generated case to a codec map.
[CodecEnumValue] enum field Sets the encoded enum name.
[CodecSurrogate<T>] type Makes a generated type the wire representation of another type.
[CodecCatalogEntry] static property or method Adds a directly callable codec to the generated assembly catalog.
[BsonRepresentation] field or property Changes one BSON numeric encoding without changing JSON.
[SchemaConforms] type Asserts that a hand written generated type matches one schema node.

[CodecValidator<T>] runs reusable validator types on a generated type or member, while [CodecValidate] marks the generated type's own validation method. Both enter the same validation plan, which validates children before member validators and then runs the local method.

[CodecEnumValue] remains separate from [CodecName] because enum fields and modeled object members have different compiler roles and diagnostics, even though both affect encoded text.

Schema and Overlay Ownership

schema/manifest.json selects schema roots, one overlay, and a generated namespace for each import. Files under schema/imports/ remain source data, while the overlay under schema/overlays/ records reviewed corrections.

An asset entry may declare the family path, file suffix, parent and tag fields, and default inheritance. Field entries may correct presence, representation, inheritance, or containment. Polymorphic entries declare fixed or extensible codec map policy.

Schema hashes in reviewedAgainst protect the review against source drift.

Asset Load Transaction

Modules register complete batches during setup. When preload begins, AssetsModule and AssetRegistry advance the host through these phases:

  1. Complete generated host bindings after every module has finished setup.
  2. Load Common assets and the authored files for every typed store.
  3. Register every completed definition with one materialization session.
  4. Materialize authored roots and drain contained assets to a global fixed point.
  5. Capture every family into a private candidate graph.
  6. Add derived contributions against that complete graph, then publish every family together.
  7. Run feature validation and cross family checks.
  8. Resolve stable indices and immutable lookup state.
  9. Prepare client packets, discard callback targets, and expose the final AssetMap<T> instances.

No callback observes a partly published graph. A parse, materialization, containment, contribution, validation, resolution, or preparation failure faults the candidate and leaves no published collection from that attempt.

Validation Responsibilities

Generated value validation checks only the completed value graph. It covers nullability, required members, nested values, schema constraints, reusable validators, and local type hooks.

Feature validation receives AssetLoadContext<T>, which supplies the current immutable family, property overrides, and the registry. It can check Common files, tags, or another captured family without teaching the codec about server state.