Add compile-time RPC codec scope routing - #320
SunSi12138 wants to merge 290 commits into
Conversation
SunSi12138
left a comment
There was a problem hiding this comment.
I found two correctness gaps worth addressing before merge: the Native builtin override is currently context-global rather than Contract-owner-scoped, and route selection still cannot fail at compile time when the selected adapter cannot create a codec for the closed payload type.
SunSi12138
left a comment
There was a problem hiding this comment.
Follow-up after rechecking the generated paths and #313: only the context-wide Native builtin leakage on response/stream paths remains as a correctness concern here. The closed-T adapter capability comment is withdrawn and resolved; SharpPack-specific compile-time capability/formatter validation belongs to #313, with #311's acceptance wording needing alignment to that split.
SunSi12138
left a comment
There was a problem hiding this comment.
Found one additional owner-scoping correctness gap on the final head: owner scoping needs to propagate through native generated codec dependency construction, not only the outer artifact binding.
Handoff / 当前工作状态(2026-08-26)这是给下一段对话/接手者的 canonical continuation checkpoint。请以这里记录的目标和当前 exact head 为准,不要从历史 detour 推断最终设计。 1. Repo / PR / exact head
2. Canonical architecture targetThe target is assembly-owned RPC codec routing: Invariants:
3. Open API decision:
|
|
Status update: this PR is now superseded by #386. #320 is being retained as the research/review history for the codec-routing work, including the API-5 per-Contract experiments and the review iterations that led to the final ownership model. It should not be merged. The final implementation moved to the clean, single-parent port in #386, which uses the assembly-owned codec-routing model and intentionally does not carry forward the rejected transitional/API-5 machinery from this branch. As of #386 head Closing #320 as superseded; keeping it available as implementation and review history. |
* feat: port assembly-owned codec routing onto the current dev line Selectively port the final assembly-owned Codec routing design from PR #320 (fix/311-codec-scope-routing) onto dev. Per-Contract/API-5 transitional machinery (ContractCodecSets, per-Contract publication, same-assembly per-Contract policy splits) is deliberately not carried over; the PR body's final ownership contract is the spec. - Public API: assembly-only RpcCodecRoute/RpcCodecScope; singular ContractCodecs/ContractDependencies on the generated assembly manifest; IRpcContractCodecProviderResolver; RpcGeneratedCodecFactoryKind; codec-aware IRpcClientStreamSink overload. - Generator: assembly-owned final Codec graph (explicit > route > default), frozen graph published in the assembly manifest, proxy/stub construction-time binding, and implicit final-selection identities (Native/UnsafeBlit) materialized into format-2 compatibility manifests so UnsafeBlit <-> Adapter transitions are wire breaks. - Runtime: RpcManifestCodecProvider as the per-assembly owner provider, RpcGeneratedCodecResolver, assembly-level manifest structure validation, and the request-level stream drain barrier. - Client/Server: proxy/stub registration binds the owning assembly provider; multi-cluster dependency closure includes ContractDependencies; builder UseCodec<T> removed; dynamic replacement validates assembly-owned dependency closure. - Tests: assembly-owned routing/compatibility/ownership suites ported; per-instance codec-independence and same-assembly split tests removed; fixture projects are real generated contract assemblies. Local validation (net10.0, Release): Unit 1223/1223, Generator 171/171, LoadTest 60/60, Integration 344/344. * test: preload generated fixture assemblies to stabilize catalog snapshots The generated contract fixtures register their manifests into the global catalogs from a module initializer. Lazy mid-suite loads raced the weak-catalog tests that snapshot and restore catalog counts, so force the fixture assemblies to load before any test executes. * test: harden generated bridge async waits on loaded CI runners * ci: pin browser desktop verification to producer SDK * fix: preserve assembly ownership for custom RPC codecs Address the review findings on #386: - classify adapter-free non-native generated factories as direct/custom construction - keep Contract-only [RpcCodec] bindings out of the global graph while freezing them into ContractCodecs - publish ContractDependencies as the RPC-only dependency delta - add generator regressions covering custom ownership, standalone behavior, and dependency separation * test: place generated factory kind regression in runtime suite Keep Generator.Tests independent from the real Abstractions project. The generator-only ownership/dependency regressions remain there, while the real IRpcGeneratedCodecFactory default-kind behavior is covered by UnitTests. * fix(client): validate RPC-only manifest dependencies * test(client): cover RPC-only dynamic dependencies * style: restore final newline in client registration source * test(client): import client builder namespace * test(generator): distinguish CLR refs from module dependencies * fix: bump generated descriptor ABI identity * fix: emit current generated ABI identity * fix: protect contract dependencies during client unregister * fix: keep custom codec helpers out of module dependencies * test: keep custom codec helpers outside module closure * test: cover contract dependency drain and stale ABI rejection * chore: keep ABI fix diff focused * test: add LINQ import for reflection helper * test: isolate stale ABI compatibility branch * test(generator): align ABI fixtures with current descriptor * fix(generator): align route classification with custom codecs * fix(generator): tighten codec policy ownership and dependencies * fix(generator): derive module dependencies from generated graph * fix(generator): let explicit builtin policy outrank native routes * test(generator): cover second codec policy review regressions * test(generator): retire ambient codec policy assumptions * fix(generator): align final codec ownership boundaries * test(generator): cover final codec ownership review cases * fix(generator): keep owner policy out of contract defaults * fix(generator): reuse existing contract ownership probe * fix(generator): keep contract custom fallbacks out of global codecs * test(generator): stop owning manifestless foreign contracts * fix: derive RPC behavior from final graphs * test(generator): lock final codec for top-level requests * fix(generator): canonicalize tuple codec identities * fix(generator): close final codec identity and compatibility gaps * test: isolate global codec catalog regression * style: restore final newline * fix(runtime): fail closed for implicit-only generated owners * fix(generator): close late review codec identity gaps * test(generator): cover fourth review codec identity regressions * test(runtime): cover implicit-only late owner fail-closed * test(runtime): avoid unassigned-field warning * test(generator): match canonical review types robustly * test(generator): resolve nullable codec by manifest type identity * fix(generator): close final codec review gaps * test(generator): cover final codec review gaps * refactor(codec): remove Native route scope * refactor(codec): define framework wire primitive boundary * refactor(codec): route only configurable payloads * refactor(codec): freeze framework primitive policy * test(codec): make enum wire policy fixed * test(codec): route configurable composites not primitives * test(codec): align route suite with configurable scopes * test(codec): fix compatibility boundary for framework primitives * test(codec): keep framework enums outside routes * test(codec): use configurable payloads for owner binding * test(codec): reject framework primitive rebinding * test(codec): remove primitive custom policy surface * test(codec): keep primitive request framing fixed * test(codec): reject primitive element rebinding * test(codec): keep enum compatibility framework-owned * test(codec): lock framework wire primitive boundary * docs(codec): document fixed framework wire primitives * refactor(codec): freeze nullable framework primitives * test(codec): freeze nullable primitive wrappers * test(generator): use direct-codec fixture for tuple alias * refactor(codec): make RpcCodecAdapter adapter-only * test(generator): remove direct codec alias coverage * refactor(codec): replace Direct factory kind with Custom * refactor(generator): remove Direct codec kind * refactor(generator): remove native route shim and direct adapter model * refactor(generator): simplify codec ownership and UnsafeBlit identity * refactor(generator): make adapter analysis adapter-only * refactor(runtime): validate custom codec factory kind * refactor(runtime): replace direct factory semantics with custom * docs(codec): document single custom codec binding API * refactor(generator): remove direct codec emitter * refactor(generator): remove direct manifest kind * test(generator): keep UnsafeBlit identity wire-only * refactor(generator): keep codec routing separate from compatibility identity * test(codec): drop compatibility-only routing regressions * docs(codec): defer stable identity model to #396 * fix(generator): preserve legacy codec manifest ownership * test(generator): keep route coverage independent of compatibility identity * test(generator): keep enum policy coverage routing-only * test(generator): restore legacy manifest compatibility coverage * style(generator): restore final newline * fix(generator): keep legacy type-level adapter defaults * fix(generator): keep explicit type adapters contract-owned * fix(generator): preserve adapter selection diagnostic * refactor(codec): remove public generated factory kind * refactor(runtime): validate adapter-backed factories structurally * refactor(runtime): infer adapter-backed factories from metadata * refactor(generator): stop emitting runtime factory kind * test(runtime): remove public factory kind coverage * test(runtime): keep custom factory adapter-free * fix(generator): keep factory emission adapter-metadata-only * test(server): keep custom factory adapter-free * fix(generator): keep adapter selection diagnostics structured * fix(generator): stop parsing diagnostic presentation text * fix(generator): preserve explicit adapter ownership * fix(generator): retain identical default codec publication --------- Co-authored-by: sunsi <suns12138@163.com>
Closes #311.
This PR adds compile-time
RpcCodecRoute/RpcCodecScoperouting with assembly-owned codec routing.Ownership model
The RPC Contract remains the RPC identity boundary, but the Contract assembly is the codec-route configuration, ownership, publication, and freeze boundary.
For one generated Contract assembly:
All
[RpcContract]interfaces emitted from the same Contract assembly therefore share the same immutable final Codec graph. Two Contracts in the same assembly do not independently select different codecs for the same closedT.Different Contract assemblies may select different codec routes/policies.
Public routing surface
RpcCodecRouteis assembly-scoped. Contract/interface-local route configuration is intentionally not part of the final API.Selection precedence remains deterministic:
Conflicting explicit selections or overlapping assembly routes with incompatible adapters produce deterministic compile-time diagnostics.
Generator / manifest / runtime model
The generator computes and publishes one assembly-owned final RPC Codec graph for the Contract assembly. Generated proxies/stubs bind to that graph at construction/registration time; endpoint runtime codec overrides cannot mutate RPC wire semantics after the assembly graph is frozen.
Contract
Typeremains the identity used for RPC method/service lookup, but it is not an independent codec-policy ownership key. Runtime resolution may start from a Contract Type and resolve to its owning assembly graph; this must not imply separate same-assembly codec policy.Legacy per-Contract/API-5 artifacts such as
ContractCodecSetsand same-assembly distinct codec-provider selection are transitional implementation history and are not part of the final target model.Structural and ownership validation
Generated manifest validation is assembly-owned. Manifest codec metadata must belong to the manifest owner assembly and must be structurally valid before preparation/adoption. Dynamic registration and direct/catalog RuntimeContext paths use the same assembly-level validation boundary.
Compatibility and lifecycle
Compatibility identity for RPC codec bindings is assembly-scoped:
Generated dependency closure is frozen with the assembly-owned graph so dynamic registration/replacement/unload keeps adapter scopes and generated modules alive until calls drain.
Acceptance boundary
The final implementation must satisfy these invariants:
RpcCodecRouteconfiguration is assembly-level only.Validation results will be recorded from the exact final head only after PR Quick, Release Gate, and CodeQL are green.