docs(adr): amend ADR-0028 — a concrete projection owns its source - #272
Merged
Conversation
Records the doctrine behind ERR_PROJECTION_INHERITED_SOURCE (bdeb476): a concrete object.projection declares its own source.*; an abstract projection base carries shape only. The rationale is worth writing down because the rule looks arbitrary until you see which of two inheritance regimes a projection belongs to. Shared-storage inheritance inherits binding AND writability together — Hibernate's @immutable is inherited by entity subclasses, EF Core keyless ToView types, SQLAlchemy single-table. Shape-reuse inheritance does not inherit the binding at all — JPA @MappedSuperclass has no table of its own, and Django documents inheriting db_table from an abstract base as a trap, in almost these words. A projection is the second kind, so the concrete class must bind its own storage. Also records why the fix guards the shape instead of flipping a predicate: "which source am I bound to" is resolving because entities legitimately inherit their table, "what KIND of source am I" is own-only because projection-ness belongs to the declaring object, and both are right for what they were designed for. Only their intersection was incoherent, and it produced no working artifact in any of the five ports. The authoring skill gains the rule with its sanctioned pattern (abstract sourceless base, concrete projections declaring their own views) so an agent authoring a versioned projection declares v_customers_v2 rather than silently sharing V1's view. Bundled into the sdk copy and the four agent-context-conformance goldens. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3msoGxjRMwx94PhKSLDuE
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.
Records the doctrine behind
ERR_PROJECTION_INHERITED_SOURCE(shipped inbdeb4765): a concreteobject.projectiondeclares its ownsource.*; an abstract projection base carries shape only.Why this needed writing down
The rule looks arbitrary until you see which of two inheritance regimes a projection belongs to — and the prior art is decisive on the split:
@Immutable(inherited by entity subclasses), EF Core keylessToView, SQLAlchemy single-table@MappedSuperclass("has no separate table defined for it"), Django abstract basesA projection is the second kind. Django documents inheriting
db_tablefrom an abstract base as a trap in almost these words: "all the child classes … would use the same database table, which is almost certainly not what you want." No surveyed system permits the unguarded middle MetaObjects had — binding inherited, kind not.Also records why the fix guards the shape rather than flipping a predicate. "Which source am I bound to" is resolving because entities legitimately inherit their table (TPH /
BaseEntity); "what KIND of source am I" is own-only because projection-ness belongs to the declaring object. Both are correct for what they were designed for — only their intersection was incoherent, and it produced no working artifact in any of the five ports (TS mounted writable CRUD over a read-only view; Java/Kotlin skipped on subtype; Python on resolved kind; C# emitted nothing).Also here
The
metaobjects-authoringskill gains the rule plus its sanctioned pattern — abstract sourceless base, each concrete projection declaring its own view — so an agent authoring a versioned projection declaresv_customers_v2instead of silently sharing V1's view. Bundled into the sdk copy and the fouragent-context-conformancegoldens.Not included: #270
#270 was scoped as "retire the redundant Kotlin/Python payload origin-typing." Its premise is falsified the same way #271's was. The redundancy claim holds for
origin.passthrough(type-preserving since #185) but not fororigin.aggregate(count→Long,avg→Double) ororigin.collection(→List<TargetPayload>), where the origin genuinely determines the type and the declared field type cannot express it. The corpus exercises exactly those —origin-collection-simpleandflattened-kitchen-sinkboth carry origins onobject.value. Deleting the handling would not be byte-identical; it would either break type derivation or make Kotlin match TS's origin-blindness, which may itself be the defect rather than the baseline. Needs its own recon before anything is deleted.Gates
Docs + skill only — no product code. TS typecheck clean; sdk suite green (150); agent-context goldens regenerated and matching.