feat(generator): per-assembly event-preservation registrar — subscribe-only events survive trimming (TRIM-003+007) - #71
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… trimmed client The subscribe-only consumer shape (event record referenced only as the generic argument of an aggregator's Subscribe<TEvent> call) fails on a publish-trimmed client: the type survives via the runtime [FactoryEvent] scan, but its ctor is stripped — inherited [DynamicallyAccessedMembers] on FactoryEventBase does not flow to derived types under ILLink. Triplet evidence: red trimmed (pure shape), green untrimmed, green trimmed with DAM on the subscribe generic parameter (validating the Raise<T>-style call-site fix mechanism). The harness check is intentionally red on this branch; TRIM-007 owns the fix, the doc corrections (current docs overpromise automatic trimming safety), and turning this check green before merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ity gate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-007) TRIM-003 proved the inherited [DynamicallyAccessedMembers] on FactoryEventBase does not preserve derived event records under ILLink — a subscribe-only event's ctor was stripped on the trimmed client (the DeserializeNoConstructor production symptom). A fourth incremental pipeline branch (CreateSyntaxProvider — descendants carry no attribute of their own) discovers every concrete, accessible FactoryEventBase descendant, walks it with the shared bucketed WalkDtoGraph (root + nested property graph), and emits one per-assembly NeatooEventPreservationRegistrar riding the existing assembly-level [NeatooFactoryRegistrar] discovery. Accessibility gate skips private/protected/file-scoped nested event records (the generated file could not legally reference them — RemoteFactory.UnitTests declares such records and now proves the gate at build time). Buckets are ordinally sorted for deterministic output. - TRIM-003's red subscribe-only harness check is now green in the pure consumer shape, including a nested record property; negative control verified (pipeline disabled -> identical failure, exit 1) - 8 new EventPreservationDiscoveryTests - Docs corrected end to end: trimming.md event section (incl. the now-inverted v1.4.0 supersession narrative and the nested section, now automatic), FactoryEventBase.cs comment, CLAUDE-DESIGN.md event paragraphs with history note, stale Design.Domain/Design.Tests comments Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y, stale DAM comment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…kill, and comments (TRIM-007 code review) Code review found the falsified 'inherited DAM preserves descendants' claim surviving in FactoryEventRelayPattern.cs (Design source of truth), docs/factory-events.md, the EventSubscribeOnlySmokeTest class summary, both RemoteFactory skill reference files, the FactoryEventTypeRegistry IL2026 suppression justification, and a harness comment. All corrected to the generator-emitted per-assembly registrar story. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two plans of the TRIM todo in one PR: TRIM-003's verification (deliberately red until this fix) and TRIM-007's fix that turns it green.
TRIM-003 (verification): a
FactoryEventBase-derived record whose only client-side reference is a genericSubscribe<TEvent>call site — the real consumer shape — loses its constructor underPublishTrimmed=true. The type resolves (runtime[FactoryEvent]scan works) but deserialization throws theDeserializeNoConstructor-shaped failure: inherited[DynamicallyAccessedMembers]does not flow to derived types under ILLink. The v1.4.0 "annotation covers every descendant" story was wrong, and the old smoke test couldn't see it (it constructs its event directly). Evidence triplet: red trimmed / green untrimmed / green with call-site DAM.TRIM-007 (fix, per user decision — generator emission): a fourth incremental pipeline branch discovers every concrete, accessible
FactoryEventBasedescendant declared in a compilation (records-onlyCreateSyntaxProvider— descendants carry no attribute), walks it with the shared bucketedWalkDtoGraph(root + nested property graph), and emits one per-assemblyNeatooEventPreservationRegistrarriding the existing assembly-level[NeatooFactoryRegistrar]discovery (whose DAM-annotatedTypeparameter roots the registrar under full trimming). Declaring an event is now sufficient — no handler, no subscription, no consumer annotation, no LinkerConfig entry. Nested reference types in event records stop being a documented manual case.Key design points from review: accessibility gate (private/protected/file-scoped nested event records are skipped — the repo's own private test events would otherwise break every consumer build), FQN base matching, ordinally-sorted deterministic output, no emission when no events.
Verification
EventPreservationDiscoveryTests(buckets, nested walk, abstract intermediates, accessibility gate, generics, FQN decoy, dedupe, determinism, no-events)Reviews (in
docs/todos/TRIM-dto-trimming-preservation-gaps/reviews/)Test plan
🤖 Generated with Claude Code