You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/08/25 11:23:44 UTC
(causeway) 07/13: plans rich GraphQL member metadata
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch htmx
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit c5cac5ac32fe7002d809aac16e7daa58725869b2
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Aug 25 06:56:06 2026 +0100
plans rich GraphQL member metadata
---
.../.openspec.yaml | 2 +
.../add-rich-graphql-member-metadata/design.md | 97 +++++++++++++++++++
.../add-rich-graphql-member-metadata/proposal.md | 34 +++++++
.../specs/rich-graphql-member-metadata/spec.md | 107 +++++++++++++++++++++
.../add-rich-graphql-member-metadata/tasks.md | 67 +++++++++++++
5 files changed, 307 insertions(+)
diff --git a/openspec/changes/add-rich-graphql-member-metadata/.openspec.yaml b/openspec/changes/add-rich-graphql-member-metadata/.openspec.yaml
new file mode 100644
index 00000000000..f161d5cc476
--- /dev/null
+++ b/openspec/changes/add-rich-graphql-member-metadata/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-08-16
diff --git a/openspec/changes/add-rich-graphql-member-metadata/design.md b/openspec/changes/add-rich-graphql-member-metadata/design.md
new file mode 100644
index 00000000000..41ff8a7661f
--- /dev/null
+++ b/openspec/changes/add-rich-graphql-member-metadata/design.md
@@ -0,0 +1,97 @@
+## Context
+
+Current rich wrappers expose datatype, value, hidden, disabled, choices, autocomplete, validation, and invocation behavior.
+Their GraphQL field descriptions may carry a Causeway description or friendly label, but a client cannot reliably distinguish the two.
+
+The executable effective-grid probe found names, descriptions, action positions, property attributes, icon classes, field sets, columns, and unreferenced placement in the XML resource.
+Adding those structural fields to rich wrappers would recreate part of the grid metamodel and enlarge an already large generated schema.
+Resource `fileAccept` metadata is already exposed for Blob and Clob property reads and resource action parameters, so this change must preserve rather than duplicate that contract.
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- Expose canonical friendly names and descriptions independently on property, collection, action, and action-parameter wrappers.
+- Expose a bounded set of local text-editor constraints on property and action-parameter wrappers.
+- Support standalone components and fallback rendering when no grid is available.
+- Preserve translated canonical metadata without invoking imperative domain-object naming or description methods.
+- Preserve dynamic hidden, disabled, validation, and invocation behavior as authoritative.
+- Remain discoverable by targeted GraphQL introspection.
+
+**Non-Goals:**
+
+- Returning annotation instances or metamodel objects.
+- Adding an aggregate member-list endpoint.
+- Duplicating rows, columns, tabs, field sets, menu sections, ordering, or unreferenced placement.
+- Duplicating action positions, prompt style, redirect policy, icons, CSS, table decorators, page-size presentation, sorting hints, or sequence from layout resources.
+- Adding a requiredness flag that conflicts with GraphQL input nullability.
+- Reproducing Wicket rendering behavior or changing web-component toolkit selection.
+
+## Decisions
+
+### Extend only existing known wrappers
+
+`friendlyName` and `description` are direct scalar children of rich property, collection, action, and action-parameter wrapper types.
+`maxLength`, `pattern`, `patternFlags`, `multiLine`, and `typicalLength` are direct scalar children only of rich property and action-parameter wrappers.
+Clients discover semantic wrapper and member IDs through standard targeted introspection and request only metadata they need.
+No aggregate metadata catalogue or member-list field is added.
+
+### Prefer direct scalar fields over a descriptor object
+
+A shared Java helper constructs direct metadata fields while each generated wrapper keeps its existing type.
+This introduces no new GraphQL object type and avoids a descriptor source object, additional resolver layer, or metadata type-version contract.
+The trade-off is several additional scalar field definitions on property and parameter wrapper types, which schema-size and startup evidence must measure.
+
+### Separate names and descriptions
+
+`friendlyName` is a non-null `String` supplied by `getCanonicalFriendlyName()`.
+`description` is a nullable `String` supplied by `getCanonicalDescription()` and never copies the friendly name when absent.
+Canonical access excludes imperative per-object naming and description methods, avoids fetching domain values, and retains the metamodel translation mechanism active for the request locale.
+Existing GraphQL schema descriptions remain compatible documentation and are not the independent runtime contract.
+
+### Normalize constraint absence
+
+`maxLength` is a nullable `Int` and ignores the unlimited fallback facet.
+`pattern` is a nullable `String` containing the authoritative Java regular-expression text.
+`patternFlags` is a nullable `Int` containing Java `Pattern` flags and is absent when no non-fallback regular-expression facet exists.
+`multiLine` is a nullable `Int` containing the positive requested line count and is absent for the fallback single-line behavior.
+`typicalLength` is a nullable `Int` containing the positive effective local sizing hint when the feature has that facet.
+Malformed, non-positive, unlimited, or absent facet values become GraphQL null rather than fabricated defaults.
+
+### Preserve established resource metadata
+
+Resource `fileAccept` remains where existing clients already find it: under Blob or Clob property-get wrappers and directly on resource action-parameter wrappers.
+The new scalar helper does not create another `fileAccept` field.
+Resource input validation and deployment policy remain authoritative.
+
+### Preserve structural authority
+
+Effective grid XML owns member placement, action positions, page grouping, layout order, icons, CSS, prompt-related presentation, collection presentation, and fallback placement.
+Effective menu XML owns bars, menus, sections, entries, labels, and structural ordering.
+Wicket decorators, redirect behavior, repainting, and CSS implementation details remain excluded.
+
+### Preserve authorization boundaries
+
+New metadata resolvers read only static canonical facets and never fetch a property value, parameter default, disabled reason, or authorization rule.
+A hidden known wrapper reveals no more static schema identity than targeted introspection already reveals.
+Dynamic `hidden`, `disabled`, validation, and invocation results remain authoritative for the current object and user.
+
+## Risks / Trade-offs
+
+- [Scalar fields enlarge generated SDL] → Measure type count, SDL bytes, schema startup, and enforce that no GraphQL object type is added by metadata itself.
+- [Java regular expressions are not JavaScript patterns] → Label `patternFlags` explicitly as Java flags and keep server validation authoritative.
+- [Runtime localization can affect caching] → Resolve suppliers during field execution and test two request locales without caching one locale globally.
+- [Static metadata can conflict with dynamic state] → Keep hidden, disabled, validation, and invocation outcomes authoritative.
+- [Local hints may encourage layout duplication] → Keep the accepted field list closed and require new framework-neutral evidence for expansion.
+
+## Migration Plan
+
+All accepted fields are additive.
+Existing schema descriptions, generated names, resource fields, and operation documents remain valid.
+Clients may adopt independent labels and constraints incrementally and must tolerate absent nullable fields.
+No persisted data, route, application markup, or generated input type migration is required.
+
+## Open Questions
+
+None for the initial scope.
+New metadata fields require a separate evidence-backed change.
diff --git a/openspec/changes/add-rich-graphql-member-metadata/proposal.md b/openspec/changes/add-rich-graphql-member-metadata/proposal.md
new file mode 100644
index 00000000000..923e2005324
--- /dev/null
+++ b/openspec/changes/add-rich-graphql-member-metadata/proposal.md
@@ -0,0 +1,34 @@
+## Why
+
+The executable Reference Application analysis confirmed that a GraphQL field description can contain either a friendly name or `describedAs` text but cannot represent both independently.
+It also confirmed that effective grid XML already owns structural layout such as positions, grouping, icons, CSS hints, and action placement.
+Standalone semantic components nevertheless need a small editor-neutral metadata surface for labels and proactive input hints when no layout resource is available.
+
+The evidence is recorded in `coverage-matrix.yaml` entries `REF-METADATA-01` and `REF-METADATA-02`.
+
+## What Changes
+
+- Add independently queryable `friendlyName` and nullable `description` fields to existing rich property, collection, action, and action-parameter wrappers.
+- Add direct nullable `maxLength`, `pattern`, `patternFlags`, `multiLine`, and `typicalLength` fields to rich property and action-parameter wrappers.
+- Preserve the already-supported resource `fileAccept` fields in their established property-get and action-parameter locations rather than introducing a duplicate metadata field.
+- Resolve metadata from static canonical metamodel facets at request time so translations follow the request locale without invoking domain-object naming or description methods.
+- Preserve targeted introspection, GraphQL input nullability for requiredness, and authoritative server validation.
+- Keep positions, grouping, sequence, icons, CSS, prompt style, redirects, paging, sorting, and complete presentation structure in grid or menu resources.
+- Add compatibility, localization, authorization, schema-growth, and reference-derived tests and documentation.
+
+## Capabilities
+
+### New Capabilities
+
+- `rich-graphql-member-metadata`: Defines a narrow additive set of canonical local names, descriptions, and editor-neutral constraints on known rich GraphQL wrappers.
+
+### Modified Capabilities
+
+None.
+
+## Impact
+
+- Affects selected rich wrapper types, a reusable scalar metadata-field helper, metamodel facet adaptation, GraphQL tests, Reference Application evidence, and documentation.
+- Depends on the completed Reference Application analysis but does not depend on a particular web-component toolkit.
+- Improves standalone property, collection, action, and parameter components and deterministic fallback rendering when a layout resource is unavailable.
+- Does not serialize the complete Causeway metamodel, add a member-list endpoint, replace grid or menu resources, or alter existing operation documents.
diff --git a/openspec/changes/add-rich-graphql-member-metadata/specs/rich-graphql-member-metadata/spec.md b/openspec/changes/add-rich-graphql-member-metadata/specs/rich-graphql-member-metadata/spec.md
new file mode 100644
index 00000000000..4b985063a66
--- /dev/null
+++ b/openspec/changes/add-rich-graphql-member-metadata/specs/rich-graphql-member-metadata/spec.md
@@ -0,0 +1,107 @@
+## ADDED Requirements
+
+### Requirement: Narrow local metadata on known wrappers
+Existing rich GraphQL property, collection, action, and action-parameter wrappers SHALL expose only the additive local metadata assigned to them by this capability.
+
+#### Scenario: Client addresses a known wrapper
+- **WHEN** a client requests accepted local metadata beneath a known semantic wrapper
+- **THEN** applicable names, descriptions, or editor-neutral constraints are available
+- **AND** absent nullable semantics return null
+
+#### Scenario: Client requests an unknown metadata expansion
+- **WHEN** a client needs grid structure, menu structure, viewer policy, or metamodel internals
+- **THEN** the known wrapper does not expose those concerns through this capability
+
+### Requirement: Independent canonical friendly name and description
+Rich property, collection, action, and action-parameter wrappers SHALL expose a non-null `friendlyName` and nullable `description` independently.
+
+#### Scenario: Member has both values
+- **WHEN** the canonical metamodel supplies a friendly name and a distinct description
+- **THEN** the wrapper returns each translated value independently
+
+#### Scenario: Only a friendly name exists
+- **WHEN** the canonical metamodel supplies no description
+- **THEN** `friendlyName` remains available
+- **AND** `description` is null rather than a copy of the friendly name
+
+#### Scenario: Domain object defines imperative text
+- **WHEN** a member can compute a name or description from a domain-object instance
+- **THEN** local metadata uses only canonical static facets
+- **AND** metadata resolution does not invoke domain-object methods or fetch member values
+
+### Requirement: Bounded standalone editor constraints
+Rich property and action-parameter wrappers SHALL expose nullable `maxLength`, `pattern`, `patternFlags`, `multiLine`, and `typicalLength` scalar fields from applicable metamodel facets.
+
+#### Scenario: Constrained value is inspected
+- **WHEN** a known property or parameter declares accepted positive local constraints
+- **THEN** the applicable scalar values are returned without local reinterpretation
+- **AND** `patternFlags` represents Java regular-expression flags
+- **AND** server validation remains authoritative
+
+#### Scenario: Constraint is absent or fallback-only
+- **WHEN** a constraint is absent, unlimited, non-positive, malformed, or supplied only by the single-line or unlimited fallback
+- **THEN** the corresponding metadata field is null
+- **AND** no client-facing default is fabricated
+
+#### Scenario: Requiredness is inspected
+- **WHEN** a client needs structural nullability or requiredness
+- **THEN** it uses the generated GraphQL input type
+- **AND** no conflicting required flag is introduced
+
+### Requirement: Established resource file acceptance remains compatible
+Resource `fileAccept` metadata SHALL remain in its established property-get and resource action-parameter locations.
+
+#### Scenario: Resource metadata is inspected
+- **WHEN** a client requests accepted-file metadata for a Blob or Clob property or resource parameter
+- **THEN** the established field and value remain available
+- **AND** this capability does not add a duplicate `fileAccept` field
+
+### Requirement: Structural metadata remains in resources
+Rich local metadata SHALL NOT duplicate complete grid or menu structure or resource-owned presentation hints.
+
+#### Scenario: Client needs complete page structure
+- **WHEN** effective grid structure is available
+- **THEN** the client uses the grid resource for rows, columns, tabs, field sets, placement, ordering, icons, CSS, and action positions
+
+#### Scenario: Client needs complete application-menu structure
+- **WHEN** effective menu structure is available
+- **THEN** the client uses the menu resource for bars, menus, sections, entries, labels, hints, and ordering
+
+### Requirement: Standard member discovery
+Member metadata SHALL complement standard introspection rather than introduce a duplicate member list or metamodel API.
+
+#### Scenario: Client discovers type members
+- **WHEN** a client needs property, action, and collection identifiers
+- **THEN** it uses standard targeted GraphQL introspection
+- **AND** no aggregate member-list field is required
+
+### Requirement: Locale-correct request resolution
+Canonical friendly names and descriptions SHALL retain Causeway translation behavior for the active request locale without globally caching one locale's result.
+
+#### Scenario: Equivalent requests use different locales
+- **WHEN** two authorized requests resolve the same metadata under different supported locales
+- **THEN** each response uses the canonical translation for its own locale
+- **AND** one response does not contaminate the other
+
+### Requirement: Metadata authorization safety
+Local metadata SHALL NOT reveal sensitive values, disabled-reason internals, authorization policy rules, or imperative domain-object results.
+
+#### Scenario: Member is hidden for the current user
+- **WHEN** runtime authorization hides a known member wrapper
+- **THEN** metadata reveals no sensitive runtime state beyond the established hidden contract
+- **AND** static schema identity reveals no more than standard introspection already reveals
+
+### Requirement: Bounded schema growth
+The local metadata implementation SHALL add no aggregate catalogue and no new GraphQL object type solely for metadata.
+
+#### Scenario: Schema is compared before and after metadata
+- **WHEN** generated schema type count, SDL bytes, and startup measurements are compared
+- **THEN** metadata introduces only the documented scalar fields on existing wrappers
+- **AND** measured deltas are recorded
+
+### Requirement: Backward-compatible metadata expansion
+Metadata additions SHALL preserve established generated names, field descriptions, resource fields, operations, and runtime behavioral fields.
+
+#### Scenario: Existing GraphQL client executes
+- **WHEN** a client uses an established document without new metadata fields
+- **THEN** its operation and response shape remain valid
diff --git a/openspec/changes/add-rich-graphql-member-metadata/tasks.md b/openspec/changes/add-rich-graphql-member-metadata/tasks.md
new file mode 100644
index 00000000000..9f6dd46edae
--- /dev/null
+++ b/openspec/changes/add-rich-graphql-member-metadata/tasks.md
@@ -0,0 +1,67 @@
+## 1. Baseline and ownership
+
+- [ ] 1.1 Record the existing rich property, collection, action, and action-parameter wrapper fields and generated schema baseline.
+- [ ] 1.2 Translate matrix entries `REF-METADATA-01` and `REF-METADATA-02` into independent-name, independent-description, constraint, absence, and localization fixtures.
+- [ ] 1.3 Classify every candidate as local semantic metadata, existing resource metadata, grid structure, menu structure, viewer policy, Wicket-specific behavior, or explicit exclusion.
+- [ ] 1.4 Confirm action position, prompt, redirect, icon, CSS, collection paging, sorting, sequence, and resource-owned fields remain excluded.
+
+## 2. Shared scalar metadata support
+
+- [ ] 2.1 Add one reusable scalar metadata-field helper without introducing a GraphQL descriptor object type.
+- [ ] 2.2 Add null-safe suppliers that execute during field resolution rather than schema construction.
+- [ ] 2.3 Normalize absent, unlimited, fallback-only, malformed, and non-positive constraint values to null.
+- [ ] 2.4 Unit test helper field names, output types, null behavior, and request-time supplier execution.
+
+## 3. Independent names and descriptions
+
+- [ ] 3.1 Add non-null `friendlyName` and nullable `description` to rich property wrappers.
+- [ ] 3.2 Add non-null `friendlyName` and nullable `description` to rich collection wrappers.
+- [ ] 3.3 Add non-null `friendlyName` and nullable `description` to rich action wrappers.
+- [ ] 3.4 Add non-null `friendlyName` and nullable `description` to rich action-parameter wrappers.
+- [ ] 3.5 Resolve names and descriptions from canonical static facets without invoking imperative domain-object methods.
+- [ ] 3.6 Verify absent descriptions remain null rather than copying friendly names.
+
+## 4. Property and parameter constraints
+
+- [ ] 4.1 Add nullable `maxLength` fields backed only by finite non-fallback facets.
+- [ ] 4.2 Add nullable `pattern` and `patternFlags` fields backed by non-fallback Java regular-expression facets.
+- [ ] 4.3 Add nullable `multiLine` fields backed only by positive non-fallback line counts.
+- [ ] 4.4 Add nullable `typicalLength` fields backed only by positive effective feature facets.
+- [ ] 4.5 Apply the same normalized field contract to rich properties and rich action parameters.
+- [ ] 4.6 Preserve GraphQL input nullability as structural requiredness and server validation as authoritative.
+- [ ] 4.7 Preserve established resource `fileAccept` fields without duplication or relocation.
+
+## 5. Discovery, security, and compatibility
+
+- [ ] 5.1 Verify every added field through targeted one-type introspection.
+- [ ] 5.2 Verify known-wrapper reads return independent names, descriptions, constraints, and nulls.
+- [ ] 5.3 Verify no aggregate member list, metamodel object, grid structure, menu structure, or viewer policy is exposed.
+- [ ] 5.4 Verify hidden members, sensitive values, disabled internals, authorization policies, and imperative text methods do not leak through metadata.
+- [ ] 5.5 Verify equivalent requests under two supported locales do not share a cached translation.
+- [ ] 5.6 Preserve existing descriptions, generated names, resource fields, and established operation documents.
+
+## 6. Reference-derived qualification
+
+- [ ] 6.1 Add deterministic GraphQL test-domain fixtures for distinct names and descriptions, every accepted constraint, and missing facets.
+- [ ] 6.2 Add property, collection, action, and parameter metadata query coverage.
+- [ ] 6.3 Add localization and authorization-negative coverage.
+- [ ] 6.4 Add Reference Application targets or executable probes for matrix entries `REF-METADATA-01` and `REF-METADATA-02`.
+- [ ] 6.5 Verify the reviewed Reference Application capability inventory remains byte-identical unless an evidence-backed reclassification is required.
+
+## 7. Schema and performance evidence
+
+- [ ] 7.1 Measure generated schema type count and prove metadata adds no GraphQL object type.
+- [ ] 7.2 Measure generated SDL byte growth and attribute it to documented scalar fields.
+- [ ] 7.3 Measure representative schema startup before and after the change.
+- [ ] 7.4 Record bounded acceptance criteria and measured results.
+
+## 8. Documentation and release gates
+
+- [ ] 8.1 Document every added field, GraphQL type, source facet, null behavior, authority, and localization contract.
+- [ ] 8.2 Document the relationship to existing resource metadata and effective grid and menu resources.
+- [ ] 8.3 Document exclusions, migration compatibility, targeted introspection, and client adoption guidance.
+- [ ] 8.4 Run focused model tests and GraphQL integration tests.
+- [ ] 8.5 Run established GraphQL compatibility, resource, value-semantics, collection, and application-entry suites.
+- [ ] 8.6 Run Reference Application integration and inventory checks.
+- [ ] 8.7 Run applicable RAT, formatting, strict OpenSpec, and Git whitespace checks.
+- [ ] 8.8 Record final gate results, production isolation, and reproducible commands.