Skip to content

intent: the model stops carrying Java - a check's literal and condition become data - #7407

Merged
delchev merged 1 commit into
masterfrom
issue-7405-neutral-check-model
Sep 17, 2026
Merged

delchev merged 1 commit into
masterfrom
issue-7405-neutral-check-model

Conversation

@delchev

@delchev delchev commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Summary

checks[] was the one place in the model that held Java source. EdmIntentGenerator compiled the comparison literal and the condition and wrote the result into the .edm — an artefact an author opens in the Entity Data Modeler — and on into the .model and .model.gen:

"literal": "new java.math.BigDecimal(\"0\")"
"guard":   "!java.util.Objects.equals((hop0 == null ? null : hop0.Status), 1)"

The model now carries the same facts as data, and the Java is rendered a layer out, by JavaLiterals:

"value": { "kind": "number", "text": "0" }
"when":  [ { "owner": "hop0", "property": "Status", "equal": false,
             "type": "integer", "value": "1", "numericKey": false } ]

Why

The .model is the shared, language-neutral description every template reads, Java and JavaScript alike — which is why JavaLiterals has a JsonLiterals twin, and why a property default has always crossed this boundary the other way round: the model carries dataDefaultValue, ModelParameterProcessor.resolveDefaultValueLiterals derives dataDefaultValueJavaLiteral. checks was the only feature that skipped that split, while:

  • emitting the neutral shape for its own UI half 250 lines away in the same method — a forbidWhen produced "masterGuard": [{"property": "Status", "equal": false, "value": "1"}] next to a Java string for the identical condition;
  • letting the template write the comparison's left-hand side in Java itself (EntityController.java.template:963), importing only the right-hand side pre-baked, for no reason.

What changed

Verification

  • IntentEmissionCoverageIT passes unchanged — it asserts on the generated controller text, not on the model, so it is the end-to-end oracle that the emitted Java is byte-identical. It still finds !(new java.math.BigDecimal(entity.Paid.toString()).compareTo(new java.math.BigDecimal("0")) >= 0) and the to-one guard shapes exactly as before.
  • The four EdmIntentGeneratorTest cases that asserted on the Java-carrying model now pin the neutral model AND the Java rendered from it, so the two halves cannot drift apart.
  • 12 new JavaLiteralsTest cases cover both renderers: number / moment / temporal, the ISO offset in both directions, the hop null guard, the numericKey comparison-by-value (intent: requiredWhen's when: guard NPEs the parser on a typeless field, refuses Integer/String spellings, and types a cross-model to-one as integer while its FK is Long #7237), the escape, and every unrenderable input yielding null rather than a weaker guard.
  • 2 new ModelParameterProcessorTest cases cover the derivation and its absence.
  • Full engine-intent (1315) and ide-template (158) unit suites green.

Test plan

  • engine-intent + ide-template unit suites
  • IntentEmissionCoverageIT (the generated-Java oracle) — green, 178s
  • formatter:format applied
  • CI

Downstream

The model shape changes, so every module regenerates. In the BusinessIntents fleet that is 52 occurrences across 18 of 80 modules; no .intent changes — the authored layer was already clean. The acceptance test there is that grep -rlE 'java\.(math|lang|util|time)\.[A-Z]' --include='*.model' --include='*.edm' . returns nothing.

Fixes #7405

🤖 Generated with Claude Code

…on become data (#7405)

`checks[]` was the one place in the model that held Java SOURCE. `EdmIntentGenerator`
compiled the comparison literal and the condition and wrote the result into the `.edm` -
an artefact an author opens in the modeler - and on into the `.model` and `.model.gen`:

    "literal": "new java.math.BigDecimal(\"0\")"
    "guard":   "!java.util.Objects.equals((hop0 == null ? null : hop0.Status), 1)"

The `.model` is the shared, language-neutral description every template reads, Java and
JavaScript alike, which is why `JavaLiterals` has a `JsonLiterals` twin and why a
property default has always crossed this boundary the other way round: the model carries
`dataDefaultValue`, `ModelParameterProcessor` derives `dataDefaultValueJavaLiteral`.
`checks` was the only feature that skipped it - while emitting the neutral shape for its
own UI half 250 lines away, and while the template already wrote the comparison's
left-hand side in Java itself.

The model now carries the same facts as data:

    "value": { "kind": "number", "text": "0" }
    "when":  [ { "owner": "hop0", "property": "Status", "equal": false,
                 "type": "integer", "value": "1", "numericKey": false } ]

- `CheckSupport` keeps every refusal - the parser rejects an unrenderable literal while
  the author is generating - and returns the typed reading instead of Java. It renders
  the Java for exactly one caller that still needs it, the declarative glue lists, from
  the very same terms, so the two outputs cannot disagree (#7406 takes the glue).
- `JavaLiterals` gains `compareLiteralExpression` and `conditionExpression`;
  `ModelParameterProcessor.splitChecks` calls them in the loop that already derives
  `messageJavaLiteral`, emitting `literalJavaExpression` / `guardJavaExpression`.
- The DAO repository and the three REST controllers read the derived keys.
- `ResolvePathSupport.Path` gains `owner` / `property` - `expression` taken apart, for a
  caller that must describe a read as data rather than emit it as code.

The generated Java is byte-identical: `IntentEmissionCoverageIT`, which asserts on the
generated controller text rather than on the model, passes unchanged, and the four
`EdmIntentGeneratorTest` cases now pin the neutral model AND the Java rendered from it so
the two halves cannot drift.

Fixes #7405

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev
delchev merged commit 8cbb095 into master Sep 17, 2026
10 checks passed
@delchev
delchev deleted the issue-7405-neutral-check-model branch September 17, 2026 06:59
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: checks[] writes Java source into the .edm/.model - the literal and the guard belong to the template layer, through JavaLiterals

1 participant