Skip to content

Akka.Serialization.V2: closed generic messages + manifest-discriminated union fields (PoC for #8384) - #8385

Merged
Aaronontheweb merged 14 commits into
akkadotnet:devfrom
Aaronontheweb:feature/serialization-v2-unions-closed-generics
Jul 16, 2026
Merged

Aaronontheweb merged 14 commits into
akkadotnet:devfrom
Aaronontheweb:feature/serialization-v2-unions-closed-generics

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Jul 15, 2026 •

Copy link
Copy Markdown
Member

Proof of concept for #8384

Implements both parts of the design in #8384 for the source-generated MessagePack serializer:

Part 1 — [AkkaSerializable<T>] closed generic messages. A generic [AkkaSerializable] type is never serialized as an open definition (a source generator cannot reify open generics — same rule STJ enforces). Each closed construction is registered on the [AkkaSerializer] module using a generic form of the same [AkkaSerializable] attribute with its own manifest; generic fields are extracted from the constructed symbol, so Roslyn's member substitution delivers T-typed properties already concretely typed. One rule: everything on the wire is [AkkaSerializable] — a closed construction just has no declaration site of its own, so its marking lives on the serializer class.

Part 2 — [AkkaUnion] closed, manifest-discriminated unions. A field whose static type is an interface/abstract base with a declared closed member set encodes structurally inline as { 1: manifest, 2: member fields } — the typed alternative to [AkkaEnvelopePayload] for payload sets known at compile time. The union member set is declared once on the union base type (field-level [AkkaUnion] remains as a per-field override); the existing serializer-owned manifest is the discriminator, so no new identity scheme is introduced. Write dispatch is exact-runtime-type: an undeclared subtype fails serialization instead of silently truncating to its base (STJ FailSerialization semantics).

The combined scenario — a generic wrapper with a polymorphic payload, fully typed with no object boundary:

[AkkaUnion(typeof(OrderPlaced), typeof(OrderCancelled), typeof(OrderNote))]
public interface IOrderEvent { }

[AkkaSerializable]
public sealed record EventWrapper<T>(
    [property: AkkaField(1)] string Id,
    [property: AkkaField(2)] T Body) : IMyProtocol;

[AkkaSerializer<IMyProtocol>("my-protocol", 1234)]
[AkkaSerializable<EventWrapper<IOrderEvent>>(Manifest = "event-wrap-v1")]
public sealed partial class MyProtocolSerializer : AkkaSerializer { }

The protocol marker moved off the base type and into [AkkaSerializer<TProtocol>] — it was always pure generator input (the runtime never used TProtocol), so the base class is now non-generic. It is also renamed MessagePackSerializer → AkkaSerializer: the old name collided with MessagePack.MessagePackSerializer (CS0104) once de-generified, and leaked the wire format into the type users inherit; the new name echoes the [AkkaSerializer<TProtocol>] attribute and keeps MessagePack an implementation detail.

Changes

  • Consolidated attribute surface (5 names): [AkkaSerializer<TProtocol>], [AkkaSerializable] (+ generic form for closed constructions), [AkkaField], [AkkaUnion], plus the [AkkaEnvelopePayload]/[AkkaSerializerFormatter] escape hatches; base class de-generified and renamed to AkkaSerializer
  • Diagnostics and generator internals use "closed generic registration" terminology throughout (no "instantiation" vocabulary survives the attribute it named)
  • AkkaUnionAttribute: type-level declaration on the union base (stated once, inherited by every field of that type) + field-level override
  • FieldKind.Union with generated dispatch helpers deduplicated by (static type, member set) signature; exact SizeHint for all member kinds
  • Arity-aware field-type resolution and STJ-style folded generated member names (WriteWrapperOrderRequest) with AKKASG024 collision detection
  • New diagnostics: AKKASG015–019 (union validation), AKKASG020–022 (closed-generic registrations / generic definitions), AKKASG023 (unregistered closed generic field), AKKASG024 (generated-name collisions), AKKASG025 (advisory: unsealed union member)
  • Unbound-generic registration is unrepresentable by construction ([AkkaSerializable<Wrapper<>>] is not legal C#) — compiler error instead of analyzer diagnostic
  • Migrates the Artery control-message serializer to the new surface (name/id/binding unchanged — wire compatible)
  • Fixes a latent codegen bug: properties whose camel-cased name is a C# keyword (e.g. Event) generated uncompilable locals; now @-escaped

Testing

134/134 (27 new): round-trips for class/struct/nested-only union members, nullable unions, type-level inheritance + field-level narrowing override, wire-format byte assertions, undeclared type/subtype rejection, unknown-manifest and payload-ordering failures, forward-compat key skipping, closed-construction round-trips + manifest dispatch + nesting, the combined scenario, and 8 diagnostics specs. API approval tests pass; Akka.Remote builds clean.

Out of scope (follow-ups tracked in #8384)

  • Cross-assembly union members / instantiation payloads (extraction is compilation-local today)
  • Wire-format docs page and BREAKING_CHANGES entry if/when this graduates from PoC

Closes nothing — PoC for discussion on #8384.


Update (2026-07-16): illegal-states-unrepresentable hardening pass

Three follow-up commits from an adversarial audit of the surface (plus a CI fix — e0c71bfbb repairs the repo-wide NU1605 from the Azure image SDK roll; unrelated to this PR's code):

  • c507f0f73 — name-bound hybrid construction on deserialize. Read-side reconstruction previously bound ctor args positionally in [AkkaField]-index order with no validation — same-typed properties whose index order differed from ctor order silently swapped values on round-trip. Now emits named arguments (ordinal, then unique case-insensitive matching) plus object-initializer assignments for settable/init leftovers, so init-only POCO shapes serialize too. New: AKKASG026 (no matching ctor), AKKASG027 (defaulted ctor param covered by no field — silent reset), AKKASG028 ([AkkaField] on static/inaccessible property). Generator-owned read locals are __-prefixed so property names like FieldCount can't collide.
  • 1b2ea0116 — serializer-level validation. AKKASG029 (concrete protocol implementor missing [AkkaSerializable] — previously vanished silently from dispatch), AKKASG031 (same protocol bound by two serializers), AKKASG032 (serializer class must be partial/non-generic/derive from AkkaSerializer), AKKASG033 (protocol type must be an interface), AKKASG034 (closed-generic registration that neither implements the protocol nor is reachable from any message — dead registration). No AKKASG030 needed: the compiler already rejects duplicate [AkkaSerializer<T>] attributes via CS0579 even with different type args (pinned by test).
  • 5ea975e30 — attribute reshapes. [AkkaSerializer<TProtocol>(string name, int serializerId)] — identity is now required and always explicit (no auto-ids: sourcegen ordering is nondeterministic and config-defined collisions are invisible to the generator); AKKASG001/002 demote to constant-validity checks. [AkkaUnion(Type first, params Type[] rest)] — empty union sets no longer compile. [AkkaSerializerFormatter<TTarget, TFormatter>] where TFormatter : IAkkaMessagePackFormatter<TTarget> — interface conformance is compiler-enforced; the null-target and interface-mismatch diagnostics are deleted as unrepresentable. Artery serializer and benchmarks migrated with byte-identical name/id bindings (wire compatible).

Diagnostics now span AKKASG001–034 (022 has structural gaps closed; 030 intentionally absent). 161/161 tests. Wire format unchanged throughout — byte-assertion specs untouched. Known follow-ups: incremental-pipeline discipline pass (cached models retain symbols; AKKASG029 couples the terminal stage to CompilationProvider), benchmark coverage for union/closed-generic dispatch paths.

Aaronontheweb and others added 12 commits July 15, 2026 15:11
…discriminated)

Part 2 of the design in akkadotnet#8384: a union-typed field is a closed, explicitly
enumerated set of [AkkaSerializable] member types, encoded structurally inline
as { 1: manifest, 2: member fields } and discriminated by each member's
serializer-owned manifest -- the typed alternative to [AkkaEnvelopePayload]
for payload sets known at compile time.

- AkkaUnionAttribute(params Type[] memberTypes) on [AkkaField] properties
- FieldKind.Union + per-field generated Write/Read/SizeOf dispatch helpers
- Exact-runtime-type write dispatch (undeclared subtypes fail, STJ
  FailSerialization semantics); unknown manifests throw on read; unknown
  union-frame keys skip for forward compat
- Diagnostics AKKASG015-019 (member not serializable / missing manifest /
  manifest collision / not assignable / invalid member set)
- Fix latent codegen bug: locals for properties named after C# keywords
  (e.g. 'Event') are now @-escaped in generated code
…eric messages

Part 1 of the design in akkadotnet#8384, modeled on System.Text.Json source gen closed
construction registration: a generic [AkkaSerializable] type is never
serialized as an open definition -- each closed construction is registered on
the [AkkaSerializer] module with its own manifest, and its generic fields are
extracted from the constructed symbol (Roslyn substitutes member types, so a
"T Payload" property arrives concretely typed with no type-parameter
special-casing).

- AkkaSerializableInstantiationAttribute(Type closedType) { Manifest }
- Arity-aware member naming (Wrapper<Foo> folds type arguments into generated
  identifiers) with AKKASG024 collision detection instead of silent dropping
- Arity-aware Object field mapping: a Wrapper<Foo>-typed field resolves to its
  registered instantiation (or fails AKKASG023) instead of colliding on the
  arity-less name
- Generic definitions become non-serialized placeholders; AKKASG022 fires
  when one implements a serializer protocol with no registered constructions
- AKKASG020/021 validate instantiation targets and duplicates
- Combined scenario green: EventWrapper<IOrderEvent> with a union payload
  round-trips fully typed, inline, exact-sized
DX fix from the PoC audit: per-field union declarations were a drift hazard
(every field of the same interface repeated the member list; lists drifting
apart silently split wire compat). The union member set is now declared ONCE
on the union base type (interface or abstract class), mirroring
[JsonDerivedType] and the case list of the proposed C# language unions --
every field of that type inherits it. A field-level [AkkaUnion] remains as a
per-field override (e.g. to narrow the accepted set for one schema).

Type-level lookup runs against the SUBSTITUTED static type inside registered
closed constructions, so a generic wrapper needs no union declaration at all:
EventWrapper<T> instantiated with T := IOrderEvent inherits the union declared
on IOrderEvent.
… helper dedupe)

Three DX-audit follow-ups from akkadotnet#8384/akkadotnet#8385:

- Generated member names now fold STJ-style (WriteWrapperOrderRequest,
  ReadUnionEnvelope) instead of namespace-qualified sanitized FQNs with
  _Of_/_End_ markers -- these names appear in stack traces. Collisions from
  flattening are detected by AKKASG024 and fail compilation, the same trade
  System.Text.Json makes with DuplicateTypeName.
- New advisory AKKASG025 (Info): a non-sealed union member works, but an
  undeclared subtype of it fails at write time under exact-runtime-type
  dispatch -- surfaced without failing the build.
- Union dispatch helpers are deduplicated by (static type, member set)
  signature: all fields sharing a type-level union declaration now share one
  Write/Read/SizeOf helper trio (Union_IOrderEvent); field-level overrides
  with distinct member sets get numbered variants (Union_IOrderEvent_2).
  Helper exception text references the union type rather than a field name.
Design-review feedback on akkadotnet#8385: too many attribute names, protocol interface
hanging off the base type. Two consolidations, no behavior change:

- [AkkaSerializer<TProtocol>(Name, SerializerId)]: the protocol marker moves
  into the serializer attribute. It was always pure generator input (TProtocol
  appeared exactly once in the runtime -- the class declaration), so the base
  class is now non-generic MessagePackSerializer.
- [AkkaSerializable<T>] replaces [AkkaSerializableInstantiation]: closed
  generic constructions are registered with a generic form of the SAME
  attribute that marks every other serializable type. One rule: everything on
  the wire is [AkkaSerializable]; a closed construction has no declaration
  site of its own, so its marking lives on the serializer class.

Bonus pit-of-success: the AKKASG020 unbound-generic case is now
unrepresentable ([AkkaSerializable<Wrapper<>>] is not legal C#), so that
failure mode moved from analyzer diagnostic to compiler error; its test is
deleted.

Also migrates the Artery control-message serializer (name/id/binding
unchanged, wire compatible), benchmarks, and stale crefs. Known trade-off:
non-generic MessagePackSerializer now collides with MessagePack's own
MessagePackSerializer under dual top-level usings (CS0104) -- flagged for
review, worked around with full qualification where it bit.

134/134 tests, Akka.Remote clean, API approvals pass.
… AkkaSerializer

Design review on akkadotnet#8385: de-generifying the base class made it collide with
MessagePack.MessagePackSerializer (CS0104) under dual top-level usings, and
the old name leaked the wire format into the type users inherit. AkkaSerializer
collides with nothing, echoes the [AkkaSerializer<TProtocol>] attribute, and
keeps MessagePack an implementation detail:

  [AkkaSerializer<IMyProtocol>(Name = "my-protocol", SerializerId = 1234)]
  public sealed partial class MyProtocolSerializer : AkkaSerializer { }

File renamed to AkkaSerializer.cs; all global::-qualified CS0104 workarounds
simplified back to the plain name. MessagePack LIBRARY references and
spec/benchmark class names intentionally untouched.
…stics and generator internals

The [AkkaSerializable<T>] consolidation killed the attribute that gave
"instantiation" its meaning here; this finishes the job so user-facing and
internal language match the mechanism's real name:

- AKKASG020 retitled "Closed generic registration is invalid"
  (InvalidClosedGenericRegistration)
- AKKASG021 retitled "Duplicate closed generic registration"
  (DuplicateClosedGenericRegistration)
- AKKASG022 retitled "Generic serializable type requires closed generic
  registrations" (GenericSerializableRequiresRegistration)
- Generator internals renamed: ExtractClosedGenericRegistrations,
  ValidateClosedGenericRegistrations, ClosedGenericRegistrationInfo,
  SerializerInfo.ClosedGenericRegistrations
- Two diagnostics-spec test names/DisplayNames updated to match

Plain-English generics usage ("at instantiation time, T := Foo") retained
where it describes type substitution rather than the registration mechanism.

Verified: full Akka.slnx build 0 errors/0 warnings; V2 suite 134/134; Artery
integration specs (ArteryControlMessageSerializerSpec,
SystemMessageAckerStageSpec, ArteryHandshakeSpec) 51/51.
Azure hosted images rolled a newer .NET SDK whose implicit FSharp.Core
(10.1.302) conflicts with the repo-pinned 10.1.301, failing restore with
NU1605 on every branch. Pin FSharp.Core explicitly in Akka.FSharp so the
resolved version no longer depends on the build agent's SDK.
…ASG026-028

Read-side reconstruction previously bound constructor arguments positionally
in [AkkaField]-index order with no validation: same-typed properties whose
index order differed from ctor-parameter order silently swapped values on
round-trip, and defaulted ctor params not covered by a field silently reset.

- Emit NAMED constructor arguments (ordinal, then unique case-insensitive
  param-name matching with implicit-conversion checks) + object-initializer
  assignments for settable/init-only leftovers (STJ-style hybrid), so
  init-only POCO and mixed shapes now serialize
- AKKASG026 (Error): no eligible constructor / leftover property unsettable
- AKKASG027 (Warning): chosen ctor has defaulted param no [AkkaField] covers
- AKKASG028 (Error): [AkkaField] on static or getter-inaccessible property
- Prefix generator-owned read locals (__fieldCount/__entryIndex/__fieldId,
  __has guards) so adversarial property names (FieldCount, Foo+HasFoo) can
  no longer collide (CS0128/CS0136)

146/146 V2 tests (12 new); wire format unchanged (byte assertions intact);
Akka.Remote builds clean.
- AKKASG029 (Error): concrete type implements the serializer's protocol
  interface but lacks [AkkaSerializable] -- previously vanished silently
  from the generated switches and only failed at runtime when first sent.
  Exempts interfaces/abstract classes (concrete subtypes are checked
  individually) and [AkkaSerializable]-marked generic definitions (governed
  by AKKASG022)
- AKKASG031 (Error): same protocol interface bound by multiple serializer
  classes (was silent last-wins at runtime registration)
- AKKASG032 (Error): serializer class must be partial, non-generic, and
  derive from AkkaSerializer (was a wall of raw CS errors in generated code)
- AKKASG033 (Error): protocol type must be an interface (a class protocol
  generated a serializer with empty dispatch switches)
- AKKASG034 (Error): closed-generic registration that neither implements
  the protocol nor is reachable from any top-level message (dead
  registration; reachable nested registrations remain legitimate)
- No AKKASG030: compiler already rejects duplicate [AkkaSerializer<T>]
  attributes via CS0579 even with different type args -- pinned by test

Wires CompilationProvider into the terminal stage for the AKKASG029 scan;
cached models already retained ISymbols (pre-existing), so a proper
incremental-discipline pass is tracked as follow-up work.

162/162 V2 tests (16 new); Akka.Remote builds clean with no false
positives on the Artery serializer.
- [AkkaSerializer<TProtocol>] now requires (string name, int serializerId)
  ctor args; Name/SerializerId are get-only. Serializer identity is always
  explicit by design: source-generator ordering is nondeterministic and
  config-defined id collisions are invisible to the generator, so there is
  no safe default. AKKASG001/002 demoted to constant-validity checks
  (null/empty/whitespace name, non-positive id)
- [AkkaUnion] ctor is now (Type first, params Type[] rest): an empty union
  set no longer compiles; the 'at least one member' half of AKKASG019 is
  deleted, the duplicate-member half remains (now with a pinning test).
  Union extraction rewritten to concatenate both ctor args
- [AkkaSerializerFormatter<TTarget, TFormatter>] with TFormatter :
  IAkkaMessagePackFormatter<TTarget>: interface conformance is now
  compiler-enforced; AKKASG008's interface half and AKKASG011's null-target
  half deleted as unrepresentable. Kept: abstract-formatter check (no new()
  constraint -- ExtendedActorSystem-only ctors are legitimate), AKKASG010,
  AKKASG011 array/closed-generic halves
- Migrated all usages: Artery control-message serializer (name/id/binding
  byte-identical -- wire compatible), benchmarks, 7 test files

161/161 V2 tests; Akka.Remote, Akka.Benchmarks, RemotePingPong build clean;
API approval suite unaffected (V2 not covered) -- 18/18 pass.
…nd diagnostic text

- Hand-written runtime and test source now uses plain names + usings
  (every reference resolved unambiguously via enclosing-namespace lookup;
  no aliases needed) -- zero global:: remains outside the generator
- Diagnostic messages no longer print global::-prefixed type names:
  ToDisplayName strips the prefix at Diagnostic.Create sites only, leaving
  dictionary keys, equality comparisons, and emitted-code type names
  untouched (29 AKKASG messages affected, no spec assertions broke)
- Emitted-code string literals deliberately KEEP global:: -- generated
  source compiles inside arbitrary user namespaces where MessagePack/
  System/Akka can be shadowed by user types

161/161 V2 tests; wire format unchanged; Akka.Remote builds clean.
@Aaronontheweb Aaronontheweb added serialization akka.net v1.6 Akka.NET v1.6-related issues labels Jul 16, 2026
@Aaronontheweb
Aaronontheweb marked this pull request as ready for review July 16, 2026 11:01
@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) July 16, 2026 11:01
@Aaronontheweb
Aaronontheweb disabled auto-merge July 16, 2026 12:46
@Aaronontheweb
Aaronontheweb merged commit 3864f3b into akkadotnet:dev Jul 16, 2026
11 checks passed
@Aaronontheweb
Aaronontheweb deleted the feature/serialization-v2-unions-closed-generics branch July 16, 2026 12:53
Aaronontheweb added a commit that referenced this pull request Jul 16, 2026
…ric benchmarks, OpenSpec sync (follow-up to #8385) (#8392)

* OpenSpec: sync messagepack-sourcegen-validation with implemented reality

- tasks.md: check off 4.5/4.8/5.10/5.11 with AKKASG/test citations; annotate
  5.7 (only T[], List<T>, IReadOnlyList<T>, Dictionary<K,V> implemented --
  immutable collections remain future work); add sections 10-12 for unions,
  closed generic registrations, and the attribute-surface/illegal-states pass
- design.md: fix stale MessagePackSerializer naming and dead attribute
  syntax; rewrite Decision 11's AKKASG008/011 description for the generic
  formatter attribute; add Decisions 13 (unions + closed generics),
  14 (always-explicit serializer identity), 15 (illegal-states pass)
- spec.md: add construction-validation scenarios and requirement sections
  for unions and closed generic registrations
- proposal.md: name the concrete attribute set

serializer-v2 change intentionally untouched (different owner scope);
its sections 1-3 are done-in-code-but-unchecked, sections 4-5/7 genuinely
incomplete -- reported separately.

* Benchmarks: cover union dispatch and closed-generic registration paths

Neither path existed when GeneratedMessagePackSerializerBenchmarks was
written, so they had no baseline at all:

- Union field round-trips: representative (first-declared) member and
  worst-case (last-declared) member. Write dispatch is a sequential
  exact-runtime-type check chain in [AkkaUnion] declaration order, so the
  last member pays N-1 failed checks; read dispatch is a manifest switch
  (jump table) with no ordering effect -- documented at point of use
- Closed-generic registration round-trips: plain Wrapper<OrderDetailPayload>
  and the flagship combined shape Wrapper<IOrderEventBenchmark> (generic
  parameter is itself the union interface)

Payload shapes match SubmitOrder field counts/types so numbers are
comparable across the file. Serializer ids 120004/120005 (unused).
Builds clean; all 8 execute under --job dry.

* Serialization.V2: incremental-pipeline discipline + AKKASG035-037 advisories

The generator's cached models retained live INamedTypeSymbols
(SerializerInfo.ProtocolType, MessageInfo.Protocols) and several models
had reference/broken equality (TypeMapping's default struct equality
compared its ImmutableArray by underlying-array reference), so incremental
caching was silently defeated; batch two had also coupled the single
terminal output stage to CompilationProvider for AKKASG029.

- All ten pipeline model types are now symbol-free and IEquatable with
  structural collection equality; protocol matching is ordinal string
  comparison against names captured at extraction
- Pipeline split: emission consumes only the collected models; the
  AKKASG029 whole-compilation scan moved to a separate diagnostics-only
  output combining CompilationProvider. Emission is no longer gated on
  coverage errors (the Error still fails the build); source is now emitted
  alongside AKKASG029, documented by spec
- New GeneratorIncrementalCachingSpec drives CSharpGeneratorDriver with
  trackIncrementalGeneratorSteps and asserts every named step reports
  Cached/Unchanged across an unrelated edit -- reintroducing a retained
  symbol or non-equatable model now fails a test
- CancellationToken propagated through extraction and the coverage walk
- AKKASG035 (Info): field-level [AkkaUnion] ignored under
  [AkkaEnvelopePayload] (type-level declarations exempt)
- AKKASG036 (Warning): abstract union member has dead dispatch (fires
  instead of AKKASG025, no double-fire)
- AKKASG037 (Info): Manifest on a generic [AkkaSerializable] definition
  is ignored (closed constructions supply their own)

169/169 V2 tests (8 new); Akka.Remote and both benchmark projects build
clean with no advisory fires.

* De-flake JsonPersistentTestRunStoreSpec: async ExpectMsgAsync with explicit timeout

Both facts (in both TestAdapter test projects) used synchronous
ExpectMsg<TestRunTree>() with the default 3s single-message timeout, after
telling the coordinator ~320+ queued messages -- routinely exceeded on
loaded CI agents (failed PR #8392's Linux unit-test run in a project the
PR does not touch). Now async Task + await ExpectMsgAsync with a 15s
timeout, per the repo's async-TestKit guideline.
Aaronontheweb added a commit to Aaronontheweb/akka.net that referenced this pull request Jul 18, 2026
…net#8385+)

The worktree's original base (84524b3) predated the Akka.Serialization.V2
surface reshape that landed via PR akkadotnet#8385 and successors. After the
coordinator merged current dev into this branch, the four new integration
specs from the prior commit no longer compiled against the new surface:

- Attribute/base-class rename: `[AkkaSerializer(Name = ..., SerializerId = ...)]`
  + `MessagePackSerializer<TProtocol>` became the generic
  `[AkkaSerializer<TProtocol>("name", id)]` (positional, required args) +
  non-generic `AkkaSerializer` base class. `CreateRegistration()` partial
  method declarations were unaffected. Fixed in all four new files
  (ClassicRemotingSpec, PersistenceIntegrationSpec, ReliableDeliveryIntegrationSpec,
  GeneratedV2WrapperSpec).

- Type collision: dev's GeneratedUnionSpec.cs (new since the base commit)
  declares a top-level `Akka.Serialization.V2.Tests.OrderPlaced` record with
  manifest "order-placed-v1". PersistenceIntegrationSpec.cs declared its own
  `OrderPlaced`/`OrderState`, causing CS0101/CS0579/CS8863 and a knock-on
  AKKASG012 + CS0534 on the unrelated UnionTestSerializer (the duplicate
  symbol confused the generator's per-serializer manifest scan). Renamed to
  `PersistedOrderPlaced`/`PersistedOrderState` with distinct manifests
  ("persisted-order-placed-v1"/"persisted-order-state-v1"); confirmed no
  other identifier collisions exist against the merged tree (checked every
  type declared across all four new spec files).

Verification against the merged tree:
- dotnet test src/core/Akka.Serialization.V2.Tests -c Release: 260/260 passing
  (was 106 pre-merge baseline + our 4; merged dev tip's own baseline is 256,
  confirming the orchestrator's original "256 tests" figure referred to
  current dev, not the stale base this worktree started from).
- dotnet test src/core/Akka.Cluster.Tests --filter ReliableDeliveryIntegrationSpec: 2/2 passing.
- dotnet test src/contrib/cluster/Akka.DistributedData.Tests --filter GeneratedV2WrapperSpec: 2/2 passing.
- dotnet format --verify-no-changes: clean on all three touched projects.
- dotnet build -warnaserror: clean on Akka.Cluster.Tests and
  Akka.DistributedData.Tests. Akka.Serialization.V2.Tests only fails
  -warnaserror when built as a standalone .csproj outside solution context,
  because the Verify package's build props can't auto-discover SolutionDir/
  SolutionName from a project 3 directories below the repo root (its search
  only walks 2 parents) and downgrades that to a real warning under
  -warnaserror; this is pre-existing since dev's wire-format-snapshot Verify
  tests (86976dd), reproducible on an unmodified file in that project, and
  resolves cleanly once SolutionDir/SolutionName are supplied (confirmed with
  explicit /p:SolutionDir/SolutionName, matching how CI builds via the
  solution) -- not something introduced by or fixable in these test files.

All new specs re-run twice post-fix with no flakiness. No product code
(generator or V2 runtime) touched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

akka.net v1.6 Akka.NET v1.6-related issues serialization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant