Skip to content

intent: the glue's expression keys carry readings, not Java - the mapped values, the Calc guards, the print SDK calls and the ternaries (#7425) - #7487

Open
delchev wants to merge 14 commits into
masterfrom
issue-7425-glue-expression-keys
Open

delchev wants to merge 14 commits into
masterfrom
issue-7425-glue-expression-keys

Conversation

@delchev

@delchev delchev commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Continues #7406, whose literal and builder-chain tiers shipped in #7426. What was left was the tier that issue named last: the expressions. Across the BusinessIntents fleet the .glue still carried Java source in every expression key:

"expr":  "Calc.eval(\"-(Net - Discount)\", source, 2)",
"guard": "Calc.eval(\"Vat\", source, 6).compareTo(new java.math.BigDecimal(\"0\")) != 0",
"attachLanguageExpression": "attachLanguageSource == null || ... ? org.eclipse.dirigible.sdk.print.Print.defaultLanguage() : attachLanguageSource.Code.trim()",
"fileNameExpression": "org.eclipse.dirigible.sdk.print.FileNames.part(document.Number) + \"_\" + ...",
"dueExpression": "entity.ValidUntil == null ? java.util.Date.from(java.time.Instant.parse(\"9999-12-31T00:00:00Z\")) : ...",
"conditionalRuleGuards": ["(Calc.eval(\"Method\", source, 6).compareTo(new java.math.BigDecimal(\"1\")) == 0 ? ruleRow.CashAccount : ruleRow.BankAccount)"]

The change

The glue now carries each of those as a reading - a kind-tagged map of the authored facts - and the Java appears one layer out:

"reading":      { "kind": "calc", "text": "-(Net - Discount)", "owner": "source", "scale": "2" },
"guardReading": { "kind": "calcCompare", "owner": "source", "property": "Vat", "equal": false, "text": "0" },
"attachLanguage": { "kind": "languageFrom", "owner": "attachLanguageSource", "property": "Code" },
"fileName": { "kind": "concat", "parts": [ { "kind": "concat", "parts": [ { "kind": "part", "of": { "kind": "read", "owner": "document", "property": "Number" } }, ... ], "forceString": true }, { "kind": "string", "text": ".pdf" } ] },
"due": { "kind": "due", "property": "ValidUntil", "shape": "date" },
"guardTerms": [ { "owner": "entity", "property": "Internal", "equal": false, "type": "boolean", "value": "true", "numericKey": false } ]
  • Readings (engine-intent, package-private) is the one factory every emitting site spells a reading through; JavaExpressions (ide-template, beside JavaLiterals) is the one renderer - a closed vocabulary of 21 kinds, each rendering exactly what the generator used to write, so the generated handlers are byte-identical (qualifiedExpression spells Calc fully qualified for the transition controller, which imports none).
  • The keys: reading on every assignment (create-from fields / items / computed lines, schedule generate fields / children / natural-key property terms, posting header and item cells, posts: cells, prompt: conversions), guardReading on a derived row and a transition, guardTerms on every event binding (triggers, notifications, integrations, outbound, resolves, waits - the very typed terms CheckSupport.conditionTerms produces for the model's checks, or the untyped spelling-inferred terms for the three sites that never had an entity to type against), due on a timer loader, attachLanguage / attachFileName on every notify site, language / fileName on a snapshot. conditionalRuleGuards is no longer carried at all: the binder derives it from the cells whose reading is a ruleCase.
  • Every binder falls back to the rendered key an older .glue carries (expr, guard, guardExpr, guardExpression, dueExpression, the attach / language / file-name expressions), so an unregenerated project renders byte-identically - ModelGenerationIT's pre-split orders.glue still exercises that path.
  • Three rules the shape obeys because the glue is serialized: keys in one fixed order per kind (intent: .glue terms built with Map.of serialize in a JVM-salted key order - a regen on a fresh container flips property/expr inside genUnique with no semantic change #7130), every number as text ("scale": "2" - the glue is read back through a parser that types every number as a Double), and an optional key omitted rather than written as null (Gson drops a null map entry).
  • The Java renderers this leaves without a caller are removed: NotificationSupport.guard(...) / literalToJava, CheckSupport.condition / javaCondition / comparison / numericComparison / access, PostSetSupport.expression, GlueIntentGenerator's promptConversion / postSetExpr / conditionalRuleExpression / literalExpression / calcExpression / stringCellExpression / computedCellExpression. The templates are unchanged - they read the rendered keys the binder now writes.

Deliberately not changed

The untyped guards of a trigger, a wait and a register lookup stay untyped (a number term type renders the bare spelling), so Objects.equals(entity.Status, 3) is unchanged; typing them against the entity would change a to-one guard's comparison and is a separate decision. Keys outside the issue's inventory that carry no Java package (toExpression / subjectExpression / bodyExpression, urlExpression, matchSourceExpression, statusMatchExpression, an arrival's mapFields) are untouched.

Verified

  • engine-intent unit suite green (1334 tests, of which the ~100 glue assertions that used to read expr / guard / guardExpression now render the reading through the test helper GlueRendering and still assert the same Java); ide-template green (192, incl. the new JavaExpressionsTest over every kind and the GlueGeneratorTest fallbacks).
  • IntentEngineIT green (81) - its glue assertion now pins that none of the ten rendered keys and no Calc.eval appear anywhere in a generated glue, and that the guards and the snapshot language travel as readings.
  • IntentEmissionCoverageIT green - it compiles and runs the generated handlers.
  • ModelGenerationIT green - the backward-compatible path, run rather than only unit-tested.
  • mvn -T 1C formatter:validate clean with the formatter cache wiped; the release-profile javadoc build of both modules clean.

Not verified: no run against a deployed instance, and no fleet regen - the fleet's .glue files change shape on their next regen (the keys above), which is the intent.

Fixes #7425

🤖 Generated with Claude Code

…ped values, the Calc guards, the print SDK calls and the ternaries (#7425)

After the literal and builder-chain tiers of #7406 moved to JavaLiterals, the
.glue still carried Java SOURCE in every expression key: a mapped value's
`expr` (a create-from's fieldAssignments / itemFieldAssignments / itemLines,
a schedule's genFieldAssignments / genChildren / genUnique, a posting's
headerAssignments / itemRows, a posts: rule's assigns, a prompt: field's
conversion), every guard (a row's `guard`, a transition's `guardExpr`, the
event axis's `guardExpression`), the print SDK calls (`attachLanguageExpression`
/ `attachFileNameExpression`, a snapshot's `languageExpression` /
`fileNameExpression`), a timer's `dueExpression` and the `conditionalRuleGuards`
ternaries - ternaries, Calc.eval invocations, boxed equalities and string
concatenations over SDK calls in the process description every template reads.

Each is now a reading: a `kind`-tagged map of the authored facts, built through
the package-private `Readings` factory and carried under `reading`,
`guardReading`, `guardTerms` (the typed terms CheckSupport already produces,
or the untyped spelling-inferred ones for a trigger / wait / register lookup),
`due`, `attachLanguage` / `attachFileName`, `language` / `fileName`. The Java
appears one layer out, in ide-template's new `JavaExpressions` - a closed
vocabulary (read, hop, local, string, number, boolean, null, now, calc, text,
concat, negate, convert, calcCompare, ruleCase, defaultLanguage, languageFrom,
part, first, numberOrId, due) rendering exactly what the generator used to
write, so the generated handlers are byte-identical. GlueGenerator renders at
bind time and derives `conditionalRuleGuards` from the cells carrying a
ruleCase; every binder falls back to the rendered key an older .glue carries,
so an unregenerated project renders byte-identically. The now-unused Java
renderers (NotificationSupport.guard, CheckSupport.condition/javaCondition,
PostSetSupport.expression, FileNameSupport's helper calls) are gone.

Verified: engine-intent unit suite (1334) and ide-template (192) green;
IntentEngineIT (its glue assertion now pins that none of the rendered keys and
no Calc.eval appear), IntentEmissionCoverageIT (compiles and runs the generated
handlers) and ModelGenerationIT (regenerates from a pre-split orders.glue, the
fallback path) green; formatter:validate clean with the cache wiped; the
release-profile javadoc build of both modules clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
iliyan-velichkov and others added 13 commits September 23, 2026 16:42
…t/alternative (#7488) (#7489)

A notify block's body was always one text/plain part, so an application
mailing people outside the organisation had no declared way to send a
styled message short of replacing the step with a hand-written delegate.

A notify block now takes an optional `html:` beside `body`, at every call
site (notifications[], schedules[].notify, transitions[].notify, a
serviceTask's args.notify, inside a forEach):

- parser: html requires body and may not be blank; its placeholders go
  through the same escalation / record-scope / cross-model checks body does
- generator: an htmlExpression resolved by the same resolver as the body,
  every interpolated value (deep links included) wrapped in the new SDK
  org.eclipse.dirigible.sdk.mail.Html.escape; the authored markup literal.
  Emitted only when declared, so a block without html is byte-identical
- templates: Send / Transition / Notification / Job add a text/html part
- MailClient: a text/plain and a text/html part are wrapped in one
  multipart/alternative (plain first) inside the mixed container, in the
  position of the first text part; a single text part is added as before.
  An html part without a charset is now sent as UTF-8

Verified: unit suites of api-modules-java, api-mail, ide-template and
engine-intent (new HtmlTest, MailClientAlternativeTest, GlueNotifyHtmlTest);
IntentEmissionCoverageIT (compiles and runs the generated html parts at all
four call sites, incl. a fan-out) and IntentEngineIT; formatter:validate
with the cache wiped; release-profile javadoc on the api modules.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…7486)

Bumps com.sap.cloud.db.jdbc:ngdbc from 2.30.7 to 2.30.11.

---
updated-dependencies:
- dependency-name: com.sap.cloud.db.jdbc:ngdbc
  dependency-version: 2.30.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…7490)

Bumps [com.icegreen:greenmail](https://github.com/greenmail-mail-test/greenmail) from 2.1.13 to 2.1.14.
- [Release notes](https://github.com/greenmail-mail-test/greenmail/releases)
- [Commits](greenmail-mail-test/greenmail@release-2.1.13...release-2.1.14)

---
updated-dependencies:
- dependency-name: com.icegreen:greenmail
  dependency-version: 2.1.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…7485)

Bumps software.amazon.awssdk:bom from 2.55.0 to 2.55.1.

---
updated-dependencies:
- dependency-name: software.amazon.awssdk:bom
  dependency-version: 2.55.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps `selenide.version` from 7.18.1 to 7.18.2.

Updates `com.codeborne:selenide` from 7.18.1 to 7.18.2
- [Release notes](https://github.com/selenide/selenide/releases)
- [Changelog](https://github.com/selenide/selenide/blob/main/CHANGELOG.md)
- [Commits](selenide/selenide@v7.18.1...v7.18.2)

Updates `com.codeborne:selenide-core` from 7.18.1 to 7.18.2
- [Release notes](https://github.com/selenide/selenide/releases)
- [Changelog](https://github.com/selenide/selenide/blob/main/CHANGELOG.md)
- [Commits](selenide/selenide@v7.18.1...v7.18.2)

---
updated-dependencies:
- dependency-name: com.codeborne:selenide
  dependency-version: 7.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.codeborne:selenide-core
  dependency-version: 7.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… pattern: and expect the process delete guard (#7411) (#7481)

The generated <module>.test manifest describes what the app promises; three of those
promises were missing from it, so the generic REST/UI flows reported a correct module
as broken.

- A `number:` field is the platform's: the DAO stamps it and then PRESERVES the column,
  so the runner - which picks the first long string field as the value it flips to prove
  an update landed - wrote "APPTEST-X-UPD" and read "APPTEST-X" back on every document
  module. The EDM already marks that field `isReadOnlyProperty` (as it does a uuid), and
  the manifest now reads both attributes back off the `.model` rather than re-deriving
  them, so a field with no editable input in the generated form has none in the manifest
  either. The handle additionally skips a `pattern:` field: the flow's own suffix would
  break the shape the controller enforces.
- A `pattern:` field (an authored regex, or the address regex behind `format: email`)
  travels as the field's `pattern`, and the sample generator trades the marker value for
  the first candidate that matches it and fits the declared length - an address-shaped
  value, the alphanumeric token, a numeric code. A pattern nothing matches keeps the
  marker, so the controller's own 400 names the field and the regex.
- An entity whose process declares `whenDeleted: refuse` (#7074) carries
  `deleteGuardedByProcess`. The REST flow asserts what actually happened - a refused
  delete leaves the record served, a clean one 404s (the instance may already have
  finished) - and the UI walk stops before the delete leg, whose only outcome is a toast.

Verified: the engine-intent unit suite (1331 tests) including a new case over the exact
`.model` attributes the EDM emits for those three; `IntentEngineIT
#generate_writes_all_model_files_into_the_workspace_project`, which now asserts the
manifest carries the email regex end to end through the real `.model` read-back and
that a numeric display format does not. The JS half was exercised under node against
the email / numeric / alphanumeric / unsatisfiable / unparsable patterns; there is no
JS test harness in this repo to keep that in.

Fixes #7411

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…er child (#7448) (#7480)

A capacity-bearing roll-up stamps an overdraw guard on its CHILD entity. The
entity carried a SINGLE `rollupGuard`, so a junction - both of whose parents are
capacities, the case the guard exists for - got only the guard of whichever
declaration came first. The other roll-up generated its sum column, balance
column and four handlers normally, with nothing in the output saying its guard
was missing: the model read as guarded, the runtime was not, and 6.00 could be
allocated out of a 5.00 payment.

The guard is now a list. `EdmIntentGenerator` collects one guard per
capacity-bearing roll-up naming the entity (deduplicating an identical one) and
emits `rollupGuards`; `Repository.java.template` renders a block per guard on
both write paths - each block scopes its own locals, so the names do not collide
- and imports each DISTINCT local parent once. A `.model` written before this
carries the singular key; `ModelGenerator` normalizes it into the list, so an
application regenerated from an old `.model` keeps the guard it had, and
`transform-edm.js` still parses the singular attribute back into an object.

Verified:
- engine-intent unit suite 1333/1333, including EdmRollupGuardTest's new cases:
  a junction carries both guards, declaration order decides only their order,
  two roll-ups over the same relation and capacity share one guard.
- RollupGuardCrossModelTemplateIT 6/6: a local and a foreign guard render side
  by side, and a shared local parent is imported exactly once.
- IntentEmissionCoverageIT green: the fixture's PledgePayment now draws on its
  pledge's total AND a fund's budget, the generated app COMPILES with both
  guards, and over REST the second capacity refuses the overdraw (400
  "PledgeFund capacity exceeded") that used to return 200.
- EdmModelRoundTripIT 2/2: rollupGuards survives the .edm save-regenerate cycle.
- ide-template unit suite 176/176; formatter:validate green with the cache wiped.

Fixes #7448

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…, pinned and documented (#7450) (#7479)

#7450 reports a gated refusal crossing an async delegate boundary coming back as 500
instead of the 400 the in-thread path uses. Measured, the boundary is not what decides
it - the exception TYPE is, and it decides the same way on both sides:

- `ClientValidationFailure` recognises `org.eclipse.dirigible.sdk.db.ValidationException`
  (and subclasses) anywhere in the cause chain, and `BpmInboxEndpoint` answers 400 with
  the authored message; the `ControllerInvoker` maps the same type the same way.
- Anything else is a server fault by construction and stays a 500 - the platform has
  nothing else to tell a refusal from a breakage by.

The reported shape refuses with an `IllegalStateException`
(`base-inventory`'s `NegativeStockGuard`), which is a 500 in the caller's own thread
too. The fleet comparisons that answered 400 were `checks:` gates, i.e.
`ValidationException`. So there is no status to re-raise differently; what was missing
is that the rule is nowhere stated for the one author who has to obey it, and nothing
pinned it end to end.

- `java-assistant-guide.md` gains the `JavaDelegate` bullet next to the `@Controller`,
  `JobHandler` and `MessageHandler` ones: a refusal the person can act on must be a
  `ValidationException`, never an `IllegalStateException` or a bare `RuntimeException`,
  because the same rule authored as a `checks:` gate already answers 400 and one rule
  must not change its status code because of where it is written.
- `IntentCheckGateAcrossADelegateIT` gains the delegate's OWN refusal on the same
  synchronous stretch: as a `ValidationException` the completion answers 400 with the
  delegate's sentence, as an `IllegalStateException` the very same sentence is a 500 -
  both roll back whole (document DRAFT, task still the poster's), and the next attempt
  with nothing to refuse posts.

Verified: `IntentCheckGateAcrossADelegateIT` 2/2 green (the new method fails on the
assertion that names the type, not on the boundary), `engine-intent` unit suite 1330
green.

Fixes #7450

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…7483)

Bumps [org.apache.groovy:groovy-bom](https://github.com/apache/groovy) from 5.1.2 to 6.0.0.
- [Commits](https://github.com/apache/groovy/commits)

---
updated-dependencies:
- dependency-name: org.apache.groovy:groovy-bom
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [com.microsoft.graph:microsoft-graph](https://github.com/microsoftgraph/msgraph-sdk-java) from 6.69.0 to 6.70.0.
- [Release notes](https://github.com/microsoftgraph/msgraph-sdk-java/releases)
- [Changelog](https://github.com/microsoftgraph/msgraph-sdk-java/blob/main/CHANGELOG.md)
- [Commits](microsoftgraph/msgraph-sdk-java@v6.69.0...v6.70.0)

---
updated-dependencies:
- dependency-name: com.microsoft.graph:microsoft-graph
  dependency-version: 6.70.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… (#7497)

* ui: collapsible Projects/Assistant side panels in the Workbench (#7492)

The Workbench's left Projects pane and right Assistant pane took a large
fixed slice of horizontal width and could not be reclaimed for the editor:
the perspective overrode the shell defaults with leftPaneMinSize 355 /
rightPaneMinSize 340, and the panel chevrons only collapse the accordion
contents, not the pane's width.

Change:
- perspective-workbench/js/workbench.js: drop the 355/340 minimum-width
  floors (to the platform default 0). The split library collapses a pane
  only down to its minSize, so a non-zero floor would keep a "collapsed"
  panel that wide and reclaim nothing; with no floor the panes can be
  dragged narrow and collapsed fully.
- platform-core layout.js: toggleLeftPane()/toggleRightPane() +
  isLeftPaneCollapsed()/isRightPaneCollapsed() driving the existing
  splitPanesState.side COLLAPSED mechanism (collapse to 0 + restore last
  size, iframes preserved); the collapsed state is persisted in the
  layout's localStorage key and restored on load.
- platform-core layout-hub.js: toggleLeftPane/onToggleLeftPane (+ right)
  so the shell header (parent frame) can drive the layout in the
  perspective iframe over the message hub.
- platform-core shell.js + header.html: two always-visible header toggle
  buttons (a collapsed pane has no width to host its own restore control).

Verified: full quick-build reactor install; the app boots clean and serves
the updated resources; formatter:validate is BUILD SUCCESS (cache wiped);
new WorkbenchSidePaneToggleIT (2 methods) is green headless - both panes
collapse to ~0 and restore via the header toggles, and a collapsed pane
stays collapsed across a reload.

Fixes #7492

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ui: collapse the side panels from their header chevron, not header buttons (#7492)

Reworks the side-panel collapse UX per review feedback: drop the two
shell-header toggle buttons and drive the collapse from the existing
chevron on the first panel of each side pane (Projects / Assistant).

- The first panel's chevron collapses the whole side pane to a slim rail
  (min-size 41px) that reclaims the editor width and keeps the chevron
  visible to expand it again; the sub-section chevrons (Import/Search/
  Debugger) keep collapsing their own sections. The chevron points the
  way it acts and mirrors per side.
- split.js: a collapsed pane restores by its stored lastSize (works for a
  0 or a rail collapse), an optional expand-size (px) reopens it at a
  fixed width, and the reopen takes space only from the adjacent center
  pane so the opposite side pane does not move.
- layout: side panes reopen at a fixed 350px (sidePaneExpandSize); the
  collapsed state still persists in the layout localStorage key.
- Removed the header buttons, their shell.js handlers and the LayoutHub
  toggle methods/listeners (collapse is now driven in-frame).

Verified: formatter:validate BUILD SUCCESS; WorkbenchSidePaneToggleIT
(2 methods) green headless - each pane collapses to the rail via its
chevron and reopens at 350px, the opposite pane's width is unchanged
across both toggles, and a collapsed pane survives a reload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ped values, the Calc guards, the print SDK calls and the ternaries (#7425)

After the literal and builder-chain tiers of #7406 moved to JavaLiterals, the
.glue still carried Java SOURCE in every expression key: a mapped value's
`expr` (a create-from's fieldAssignments / itemFieldAssignments / itemLines,
a schedule's genFieldAssignments / genChildren / genUnique, a posting's
headerAssignments / itemRows, a posts: rule's assigns, a prompt: field's
conversion), every guard (a row's `guard`, a transition's `guardExpr`, the
event axis's `guardExpression`), the print SDK calls (`attachLanguageExpression`
/ `attachFileNameExpression`, a snapshot's `languageExpression` /
`fileNameExpression`), a timer's `dueExpression` and the `conditionalRuleGuards`
ternaries - ternaries, Calc.eval invocations, boxed equalities and string
concatenations over SDK calls in the process description every template reads.

Each is now a reading: a `kind`-tagged map of the authored facts, built through
the package-private `Readings` factory and carried under `reading`,
`guardReading`, `guardTerms` (the typed terms CheckSupport already produces,
or the untyped spelling-inferred ones for a trigger / wait / register lookup),
`due`, `attachLanguage` / `attachFileName`, `language` / `fileName`. The Java
appears one layer out, in ide-template's new `JavaExpressions` - a closed
vocabulary (read, hop, local, string, number, boolean, null, now, calc, text,
concat, negate, convert, calcCompare, ruleCase, defaultLanguage, languageFrom,
part, first, numberOrId, due) rendering exactly what the generator used to
write, so the generated handlers are byte-identical. GlueGenerator renders at
bind time and derives `conditionalRuleGuards` from the cells carrying a
ruleCase; every binder falls back to the rendered key an older .glue carries,
so an unregenerated project renders byte-identically. The now-unused Java
renderers (NotificationSupport.guard, CheckSupport.condition/javaCondition,
PostSetSupport.expression, FileNameSupport's helper calls) are gone.

Verified: engine-intent unit suite (1334) and ide-template (192) green;
IntentEngineIT (its glue assertion now pins that none of the rendered keys and
no Calc.eval appear), IntentEmissionCoverageIT (compiles and runs the generated
handlers) and ModelGenerationIT (regenerates from a pre-split orders.glue, the
fallback path) green; formatter:validate clean with the cache wiped; the
release-profile javadoc build of both modules clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

engine-intent: the .glue's EXPRESSION keys still carry Java - expr, the Calc guards, the print SDK calls and the ternaries (#7406 follow-up)

4 participants