Skip to content

feat(intent): resolve cross-model notify recipients + surface dropped glue (#6360)#6391

Merged
delchev merged 1 commit into
masterfrom
feat/intent-notify-cross-model-recipient
Jul 22, 2026
Merged

feat(intent): resolve cross-model notify recipients + surface dropped glue (#6360)#6391
delchev merged 1 commit into
masterfrom
feat/intent-notify-cross-model-recipient

Conversation

@delchev

@delchev delchev commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Closes #6360.

Problem

A schedules.notify / notifications recipient (to) or {placeholder} that is a one-hop relation.field only resolved against local entities. For a cross-model relation (Partner.email where Partner is owned by another uses model) the target lookup returned null and the entire notification/schedule was silently skipped with only a server-side LOGGER.warn. Cross-model to-one relations are first-class everywhere else (dropdowns, generates targets resolve through CrossModelSupport).

Changes

  1. Cross-model recipient resolution. NotificationSupport gains an injected CrossModelLookup (kept Spring/IO-free; the caller's lambda does the IO via CrossModelSupport). A cross-model relation.field resolves against the owner model, validates the field against the owner's property names, and records a RelationLoad flagged crossModel carrying the owner alias/project. The generated listener/job imports the owner's gen Entity/Repository (the registry-wide-compile mechanism the relation links / personal assignee already use): Job.java.template / Notification.java.template now import from ${load.javaGenFolder}, which generateUtils derives from the owner alias for a cross-model load and this project's folder otherwise.
  2. Dropped glue is surfaced, not silent. An unresolvable recipient is recorded on the generation context and returned in the generate response under warnings (generation still succeeds), in addition to the log line.

Verification (outermost layer)

Two-model fixture on a running instance (owner Partner{email}, consumer Deal with a cross-model Partner relation + a notification and a scheduled notify to Partner.email, plus one bogus recipient):

  • emitted DueDealsJob.java / DealCreatedNotification.java import gen.xm_owner.data.partner.Partner{Entity,Repository} and read Partner.Email / Partner.Name;
  • the client-Java batch compiled and registered the job (Registered client-Java job [gen.events.DueDealsJob]) and the listener (@Listener [gen.events.DealCreatedNotification] connected to TOPIC), 0 javac errors;
  • the bogus recipient came back in the generate response warnings and its schedule was dropped.

Unit coverage added in NotificationSupportTest (cross-model resolve, owner-field validation, no-lookup fallback). Existing NotificationSupportTest + GlueSchedulesTest green.

🤖 Generated with Claude Code

… glue (#6360)

A `schedules.notify` / `notifications` recipient (`to`) or `{placeholder}` that
is a one-hop `relation.field` could only reference a LOCAL entity: for a
cross-model relation the target was looked up in the model's own byName map,
resolved to null, and the whole notification/schedule was silently skipped with
only a server-side LOGGER.warn. Cross-model to-one relations are first-class
everywhere else (dropdowns, generate targets resolve through CrossModelSupport),
so a recipient like `Partner.email` where Partner is owned by another `uses`
model should resolve the same way.

Two changes:

1. Cross-model recipient resolution. NotificationSupport gains an injected
   CrossModelLookup (kept Spring/IO-free; the caller's lambda does the IO via
   CrossModelSupport) so a cross-model `relation.field` resolves against the
   owner model, validates the field against the owner's properties, and records
   a RelationLoad flagged crossModel with the owner alias/project. The generated
   listener/job then imports the OWNER's gen Entity/Repository (the same
   registry-wide-compile mechanism the relation links / personal assignee already
   use) - Job.java.template / Notification.java.template import from
   ${load.javaGenFolder}, which generateUtils picks from the owner alias for a
   cross-model load and this project's folder otherwise.

2. Dropped glue is no longer silent at the API level. A recipient that cannot be
   resolved is recorded on the generation context and returned in the generate
   response under "warnings" (the generation still succeeds), in addition to the
   log line - so an author sees that a notification/schedule was NOT emitted.

Verified end-to-end on a running instance with a two-model fixture (owner
Partner{email}, consumer Deal with a cross-model Partner relation): the emitted
DueDealsJob / DealCreatedNotification import gen.<owner>.data.partner.Partner*
and read Partner.Email; the client-Java batch compiled and registered the job +
listener (0 javac errors); a bogus recipient surfaced in the generate response
"warnings" and its schedule was dropped. Unit coverage added in
NotificationSupportTest (cross-model resolve, owner-field validation, no-lookup
fallback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@delchev
delchev merged commit c9f3138 into master Jul 22, 2026
10 checks passed
@delchev
delchev deleted the feat/intent-notify-cross-model-recipient branch July 22, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Intent schedules-notify: cross-model recipient resolution + surfaced validation for dropped glue

1 participant