fix: resolve non-object references, make dbImport/dialect optional, stamp gen-state engine version - #245
Open
dmealing wants to merge 3 commits into
Open
fix: resolve non-object references, make dbImport/dialect optional, stamp gen-state engine version#245dmealing wants to merge 3 commits into
dmealing wants to merge 3 commits into
Conversation
…e; docs(#194): adopter constraints #232 — meta gen records the @metaobjectsdev/codegen-ts engine version alongside the gen-state hashes (a separate .engine.json, never part of the three-way merge) and prints one informational line when it differs from the last run, so a post-`npm update` regen diff is explained rather than surprising. No change to generated output. #194 — document three adopter constraints in extending-with-providers.md: a new top-level node type needs registry.extend on metadata.root's child rules; the built-in ref resolver only indexes object.* targets; and dbImport/dialect are required config even for value-object-only projects (inert when no DB code is generated). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TJRi8FtEW24z9HKGUL1xh8
…onal (replaces docs) A Fable adjudication of the three #194 adopter constraints ruled two of them real bugs the prior commit had merely DOCUMENTED rather than fixed. This fixes both in code and keeps only the genuinely correct-by-design one as docs: - Item 2 (FIX): a ReferenceDescriptor's targetType is a free string, but the loader symbol table indexed only object.* nodes, so a descriptor targeting a custom top-level type resolved cleanly then unconditionally failed with a false 'does not resolve to an object'. The symbol table is now keyed per node type; a reference to any registered top-level kind resolves under the ADR-0042 contract, and a top-level non-object node establishes package context for its subtree. Object-target refs are byte-identical (2252 metadata tests green). - Item 3 (FIX): dbImport/dialect were required config even for a value-object-only project that emits zero DB code. They are now optional on the user config; the runner fills inert defaults when absent AND no DB object is present, and throws a clear error naming the entities when a DB-emitting model omits them (never a silent sqlite default for a forgotten Postgres dialect). Resolved config the generators consume stays required — DB-project output unchanged. - Item 1 (DOCUMENT): requiring registry.extend on metadata.root to license a custom top-level type is chartered fail-closed design (FR-033) — kept as docs, rewritten to a 'defining a custom top-level node type' how-to that also notes item 2's fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TJRi8FtEW24z9HKGUL1xh8
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.
Intent
Fable-adjudicated fixes for the 'batch of small fixes'. The maintainer challenged that I'd been lazy — documenting adopter papercuts instead of fixing them — so Fable audited all three #194 constraints against the code and ruled two of them real bugs to FIX. This branch already carries the earlier-validated #232 (gen-state engine-version stamp + the pipeline's F1 isolation fix), unchanged. The new work fixes:
ITEM 2 (metadata pkg): the loader symbol table indexed ONLY object.* nodes, so a ReferenceDescriptor targeting a custom top-level type (targetType:'adapter') registered cleanly then UNCONDITIONALLY failed every reference with a false 'does not resolve to an object' — a silently-unsatisfiable config, breaking the mechanism's 'present and future' promise. Fix: symbol table keyed PER node type (byType map); resolve(type,ref,referrerPkg) added to SymbolTable with resolveObject delegating; and a TOP-LEVEL node of any type now establishes package context for its subtree (previously only objects did). Object-target resolution byte-identical: 2252 metadata tests + whole conformance corpus unchanged; strictly enabling. Test added (probe->adapter resolves; dangling errors naming the kind).
ITEM 3 (codegen-ts + cli): dbImport/dialect were REQUIRED config even for a value-object-only project that emits zero DB code. Made them optional on the USER config; ResolvedGenConfig (generators' view) keeps them required so no generator changes. The runner guards BEFORE defaulting: a model with any concrete non-object.value object (entity/projection) that omits them throws a clear error naming the entities — never a silent sqlite default for a forgotten Postgres dialect (fail-closed). VO-only gets inert placeholders. This rippled to ONE cli consumer (gen.ts display header via output.ts GenResultShape.dialect) which is now widened to show the dialect only when present — the pre-push build+typecheck gate correctly caught this (a stale-dist typecheck had missed it); now build+typecheck clean across all 18 packages. 1017 codegen-ts + 141 golden + 418 cli tests green; DB-project output unchanged. VO-only test added (omit both=>generates; DB entity omitting both=>clear throw; with both=>normal).
ITEM 1 (kept as DOCUMENT): requiring registry.extend on metadata.root to license a custom top-level type is chartered fail-closed design (FR-033 rejected auto-licensing). Docs rewritten into a 'defining a custom top-level node type' how-to that also documents item 2's now-working non-object references.
Deliberate: kept ResolvedTarget.dbImport as string (defaulting) rather than widening to string|undefined, to avoid rippling undefined through render-context; per-type symbol-table bucketing also prevents a template and object sharing pkg::Name from colliding.
What Changed
byTypemap) with a newresolve(type, ref, referrerPkg)(whichresolveObjectdelegates to): a reference descriptor targeting a custom non-objecttop-level type (itstargetTypeis a free string) now resolves instead of unconditionally failing with a false "does not resolve to an object", and a top-level node of any type now establishes package context for its subtree. Error messages name the expected kind; object-target resolution is byte-identical.dbImport/dialectare now optional on the user-facingmetaobjects.config.ts(still required onResolvedGenConfig): the runner guards before defaulting — a model with any concrete non-object.valueobject that omits them throws a clear error naming the entities (fail-closed, never a silent sqlite default for a forgotten Postgres dialect), while a value-object-only project gets inert placeholders. The CLI gen header now shows the dialect only when present..metaobjects/.gen-state/.engine.json(separate from.hashes.json, never affects the merge) and warns when it changed since the last gen; adopter docs are rewritten into a "defining a custom top-level node type" how-to covering the now-working non-object references.Risk Assessment
✅ Low: Clean, well-bounded change: the symbol-table refactor is byte-identical for every core reference path (all core descriptors target TYPE_OBJECT and no core reference-bearing node is top-level-non-object), the optional dbImport/dialect guard is fail-closed and reads raw config before defaulting, and the engine-version stamp is informational and isolated to a separate file under already-gitignored .gen-state — no material bugs found.
Testing
Built the TS packages and drove the actual
metaCLI and metadata loader: ITEM 3 is shown three ways via realmeta gen(VO-only with omitted dbImport/dialect generates with a dialect-less header; DB-entity omitting them fail-closes with a clear error naming the entity; with both set it generates and shows the dialect in the header), ITEM 2 is shown via the real loader (a custom non-objectadapterreference resolves cleanly, a dangling one errors naming the kind), and #232 is shown via two realmeta genruns (the.engine.jsonstamp is written and an engine change emits the explanatory warning). The four focused test files pass (13 tests) and a 55-test regression set over the changed config/output/reference surfaces passes, confirming object-target resolution stays byte-identical. No failures; worktree left clean (only gitignored build outputs).Evidence: ITEM 3 — value-object-only gen (dbImport/dialect omitted)
$ meta gen --format text meta gen — generated NEW generated/Address.ts 1 written exit=0Evidence: ITEM 3 — DB entity omitting dbImport/dialect fail-closes
$ meta gen meta: gen failed: codegen config is missing dialect and dbImport — required because this model generates database code for: User. Set dialect and dbImport in metaobjects.config.ts. (Only a value-object-only model may omit them.) exit=1Evidence: ITEM 3 — DB entity WITH dbImport+dialect generates, header shows dialect
$ meta gen --format text meta gen — postgres, generated NEW generated/User.ts 1 written exit=0Evidence: ITEM 2 — non-object reference resolves; dangling errors naming the kind
===== VALID non-object reference (probe -> adapter resolves) ===== OK — no errors (reference resolved) ===== DANGLING non-object reference ===== ERR_PROBE_ADAPTER_UNRESOLVED: probe.ping "HealthCheck" @adapterRef "NoSuchAdapter" does not resolve to a adapter.Evidence: #232 — engine-version change emits an explanatory warning
$ meta gen meta: codegen engine 0.20.8 → 0.20.9 since last gen — generated output may differ; see CHANGELOG. gen[1]{file,status}: generated/User.ts,unchanged summary: 1 unchanged exit=0Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
bun test packages/metadata/test/validation-registry.test.ts— 4 pass: non-object reference (probe→adapter) resolves; dangling reference errors naming the kind ('does not resolve to a adapter'); widget object-reference valid/dangling cases.bun test packages/codegen-ts/test/vo-only-config-optional.test.ts— 3 pass: VO-only omit dbImport/dialect generates; DB-entity omit throws clear error naming the entity; DB-entity with both generates.bun test packages/codegen-ts/test/engine-version-stamp.test.ts— 3 pass: unstamped→undefined; save/load round-trip writes separate.engine.json(not.hashes.json); malformed→undefined never throws.bun test packages/codegen-ts/test/metaobjects-config.test.ts packages/cli/test/unit/output-gen.test.ts packages/metadata/test/relationship-definition-completeness.test.ts packages/metadata/test/relationship-m2m.test.ts packages/metadata/test/template-validation.test.ts— 55 pass: confirms byte-identical object-target resolution + the config-shape and CLI dialect-header changes.Manual end-user CLI runmeta genon a real value-object-only project with dbImport/dialect omitted → headermeta gen — generated(no dialect), generatesAddress.ts.Manual end-user CLI runmeta genon a DB-entity project omitting dbImport/dialect → fail-closed error namingUser, exit 1.Manual end-user CLI runmeta gen --format texton a DB-entity project WITH dbImport+dialect:postgres → headermeta gen — postgres, generated, generatesUser.ts.Manual loader run with a customadapter/probeprovider: valid non-object reference resolves (no errors); dangling reference →ERR_PROBE_ADAPTER_UNRESOLVED ... does not resolve to a adapter.Manual end-user CLI runmeta gentwice: first stamps.metaobjects/.gen-state/.engine.json(codegenVersion 0.20.9); after simulating an older stamp, re-run warnscodegen engine 0.20.8 → 0.20.9 since last genand refreshes the stamp.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.