fix(codegen-ts): read field attributes via effective attr(), not ownAttr()#55
Merged
Merged
Conversation
…ttr()
Field-level `extends` (abstract fields — a concrete field extending a package-level
abstract field to inherit `required` / `maxLength` / `default` / etc.) was silently
dropping the inherited attributes: the generators read them with `ownAttr()` (own
declarations only), so an inherited `@required` produced a NULLABLE column, an
inherited `@maxLength` was ignored, etc. Output was under-constrained — e.g. a field
`{ extends: Name }` of an abstract `Name {required, maxLength:200}` generated
`text("name")` instead of `varchar("name",{length:200}).notNull()`.
This surfaced after the collect-all rework (#52) made inherited attrs resolve as
effective-but-not-own; the generators' long-standing `ownAttr()` usage then missed
them. Switched every FIELD_ATTR_* read (57 sites across column-mapper, drizzle-schema,
zod-validators, inferred-types, entity-constants, filters, payload/extract, docs, …)
to the effective `attr()`. For a field with no `extends`, `attr() === ownAttr()`, so
this is a no-op for all existing output (codegen-ts 803/0, byte-identical goldens);
it only restores the inherited case. Identity/object/source/validator reads stay
`ownAttr()` (correctly own-only).
Also: buildFkMapForEntity now strips the package from the FK target before the lookup
(`findObject(stripPackage(targetName))`) — the YAML front-end resolves `@references`
to an FQN (`pkg::Entity`), and the un-stripped lookup dropped every FK under YAML.
Mirrors the relation-resolver, which already strips.
Tests: a YAML fixture exercising both (abstract-field required+maxLength inheritance,
and a package-qualified FK target). codegen-ts 803/0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dmealing
force-pushed
the
fix/codegen-effective-field-attrs
branch
from
June 21, 2026 16:21
ae1f0a0 to
af411f9
Compare
dmealing
added a commit
that referenced
this pull request
Jun 21, 2026
…ot own-only (#56) Completes the #56 fix across the three affected ports (the gates in this PR now pass). Builds on #55, which fixed the TS *field*-attr reads. - TS (codegen-ts): identity @fields/@generation/@unique reads switched ownAttr → attr (pk-resolver, drizzle-schema, zod-validators, queries, docs-data-builder). #55 fixed field reads but not identity reads, so a BaseEntity-inherited PK was still dropped. - Python (codegen): ~17 field-attr reads switched field.attr(X) → field.attrs().get(X). Python's attr() is OWN-ONLY despite the name (the footgun); attrs() is the effective map. Covers @required/@maxLength/@objectRef/@default/@values/@isArray/@filterable/ @enumAlias/@enumDoc/@column. (Validator/origin/template reads left own — correct as-is.) - Kotlin (codegen-kotlin): field-attr reads switched getMetaAttr/hasMetaAttr(..., false) → (..., true) for @maxLength/@storage/@objectRef/@filterable (TypeMapper, ExposedTableGenerator, EntityGenerator, RenderHelperGenerator, FilterAllowlist). Node-level reads (identity/relationship via effective iterators) and the validation reference-walk (validate-at-declaration) are deliberately left own-only. Gates green: TS codegen-ts 883 + react/tanstack 69 + whole-workspace typecheck clean; Python 1207; Kotlin codegen-kotlin 35. C# and Java codegen need no change (their attr accessors already default effective / inherit whole nodes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dmealing
added a commit
that referenced
this pull request
Jun 21, 2026
…ython/Kotlin + regression gates (#57, #56) * test(gate): RED tripwires for inherit-without-restate codegen bug (#56) Two failing gates (RED until #56 is fixed) asserting codegen reads INHERITED attrs via the effective accessor, not own-only: - TS (codegen-ts): an identity.primary that inherits @fields via node-level extends without restating it must still yield a primary key. pk-resolver reads primary.ownAttr(@fields) → undefined → the entity is dropped from the PK map → no PK. - Python (codegen): a field that inherits @required via extends (abstract-field reuse) must generate as a REQUIRED Pydantic field, not Optional. entity_model reads field.attr(@required) — which in this port is OWN-ONLY despite the name — so the inherited @required is invisible and `label: str | None = None` is wrongly emitted. Both assert the CORRECT behavior and turn green when the ownAttr→attr fix lands (see #56 for the full cross-port inventory). Intentional RED tripwire PR — do not merge until #56 is fixed; then these become the permanent regression gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gate): add Kotlin tripwire for inherit-without-restate (#56) Kotlin (codegen-kotlin): a field that inherits @maxlength via extends (abstract-field reuse) without restating it must emit varchar with the inherited length, but KotlinTypeMapper reads @maxlength own-only → falls back to the 255 default. Confirmed RED: emits varchar("label", 255) for an inherited @maxlength=80. Completes the confirmed-port coverage (Python + Kotlin) alongside the latent TS edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(codegen): read inherited attributes via the effective accessor, not own-only (#56) Completes the #56 fix across the three affected ports (the gates in this PR now pass). Builds on #55, which fixed the TS *field*-attr reads. - TS (codegen-ts): identity @fields/@generation/@unique reads switched ownAttr → attr (pk-resolver, drizzle-schema, zod-validators, queries, docs-data-builder). #55 fixed field reads but not identity reads, so a BaseEntity-inherited PK was still dropped. - Python (codegen): ~17 field-attr reads switched field.attr(X) → field.attrs().get(X). Python's attr() is OWN-ONLY despite the name (the footgun); attrs() is the effective map. Covers @required/@maxLength/@objectRef/@default/@values/@isArray/@filterable/ @enumAlias/@enumDoc/@column. (Validator/origin/template reads left own — correct as-is.) - Kotlin (codegen-kotlin): field-attr reads switched getMetaAttr/hasMetaAttr(..., false) → (..., true) for @maxLength/@storage/@objectRef/@filterable (TypeMapper, ExposedTableGenerator, EntityGenerator, RenderHelperGenerator, FilterAllowlist). Node-level reads (identity/relationship via effective iterators) and the validation reference-walk (validate-at-declaration) are deliberately left own-only. Gates green: TS codegen-ts 883 + react/tanstack 69 + whole-workspace typecheck clean; Python 1207; Kotlin codegen-kotlin 35. C# and Java codegen need no change (their attr accessors already default effective / inherit whole nodes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <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.
Publish-blocker for abstract-field models.
Field-level
extends(a concrete field extending a package-level abstract field to inheritrequired/maxLength/default/…) was silently dropping the inherited attributes — the generators read them viaownAttr()(own-only). So{ extends: Name }of an abstractName {required, maxLength:200}generatedtext("name")instead ofvarchar("name",{length:200}).notNull().This regressed after #52's collect-all made inherited attrs resolve effective-but-not-own; the long-standing
ownAttr()usage then missed them. Switched everyFIELD_ATTR_*read (57 sites) to the effectiveattr().extends,attr() === ownAttr()→ no-op for all existing output (goldens byte-identical, 803/0). Only the inherited case is restored.ownAttr()(correctly own-only).Also folds in:
buildFkMapForEntitystrips the package from the FK target before lookup — the YAML front-end resolves@referencesto an FQN (pkg::Entity), and the un-strippedfindObjectdropped every FK under YAML. Mirrors the relation-resolver.Test: a YAML fixture exercising both (abstract-field
required+maxLengthinheritance, and a package-qualified FK target).🤖 Generated with Claude Code