Skip to content

Expand one generic registration over every protocol message with ManifestPrefix (F2) - #8536

Merged
Aaronontheweb merged 1 commit into
feature/serialization-v2-metadata-schemasfrom
feature/serialization-v2-expansion-adoption
Sep 11, 2026
Merged

Aaronontheweb merged 1 commit into
feature/serialization-v2-metadata-schemasfrom
feature/serialization-v2-expansion-adoption

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Stack, bottom to top: S1 #8525 -> S2 #8526 -> S3 #8527 -> object elements #8528 -> S4 #8530 -> S5 #8532 -> S6 #8533 -> F1 #8534 -> F2 #8536 -> F3 #8537 -> S7 #8538. Each PR is one commit on top of the one below it. This PR is F2.

What changes

One line registers a generic envelope for every message in a protocol, and the generator emits one concrete construction per member, all static:

[AkkaSerializer<ICommsMessage>("comms", 8677751)]
[AkkaSerializable<Envelope<ICommsMessage>>(ManifestPrefix = "env")]
public sealed partial class CommsSerializer : AkkaSerializer { }

This is the customer case from #8384. The rules are Decisions 17 and 18 in the design record and Decision C on the decision page.

  • ManifestPrefix on the registration attribute. The one public API change: a new init-only property on AkkaSerializableAttribute<TMessage>, extend-only. Manifest alone still registers exactly the named construction. Both together register the literal construction under Manifest and the expansion under the prefix. Neither is the existing manifest-required error.
  • How a manifest is derived. Prefix, a slash, then each type argument's manifest in order, over every combination of the arguments. Envelope<AcceptCassette> under prefix env is env/dmac. Pair<ICommsMessage, Envelope<AcceptCassette>> under prefix pair, with the inner construction registered as env-dmac-v2, is pair/dmac/env-dmac-v2. An explicit registration for a construction wins over the derived name. AKKASG012 checks derived manifests for collisions like any other.
  • A registration adopts its type. [AkkaSerializable<T>] on a serializer now adopts any serializable type: a concrete type from this or a referenced assembly, or a construction. AKKASG020 no longer rejects non-generic targets. Every registration is a top-level message with a manifest, so a construction registered only for nested-field use now needs one too. The design text already said so. The golden corpus had one such registration; it gained a manifest, which is why three golden files and the resolved-serializer snapshot changed.
  • A field typed as the protocol interface is a union over the serializer's closed set, with no [AkkaUnion] list.
  • AKKASG034 retired. Its id stays reserved like 030 and 035. Baseline case 4, which pinned that AKKASG034 suppressed a whole serializer, now pins success: the customer envelope is adopted, emitted, and dispatched by manifest.
  • Three new diagnostics. AKKASG040, error: ManifestPrefix on a target with no closed set to expand, at the registration attribute. AKKASG041, error: a message owned by two serializers' closed sets, reported at both. AKKASG042, info: how many constructions an expansion produced. The design record and the decision page both say info with no threshold.
  • Decision 17 needed no code. The generated union dispatch already throws on a type or manifest it has no case for, with no fallback. The design addendum records that.
  • Docs. New guide sections on adoption, ManifestPrefix with the worked examples, the manifest rule, the implicit union, and the one-owner rule. The diagnostics table gains three ids and drops one.

Known limit, for F3

The closed set behind an expansion is found by walking the compilation's types inside the serializer's per-keystroke step, once per prefix registration per edit. That is the shape S5 moved out of the coverage check. F3 moves that discovery into the facts stage. The benchmark corpus has no prefix registrations, so this cost does not show below.

Cost

Base is the F1 tip. Allocations measured twice, identical both times; means within noise. A first cut allocated 5 percent more on the rename row from an eager per-message builder in resolve, fixed with a cheap pre-check.

Row Base (F1) This PR
Fresh driver, full corpus 22.3 ms, 18.93 MB 22.9 ms, 19.00 MB
Warm driver, one field renamed 16.1 ms, 9.51 MB 16.9 ms, 9.57 MB
Warm driver, comment edit 8.8 ms, 3.55 MB 9.4 ms, 3.55 MB
Warm driver, unrelated file edited 7.4 ms, 3.52 MB 7.7 ms, 3.53 MB

How it was checked

350 tests pass (334 plus 16: expansion end to end, expansion diagnostics, and two golden cases). Akka.API.Tests: 18 pass; V2 has no approval file yet, so nothing to regenerate. V2, the generator, and Akka.Remote build with warnings as errors. cspell passes on the guide. BREAKING_CHANGES_V1.6.md has no V2 entries, so none was added.

@Aaronontheweb
Aaronontheweb added this pull request to stack #8531 September 9, 2026 13:28
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-expansion-adoption branch from ac3376f to f2f4e89 Compare September 9, 2026 13:32
@Aaronontheweb Aaronontheweb changed the title Serialization.V2: closed-generic expansion and adoption; ManifestPrefix; AKKASG034 retired (Decisions 17 and 18, F2) Expand one generic registration over every protocol message with ManifestPrefix (F2) Sep 9, 2026
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-expansion-adoption branch from f2f4e89 to 9cf2687 Compare September 9, 2026 17:15
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-expansion-adoption branch from 9cf2687 to 185decc Compare September 9, 2026 18:08
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-expansion-adoption branch 2 times, most recently from bfbdef8 to f29d198 Compare September 10, 2026 23:44
…ix; AKKASG034 retired (Decisions 17 and 18)

Implements Decision 18 (closed-set expansion and adoption on the serializer) from
openspec/changes/messagepack-sourcegen-validation/design.md: the ManifestPrefix
property and its manifest-derivation formula, adoption of any AkkaSerializable
type (generic or not) via a registration, the protocol-interface field as an
implicit union, the one-owner rule (AKKASG041), and the construction-count info
diagnostic (AKKASG042). AKKASG034 is retired; AKKASG020 stops rejecting a
non-generic registration target. Decision 17 needed no code change (the
generated union dispatch already throws on an unmatched runtime type or an
unrecognized manifest).

Adds round-trip, diagnostic, and golden-output test coverage; updates the user
guide, the design record, and the task list.
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-expansion-adoption branch from f29d198 to ccd44ba Compare September 11, 2026 15:05
@Aaronontheweb
Aaronontheweb merged commit d5bcef0 into dev Sep 11, 2026
17 checks passed
@Aaronontheweb
Aaronontheweb deleted the feature/serialization-v2-expansion-adoption branch September 11, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant