Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/docs/intent-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ A single `app.intent` YAML file at a project root is the source of truth one alt

**The enrichment channel (`phases:` + `onPhase`, [#6929](https://github.com/eclipse-dirigible/dirigible/issues/6929)):** a value a listener computes AFTER the insert — a moving-average cost, a snapshot column, an external lookup — must be written back **event-silently** or it re-fires every onUpdate consumer of a change the user never made; so it published nothing at all, and a declarative consumer of that value had no moment to bind. Bound to `onCreate` it RACED the enrichment (two listeners on one topic have no order — each `MessageHandler` is its own durable subscriber, and there is no priority anywhere), and posted a balanced-looking journal entry for a null amount with parse, generation, compile and publish all green. The fix is a CHANNEL, not an ordering contract the broker cannot keep: an entity declares the moments it announces (`phases: [costed]`), the Java DAO template emits one **`announce<Phase>(id, values)`** per phase — `updateProperties` with the phase's own topic, so the enrichment and its notice ride ONE write into the outbox and commit together — and any glue consumer binds `event: { onPhase: <Entity>, phase: <name> }`. The generated method is the point: a hand-typed topic string reproduces exactly the silence being removed, a mistyped `announceCosted` is a compile error. Accepted by `postings:` (the driver), `notifications:`, `integrations:`, `outbound:` and an event-driven `generates:`, with the `when:` guard optional there (the phase already IS one moment); deliberately not by a process `trigger:`, a `wait` or `resolves:`. Refused at parse, each because it is otherwise silent: a phase that is not a lower-camel identifier, one named after a platform channel (`updated`/`deleted`/`transitioned`/`rekeyed`), a duplicate, a `phase:` key on another axis, and a binding naming a phase the entity does not declare. Details in the engine-intent guide's phases bullet.

**Which source rows become lines (`items: where:` + `refuse:`, [#7091](https://github.com/eclipse-dirigible/dirigible/issues/7091)):** a create-from's mirror `items:` block cloned EVERY row of the source document into a target line and the DSL could not say which rows qualified, so base-timesheets billed every member timesheet of the project-month - a DRAFT / REJECTED one at the same footing as an APPROVED one, and an EMPTY one (whose mapped quantity the target refuses) stopped the whole Generate until someone deleted the row by hand. "Invoice the approved month" is the one flow a billing clerk runs, and the module could either bill unapproved hours or not bill at all; the gap is fleet-wide (proforma -> invoice, quotation -> order, order -> invoice). `where:` is the rule - the same `{ field, op, value }` triples a `schedules[].where` carries, incl. a moment value resolved against the clock of the run - pushed into the very `Criteria` that already selects the source's rows by their master foreign key, so an unqualified row is never loaded; a condition naming the source ITEM's own `function: EntityStatus` relation may use the seeded status name (on the item's nomenclature, never the header's). `refuse:` declares the other reading: an unqualified row stops the whole run with the authored message plus the KEYS of the offending rows, instead of being left out - dropping a rejected line silently and billing it silently are both wrong for different months, so skipping is the default and `refuse:` without a `where:` is refused at parse. **A rule that qualifies no row refuses too**, rather than committing a header with no lines - the harder failure to notice, the document existing and counting as the period's billing. Scoped to a `where`-declaring block, so a rule-less items block is byte-identical; the rule's `field` is checked against the item source's own properties at parse, unlike a schedule's query, whose source may be a cross-model row.
**Which source rows become lines (`items: where:` + `refuse:`, [#7091](https://github.com/eclipse-dirigible/dirigible/issues/7091)):** a create-from's mirror `items:` block cloned EVERY row of the source document into a target line and the DSL could not say which rows qualified, so base-timesheets billed every member timesheet of the project-month - a DRAFT / REJECTED one at the same footing as an APPROVED one, and an EMPTY one (whose mapped quantity the target refuses) stopped the whole Generate until someone deleted the row by hand. "Invoice the approved month" is the one flow a billing clerk runs, and the module could either bill unapproved hours or not bill at all; the gap is fleet-wide (proforma -> invoice, quotation -> order, order -> invoice). `where:` is the rule - the same `{ field, op, value }` triples a `schedules[].where` carries, incl. a moment value resolved against the clock of the run - pushed into the very `Criteria` that already selects the source's rows by their master foreign key, so an unqualified row is never loaded; a condition naming the source ITEM's own `function: EntityStatus` relation may use the seeded status name (on the item's nomenclature, never the header's) - as, since [#7251](https://github.com/eclipse-dirigible/dirigible/issues/7251), does the `schedules[].where` row query this shape was modelled on, where a name generated `.eq("Status", "OVERDUE")` into the job and matched nothing forever. `refuse:` declares the other reading: an unqualified row stops the whole run with the authored message plus the KEYS of the offending rows, instead of being left out - dropping a rejected line silently and billing it silently are both wrong for different months, so skipping is the default and `refuse:` without a `where:` is refused at parse. **A rule that qualifies no row refuses too**, rather than committing a header with no lines - the harder failure to notice, the document existing and counting as the period's billing. Scoped to a `where`-declaring block, so a rule-less items block is byte-identical; the rule's `field` is checked against the item source's own properties at parse, unlike a schedule's query, whose source may be a cross-model row.

**A create-from is not offered twice (`fromStatus:`, [#7068](https://github.com/eclipse-dirigible/dirigible/issues/7068)):** a `generates:` with a `sourceStatus:` completion hook flipped its source once the target existed and then went on offering the same button on the flipped record - and answering the same endpoint 200 - so a second click minted a **second document**: a proforma already INVOICED produced a second invoice, in the customer's hands. The hook declared what "already done" looks like; nothing consulted it. A create-from now carries a from-status guard resolved ONCE and fed to both halves of the action: the generated `run()` refuses with **409** before anything is created, and the contributed action descriptor carries the same guard so the shared `customActions` store stops OFFERING the click on a record it would refuse (`getActions(view, type, record)` takes the record the view already has). Two shapes: `fromStatus: [...]` is the explicit allow-list - the `from:` of a `transitions:` entry, spelled differently only because `from:` on a create-from already names the source ENTITY - and absent it a declared `sourceStatus` IMPLIES the deny-list of exactly that status, so a model that already carries the defect is fixed with no authoring change. The guard is on the CLICK: an event-driven create-from keeps its own at-most-once back-reference guard and qualifies its moment with `event.when`, so `fromStatus` on an event-only rule is refused at parse rather than silently ignored - as are a `page` scope, a source with no `function: EntityStatus` relation, and an allow-list containing the `sourceStatus` the action itself writes.

Expand Down
Loading
Loading