feat: support optional statements inside statement groups#554
Merged
Conversation
Individual members of a nt:GroupedStatement can now be marked nt:OptionalStatement: the member's triple is emitted only if its placeholders are filled, while the group's other members keep their own requirements. Design doc: docs/optional-statements-in-groups.md. - Template parsing: an all-optional group is normalized to a group-level optional with a warning (a group needs at least one required member, otherwise matching would be vacuous and repetition detection would not terminate); out-of-scope member flags (repeatable, nested group) are ignored with a warning instead of half-rendering. - Publish path: hasEmptyElements/addTriplesTo skip empty optional members instead of blocking or dropping the whole group. - Form UI: member-optional fields are non-required; a per-line "(optional)" mark is rendered inline via a new part-label slot, leaving the group-level mark untouched. - Fill/unification: assignParts gets a skip branch for optional members (match-over-skip keeps fills maximal), matches() requires at least one consumed statement to guard the repetition loop, and skipped member rows are hidden in read-only rendering while staying editable in derive/update contexts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 5.6.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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.
Implements member-level optionality inside statement groups per the design doc
docs/optional-statements-in-groups.md(merged in #553's sibling d227dbc): a member of ant:GroupedStatementmarkednt:OptionalStatementmay stay empty — its triple is then dropped — while the group's other members keep their own requirements. Repeatable members and nested groups remain out of scope (the group is the unit of repetition) and are now explicitly ignored with a warning.Changes
Template): an all-optional group is normalized to a group-level optional with a warning — a group needs ≥1 required member, otherwise matching would be vacuous and repetition detection would not terminate. Out-of-scope member flags (nt:RepeatableStatement, nestednt:GroupedStatement) are stripped with a warning.StatementItem):hasEmptyElements/addTriplesToskip empty optional members instead of blocking (required statement) or silently dropping the whole group (optional statement). Both gated onisGrouped()so top-level optional statements behave exactly as before.ValueItem,StatementPartItem.html,style.css): member-optional fields are non-required (per repetition — unlike group-level optionality, which turns off when repeated); a per-line "(optional)" mark renders inline after the statement via a newpart-labelslot, leaving the group-level mark and its repetition toggling untouched.StatementItem):assignPartsgets a skip branch for optional members, tried only after all candidate statements fail, so fills stay maximal (match-over-skip).matches()additionally requires at least one consumed statement, guarding the trial-repetition loop against vacuous matches. Skipped members are tracked inunmatchedParts; their rows are hidden in read-only rendering and shown as empty editable fields in derive/update contexts.Backwards compatibility
Existing templates parse and behave identically (the new paths only activate on the member-level flag, which nothing honored before). Old parsers treat member-optional statements as required — stricter but never invalid; fill of a nanopub legitimately omitting the optional triple falls back to the generic statement display there. All 196 live templates in
AllAssertionTemplatesLoadingTeststill load.Tests
OptionalGroupMemberTest: parsing, all-optional normalization, out-of-scope flag stripping, memberless-group regression.OptionalGroupMemberPublishTest: empty optional member dropped, filled member emitted, empty required member still fails.OptionalGroupMemberFillTest: group matches a nanopub omitting the optional triple (skipped member tracked for row hiding), full match, required-member-missing non-match, repeatable group with per-repetition optionality, termination guard (@Timeout).All test fixtures are built programmatically (no new network dependencies). Full suite: 771 tests, 0 failures.
Verified additionally against the live "Annotation as a discourse contribution" template (
…RADVnxF4…): its selector group publishes correctly withoa:prefix/oa:suffixomitted. (That template independently needs republishing withnt:LocalResourcedeclarations forstatement/selector/anchor— unrelated to this PR.)🤖 Generated with Claude Code