You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
validate / lint / build accept a lookup or master_detail whose reference names an object that exists nowhere — the dangling target is found only at runtime #16611
objectstack validate, objectstack lint and objectstack build all exit 0, with no diagnostic of any severity, for a Field.lookup (and Field.masterDetail) whose reference names an object that is declared nowhere — neither in the stack nor in the platform-provided object registry. The metadata ships, and the target is discovered only at runtime: the record picker for that field asks the REST layer for an object that is not registered (404 / OBJECT_NOT_FOUND), $expand on the field fails, and the object's own form renders a lookup control that can never resolve.
Measured on @objectstack/spec / @objectstack/cli / @objectstack/lint17.3.0, in the objectstack-ai/hotclm app (9 objects, requires: ['ui']).
Same result for a plausible cross-package spelling (Field.lookup('crm_contract', …) in an app that does not ship HotCRM) — which is the case that made this visible: a card asked "declare the cross-package lookup only if validate accepts it", and validate cannot answer that question at all.
packages/lint/src/validate-object-references.ts (the object-reference-unknown rule, ADR-0072) deliberately covers "the reference sites no other rule owns": action params, dashboard globalFilters[].optionsFrom.object, dataset object, navigation requiresObject / requiresService / objectName. Field-level reference is not on the list.
defineStack's validateCrossReferences hard-fails on hooks, view data, seeds, mappings, permission grants, nav objectName and action targets — not on field references.
#4441 covers a related but different gap (a lookup VALUE — a row id — that does not exist, refused on the write path since then); the object-level target is a different hole.
So the one metadata reference every record form depends on is the one no gate reads.
Expected capability
A finding on the field's reference target, with the same severity ladder validate-object-references.ts already implements for its other surfaces:
resolves in the stack's own objects → OK;
resolves in PLATFORM_PROVIDED_OBJECT_NAMES (e.g. sys_user, which Field.user() targets) → OK;
unresolved, not platform-prefixed → error at validate / build;
unresolved, platform-prefixed but unknown → the existing advisory.
Cross-package references (an app that expects a sibling package to provide the target, e.g. HotCLM's clm_contract.crm_contract → HotCRM's crm_contract) need a declared escape rather than a silent pass — e.g. an authored crossPackage: true / externalObject marker on the field, or resolution against requires / composition — so the reviewer sees the choice in the diff instead of the gate being blind to every spelling.
Where it was found
hotclm issue #2 (card 02, contract domain): the card's condition "declare crm_contract only if objectstack validate accepts a cross-package reference" turned out to be vacuous — validate accepts every reference, including a control probe to a nonexistent object. The field was left out of that PR (objectstack-ai/hotclm#5) rather than shipped as a dangling reference. Per hotclm AGENTS.md ("Platform gaps: report, never patch") this issue is the report; the docs/PLATFORM_GAPS_FROM_TEMPLATES.md append is left to whoever picks this up here.
Symptom
objectstack validate,objectstack lintandobjectstack buildall exit 0, with no diagnostic of any severity, for aField.lookup(andField.masterDetail) whosereferencenames an object that is declared nowhere — neither in the stack nor in the platform-provided object registry. The metadata ships, and the target is discovered only at runtime: the record picker for that field asks the REST layer for an object that is not registered (404 /OBJECT_NOT_FOUND),$expandon the field fails, and the object's own form renders a lookup control that can never resolve.Measured on
@objectstack/spec/@objectstack/cli/@objectstack/lint17.3.0, in the objectstack-ai/hotclm app (9 objects,requires: ['ui']).Minimal repro
In any
ObjectSchema.create({...})add:Then:
Same result for a plausible cross-package spelling (
Field.lookup('crm_contract', …)in an app that does not ship HotCRM) — which is the case that made this visible: a card asked "declare the cross-package lookup only if validate accepts it", and validate cannot answer that question at all.Why nothing catches it
packages/specFieldSchemarequiresreferenceto be present and non-empty onlookup/master_detail([spec]FieldSchemaaccepts alookup/master_detailwith noreferencetarget, though its own TSDoc calls the key required #13632 closed that hole), but says nothing about the target existing.packages/lint/src/validate-object-references.ts(theobject-reference-unknownrule, ADR-0072) deliberately covers "the reference sites no other rule owns": action params, dashboardglobalFilters[].optionsFrom.object, datasetobject, navigationrequiresObject/requiresService/objectName. Field-levelreferenceis not on the list.defineStack'svalidateCrossReferenceshard-fails on hooks, view data, seeds, mappings, permission grants, navobjectNameand action targets — not on field references.#4441covers a related but different gap (a lookup VALUE — a row id — that does not exist, refused on the write path since then); the object-level target is a different hole.So the one metadata reference every record form depends on is the one no gate reads.
Expected capability
A finding on the field's
referencetarget, with the same severity laddervalidate-object-references.tsalready implements for its other surfaces:PLATFORM_PROVIDED_OBJECT_NAMES(e.g.sys_user, whichField.user()targets) → OK;validate/build;Cross-package references (an app that expects a sibling package to provide the target, e.g. HotCLM's
clm_contract.crm_contract→ HotCRM'scrm_contract) need a declared escape rather than a silent pass — e.g. an authoredcrossPackage: true/externalObjectmarker on the field, or resolution againstrequires/ composition — so the reviewer sees the choice in the diff instead of the gate being blind to every spelling.Where it was found
hotclm issue #2 (card 02, contract domain): the card's condition "declare
crm_contractonly ifobjectstack validateaccepts a cross-package reference" turned out to be vacuous — validate accepts every reference, including a control probe to a nonexistent object. The field was left out of that PR (objectstack-ai/hotclm#5) rather than shipped as a dangling reference. Per hotclm AGENTS.md ("Platform gaps: report, never patch") this issue is the report; thedocs/PLATFORM_GAPS_FROM_TEMPLATES.mdappend is left to whoever picks this up here.