Skip to content

spec: event-driven creation - a create-from may run on a source event - #14

Open
delchev wants to merge 1 commit into
mainfrom
spec/generates-event
Open

spec: event-driven creation - a create-from may run on a source event#14
delchev wants to merge 1 commit into
mainfrom
spec/generates-event

Conversation

@delchev

@delchev delchev commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

The gap

generates: (create-from) was strictly a user action: a button on the source view. There was no way to say "when the source reaches this state, create the follow-up document".

The shape that keeps coming up: a record is completed by an earlier automated or human step, and a document must be minted from it without another click. A fine arrives by webhook; once the responsible person is identified on it (a status transition), a declaration document must be created from the fine and that person. The expressible options were all wrong:

  • a generates button plus a wait step — the automation degrades to a person remembering to click, and an unclicked record parks its process instance indefinitely;
  • posts — event-driven and idempotent, but it emits flat mapped rows and cannot mint a header-and-items document (the item rows need the id of the header it would have to create first);
  • hand-written code.

The 1.1 "Planned" list named this gap; this PR closes it and removes the entry.

What it adds

versions/1.2.md gains #### Event-driven creation — event:` under generates — create-from, plus an Appendix A row and a "what this version adds" bullet.

generates:
  - name: declaration-from-fine
    from: Fine
    to: Declaration
    event: { onTransition: Fine, when: "Status == IDENTIFIED" }   # or { onCreate: Fine }
    map:
      Fine: id                       # REQUIRED with an event — the back-reference, i.e. the guard
      Vehicle: Vehicle
    defaults: { declaredAt: now }
    items:
      - { name: "Fine {number}", amount: Amount }

Three normative rules, each answering a way this could go quietly wrong:

  • The event says WHEN, never what. It names the entity from: already declares, and never repeats the owning model (fromUses: owns that). Two ways to name one source could only drift. The guard is evaluated against the source re-read at delivery, never the payload — which is as-of the event and lacks anything a later step wrote.
  • At most once. map must copy the source's primary key onto the target's to-one back to the source, and the creation must return the target that already back-references it. Without that back-reference a redelivery mints a duplicate document, so a declaration lacking it is rejected. A create-from with no event keeps no such guard — producing several targets from one source by clicking twice is a legitimate manual act.
  • Declaring an event drops the button unless button: true asks for both; button: false with no event is rejected (the action would have no trigger at all). When both triggers exist they must share one creation path, and therefore one guard.

Reference implementation

Eclipse Dirigible: eclipse-dirigible/dirigible#6711 — the listener is generated as a message handler on the source's -transitioned (or create) topic that re-reads the source, applies the guard and calls the same create-from a button would, carrying no mapping of its own. Verified at the outermost layer: posting the source mints the whole document, line items included, with nobody calling the create-from, and a click afterwards returns that same document.

A create-from was strictly a user action: a button on the source view. When a
document must be minted the moment the source reaches a state - a fine ingested
by a webhook, whose responsible person is identified by a transition, must
produce a declaration document - the only expressible shapes were a button plus
a wait step (the automation degrades to a person remembering to click, and an
unclicked record parks its process instance indefinitely), row-shaped posts
(which cannot reference the header it would have to create), or hand-written
code.

A generates entry may now declare an event: onTransition with a mandatory status
guard, or onCreate for a source with no lifecycle. Three normative rules keep it
honest:

- the event says WHEN, never what - it names the entity from: already declares,
  and never repeats the owning model;
- it is at most once - the map entry copying the source's key IS the
  back-reference, and a declaration without it is rejected rather than minting a
  duplicate document on a redelivery;
- declaring an event drops the button unless button: true asks for both, and the
  two triggers share one creation path, hence one guard.

Removes event-driven document generation from the Planned list, which named this
gap.
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.

1 participant