Repository navigation
intent: emptyText: on a field names what an empty value reads as - "No limit" or "Never" instead of a hidden row or an em dash (#8605) - #8606
Merged
Conversation
…o limit" or "Never" instead of a hidden row or an em dash (#8605) Cause: a generated page had two ways to show an empty field value and neither could say what the emptiness means. The Details rail hid the row (and the whole block when every value was empty, #7522); every other surface printed an em dash. A reader could not tell "no limit" from "not loaded", and the only workaround was a sentinel value every rule, check and report had to know. Change: a new field key `emptyText: <text>`. - FieldIntent.emptyText; IntentParser.validateEmptyText refuses it blank, on a boolean, a uuid, the primary key, a required field, a number:, placement: hidden, an aggregate, the document title and a chat messageBody. On a relation it stays an unknown key. - EdmIntentGenerator emits the trimmed scalar widgetEmptyText (also on a related: register column); ModelParameterProcessor escapes widgetEmptyTextJs and copies emptyText/emptyTextJs onto a register column; ModelTranslations seeds t.<DATA_NAME>_empty beside the label. - Runtime: one helper, basePage.orEmptyText(value, shown, tkey, text); detailPanel.cellValue wraps its result with it (composition panels and related: registers). - Templates: the six view templates' valueDisplay (manage form, power/my/partner document, my/partner form) print the text, valueGate keeps only the role and visibleWhen terms so the rail row always shows, hasDetailValues counts the field; text/email/url/tel/number/textarea inputs get it as placeholder; the subtitle and the document summary line, the manage and plain list cells and preview sheets, the my/partner lists' display() and the detail/related register columns carry it. Every change is guarded by widgetEmptyText, so a model without the key generates byte-identical output. Exports keep ''. - Docs: assistant guide, intent-dsl-features.md, harmonia-ui.md. Verified: formatter:validate (cache wiped); engine-intent 2121 and ide-template 359 unit tests green, incl. the new FieldEmptyTextIntentTest (12), EdmFieldEmptyTextTest (3) and three ModelParameterProcessorTest cases; IntentEmissionCoverageIT (TeamNote.reviewBy: emission, catalog, no emptyText code on any other page, value stays null), FieldEmptyTextHarmoniaIT (new, browser), FieldPlacementHarmoniaIT and RecordHeaderHarmoniaIT green. The 14 changed templates rendered before/after without the key are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 13, 2026
Open
…ue macros (#8605) Cause: the structural guard matched the six view templates' #valueDisplay and #valueGate macros by their exact text; #8605 wraps both in an #if($property.widgetEmptyText) branch, so the smoke gate's everyReadOnlyCardPrintsThroughTheSharedHelper failed although every value still prints through fieldDisplay()/fieldText() and a role-scoped field still asks canSee(). Change: the guard asserts the new macro shape - the #{else} branch is the unchanged one, the emptyText branch wraps the same formatted value in orEmptyText - and that both branches of the gate ask canSee(). A new GraalJS test runs basePage.orEmptyText: null / undefined / '' / [] read as the text, the text resolves through its catalog key, a value (zero included) passes through. Verified: HarmoniaFieldDisplayIT 9/9 locally; formatter:validate (cache wiped). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cause
A generated page had two ways to show a field whose value is empty, and neither could say what the empty value means. The Details rail hid the row (and the whole Details block when every value in it was empty, #7522); every other surface printed "—". A reader could not tell "no limit" from "not loaded" or "not filled in yet", and the only workaround was a sentinel value (
-1,9999-12-31) that every rule, check and report then had to know about.Change
A new field key, as the issue designs it:
FieldIntent.emptyText;IntentParser.validateEmptyTextrefuses it blank, on aboolean, auuid, the primary key, arequiredfield, anumber:,placement: hidden, anaggregate, the document title and a chatmessageBody, each naming the entity and field. On a relation it stays an unknown key (pinned by a test).EdmIntentGeneratoremits the trimmed scalarwidgetEmptyText(both twins) and carries it on arelated:register column;ModelParameterProcessorescapeswidgetEmptyTextJs(templates: an authored field label: is written raw into the Harmonia report JS and the form T() fallback - an apostrophe blanks the page (#7207 class) #7294) and copiesemptyText/emptyTextJsonto a register column;ModelTranslationsseedst.<DATA_NAME>_emptybeside the label.basePage.orEmptyText(value, shown, tkey, text)(empty = null / undefined /''/[]);detailPanel.cellValuewraps its result with it, so composition panels andrelated:registers follow (a cross-project register has noemptyTkeyand prints the authored text).#valueDisplay(manage form, power/my/partner document, my/partner form) print the text;#valueGatekeeps only thecanSee/visibleWhenterms, so the rail row always shows, andhasDetailValuescounts the field whenever the caller may see it; the text/email/url/tel/number/textarea inputs get it as:placeholder(date-family pickers unchanged); the subtitle (subtitleParts) and the document summary line; the manage and plain list cells and preview sheets; the my/partner lists'display(); the detail and related register columns.exportCellis unchanged, so exports keep''.widgetEmptyText, andwidgetEmptyText/widgetEmptyTextJs/ the_emptycatalog entry are emitted only when authored..claude/docs/intent-dsl-features.md,.claude/docs/harmonia-ui.md.Verification
mvn -T 1C formatter:validateafter wiping the formatter cache - BUILD SUCCESS.FieldEmptyTextIntentTest(12),EdmFieldEmptyTextTest(3) andModelParameterProcessorTest#anEmptyTextIsEscapedForTheTemplates/#anEmptyTextSeedsItsCatalogEntryBesideTheLabel/#aRelatedColumnCarriesTheEmptyText;ConsumedAttributesAuditTestgreen.IntentEmissionCoverageIT(newTeamNote.reviewBy: the record page wraps it, the rail has no emptiness gate for it, other surfaces name the key, the en-US catalog holdsTEAM_NOTE_REVIEW_BY_empty: Not scheduled, no page of a field without the key carries any emptyText code, and a created record reads it backnull), the newFieldEmptyTextHarmoniaIT(record page rail "Loan limit / No limit" with the Details block shown, "Never" / "Not set" in the main column, "Not returned" in the composition panel, the list cells, the edit form's placeholder, a filled member printing its values with no emptyText, REST answeringnull),FieldPlacementHarmoniaIT,RecordHeaderHarmoniaIT- all green.HarmoniaFieldDisplayIT(smoke gate) matched the value macros by their exact text and failed on the first CI run; it now asserts the new shape (the#{else}branch is the unchanged one, both gate branches askcanSee()) and runsbasePage.orEmptyTextin GraalJS - 9/9 locally.origin/masterand from this branch with a context carrying nowidgetEmptyText(fresh Velocity engine per template) produce identical output.Not verified / not covered:
exportCellis unchanged code.FieldIntent, which is not a published SDK type.cellDisplay) and the my/partner child tables (displayChild). Both still print "—"; changing them would also touch every document model's output.Documentation
Fixes #8605
🤖 Generated with Claude Code