0f1626ecef
Three structural fixes to flatten mode, found bringing up games on-device (Epic A1 Phase 5): 1. Nested classes: processFlatten now recurses through getDeclaredClasses() (not gated on the outer class's eligibility), so nested Streamables like BangConfig.Round get deterministic methods too. All detection/substitution/ insertion is scoped to each class's own body region (brace-matched, comment/string-aware, member-class bodies masked during detection) so an instrumented outer class can't hide — or poison — a nested one. 2. Ancestor-aware generation: a class whose ancestor declares read/writeObject now generates `super.readObject(ins)` + only the fields declared below that ancestor, instead of a self-contained full-set method. A self-contained subclass method silently dropped the ancestor's side effects for every subclass that previously inherited it: Prop.init() (server NPE cloning board props at startRound), Item's _nondb identity extras (itemId=0 on every wire-streamed item), InvocationMarshaller's _invdir rebind. Byte layout is unchanged: the ancestor's method streams exactly the flattened prefix. When the subclass adds no fields, nothing is generated — plain inheritance is already complete (the field-free marshaller/unit case). 3. Abstract classes: substitution-only. Their default*Object() pass-throughs are replaced in place with the class's own flattened prefix (making them safe super-call targets — the reflective pass-through reads the full dynamic-class set and would double-read the subclass tail), but whole methods are never generated for them. Requires every Streamable ancestor to be covered by the task's filesets; see findStreamMethodAncestor doc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhfHCXYp2ctWi76Z91y9Ut