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/24 21:10:54 UTC

(causeway) 04/15: plans versionless web component identity correction

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 1df0857c476f27049b9ea5054dc2506bd3b04eed
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Aug 24 11:00:33 2026 +0100

    plans versionless web component identity correction
---
 .../.openspec.yaml                                 |  2 +
 .../design.md                                      | 79 ++++++++++++++++++++++
 .../proposal.md                                    | 29 ++++++++
 .../specs/domain-web-components/spec.md            | 35 ++++++++++
 .../spec.md                                        | 25 +++++++
 .../tasks.md                                       | 62 +++++++++++++++++
 6 files changed, 232 insertions(+)

diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/.openspec.yaml b/openspec/changes/fix-webcomponent-versionless-identity-preparation/.openspec.yaml
new file mode 100644
index 00000000000..4102db8a47b
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-08-24
diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/design.md b/openspec/changes/fix-webcomponent-versionless-identity-preparation/design.md
new file mode 100644
index 00000000000..77380529242
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/design.md
@@ -0,0 +1,79 @@
+## Context
+
+The foundation already derives object-header metadata fields from the advertised rich metadata wrapper, and the action-dispatch correction made action result and parameter-state selections type-aware.
+Two older projection paths remain unconditional: object-valued property reads and collection-row reads always request `_meta { id logicalTypeName title version }`.
+The pinned Reference Application proves that view models including `DependentArgsDemoItem`, `ActionSemanticsVm`, and `CollectionLayoutPagedChildVm` legitimately omit `version` while retaining `id`, `logicalTypeName`, and `title`.
+Those reads currently fail GraphQL validation before existing identity, editor, cancellation, and row-hydration policies can run.
+
+Identity and optimistic concurrency are distinct concerns.
+A versionless object can still have a stable logical type and identifier suitable for values, choices, hydration, and navigation, while a mutation that requires an advertised concurrency token must not invent one.
+The GraphQL schema remains authoritative, strict CSP and route-lazy delivery remain unchanged, and this change must not absorb union or opaque-route corrections.
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- Request only metadata fields advertised by the effective concrete object metadata type.
+- Preserve `version` for versioned entities and omit it for versionless view models.
+- Reuse one metadata-selection policy across property references, direct action/preparation values, and collection rows.
+- Require `id` and `logicalTypeName` before claiming navigable or hydratable semantic identity.
+- Prove preparation, choices, autocomplete, object values, and collection rows against versionless Reference Application targets.
+- Preserve cancellation, stale-response, refresh, error, redaction, and native/Vaadin boundaries.
+
+**Non-Goals:**
+
+- Adding a nullable version or concurrency field to rich GraphQL.
+- Defining optimistic-concurrency behavior for a mutation that explicitly requires an unavailable token.
+- Selecting metadata from interfaces or unions through fragments.
+- Changing opaque bookmark route encoding, decoding, or length limits.
+- Changing action placement, reference paging, Vaadin adapters, dependencies, or public browser contracts.
+
+## Decisions
+
+### Centralize concrete metadata selection
+
+A shared selection helper will inspect the effective object type, locate its `_meta` field, inspect the referenced metadata type, and select the advertised subset of `id`, `logicalTypeName`, `title`, and `version`.
+Callers will use this helper instead of constructing metadata selections independently.
+
+This extends the introspection-driven result-selection approach introduced by action dispatch and avoids maintaining a second list of versionless types.
+The rejected alternative is to remove `version` globally, because that would discard an advertised concurrency value from ordinary entities and weaken existing refresh behavior.
+
+### Keep semantic identity minimums explicit
+
+A value is navigable or suitable for hydrated row context only when the effective metadata advertises both `id` and `logicalTypeName` and the response supplies both values.
+`title` remains optional with the established identifier fallback, and `version` remains optional.
+If the minimum fields are absent, the projection returns bounded non-navigable data or an existing local unsupported/error state rather than manufacturing identity.
+
+The rejected alternative is to infer logical type from the enclosing member or generated GraphQL name, because polymorphism and aliases make that speculative.
+
+### Merge row identity with authored columns
+
+Collection row selection will start with the concrete row type's advertised metadata selection and then merge declared semantic column selections.
+Versionless rows therefore retain identity and row hydration, while rows without concrete metadata remain bounded and await the separate union-projection change.
+Windowing, offsets, ordering, partial errors, and lazy activation remain unchanged.
+
+### Treat preparation as a regression boundary
+
+Action and property preparation paths already route nested result selection through described types after the action-dispatch change.
+This change will add focused fixtures and Reference Application assertions to prove that versionless defaults, choices, autocomplete results, and valid pending values remain usable without reintroducing unconditional metadata.
+Production changes will be limited to any remaining unconditional paths revealed by those tests.
+
+### Preserve schema and host boundaries
+
+No public GraphQL operation or metadata field changes.
+No route, event, custom-element, HTMX, Vaadin, CSP, or asset policy changes.
+Capability inventory classifications change only when executable evidence changes, and unrelated union and opaque-route gaps remain visible.
+
+## Risks / Trade-offs
+
+- [A helper receives an incomplete type graph] → Return a bounded `__typename` or unsupported projection and add focused missing-description tests rather than speculating about metadata.
+- [Versionless identity is mistaken for mutation concurrency] → Keep identity extraction separate from target-input construction and never manufacture `version`.
+- [Collection column merging creates invalid nested selections] → Derive the base row selection from the concrete element type and retain existing member-wrapper column grammar.
+- [Fixing concrete rows masks union failures] → Keep union targets classified and asserted as known gaps until fragment projection is implemented separately.
+- [A stale Reference Application target changes upstream] → Pin tests to the copied corpus and validate the semantic target catalogue against introspection.
+
+## Migration Plan
+
+The change is a client-side projection correction with no persisted-data or schema migration.
+Deploy through the existing same-origin foundation artifact and verify both versioned and versionless paths.
+Rollback is a source revert with no application configuration or route migration.
diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/proposal.md b/openspec/changes/fix-webcomponent-versionless-identity-preparation/proposal.md
new file mode 100644
index 00000000000..8f83a70a2bd
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/proposal.md
@@ -0,0 +1,29 @@
+## Why
+
+The pinned Reference Application exposes view models and collection rows whose rich metadata advertises stable identity and title but no `version` field.
+The generic web-component viewer still hard-codes `_meta.version` in property-reference and collection-row projections, causing otherwise valid preparation, choice, autocomplete, editing, and collection operations to fail visibly.
+
+## What Changes
+
+- Derive identity metadata selections from each effective advertised metadata type rather than assuming `id`, `logicalTypeName`, `title`, and `version` are all present.
+- Preserve version when advertised while allowing versionless identities to participate in property values, action-parameter preparation, choices, autocomplete, collection rows, and authoritative refresh.
+- Define bounded behavior when metadata lacks fields required for semantic identity instead of inventing a version or bookmark.
+- Add focused foundation fixtures and pinned Reference Application browser and integration coverage for versioned and versionless values and rows.
+- Reclassify only capability-inventory entries whose executable evidence changes.
+- Keep union-fragment projection, opaque route encoding, action placement, GraphQL schema changes, paging, and Vaadin policy outside this change.
+
+## Capabilities
+
+### New Capabilities
+
+None.
+
+### Modified Capabilities
+
+- `domain-web-components`: Require metadata-aware property, preparation, choice, autocomplete, collection-row, and refresh projections that request only advertised identity fields.
+- `reference-application-viewer-regression-suite`: Require executable versionless view-model and row journeys and preserve unrelated union and opaque-route gaps.
+
+## Impact
+
+The change affects internal selection construction in the framework-neutral web-component foundation, its targeted-introspection descriptions and fixtures, and the pinned Reference Application regression corpus.
+Public Causeway elements, semantic events, GraphQL operations and generated names, canonical routes, HTMX lifecycle, Vaadin qualification policy, dependencies, and production asset inputs remain unchanged.
diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/domain-web-components/spec.md b/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/domain-web-components/spec.md
new file mode 100644
index 00000000000..9b1d3db28df
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/domain-web-components/spec.md
@@ -0,0 +1,35 @@
+## ADDED Requirements
+
+### Requirement: Advertised semantic identity projection
+The component library SHALL derive metadata selection for concrete object values and rows from the effective metadata type advertised by targeted introspection.
+It MUST preserve advertised identity and presentation fields, MUST treat `version` as optional, and MUST NOT request, synthesize, or infer absent metadata fields.
+
+#### Scenario: Versioned object value is read
+- **WHEN** a concrete property, choice, autocomplete result, preparation value, action result, or collection row advertises `id`, `logicalTypeName`, `title`, and `version`
+- **THEN** the operation selects those advertised fields
+- **AND** the semantic value preserves the returned version
+
+#### Scenario: Versionless object value is read
+- **WHEN** a concrete property, choice, autocomplete result, preparation value, action result, or collection row advertises `id`, `logicalTypeName`, and `title` but not `version`
+- **THEN** the operation omits `version`
+- **AND** the value remains available for supported rendering, pending-value conversion, validation, hydration, refresh, and navigation
+
+#### Scenario: Metadata lacks semantic identity minimums
+- **WHEN** an effective metadata type does not advertise both `id` and `logicalTypeName`
+- **THEN** the component does not claim a navigable bookmark or hydratable object identity
+- **AND** it exposes only a valid bounded projection or the existing local unsupported state
+
+#### Scenario: Versionless value participates in preparation
+- **WHEN** a property or action parameter preparation field returns a concrete versionless object through default, choices, autocomplete, validity, or datatype semantics
+- **THEN** the context selects only metadata fields advertised for that object
+- **AND** cancellation and stale-response protection remain unchanged
+
+#### Scenario: Versionless collection rows are loaded
+- **WHEN** a concrete collection element type advertises semantic identity without `version`
+- **THEN** row selection merges the advertised identity fields with requested semantic columns
+- **AND** the rows remain hydratable without manufacturing a concurrency token
+
+#### Scenario: Abstract row type requires fragments
+- **WHEN** an interface or union row cannot expose semantic metadata without concrete fragments
+- **THEN** the component retains a bounded non-navigable or partial-error result
+- **AND** does not treat versionless concrete-object support as general union-projection support
diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/reference-application-viewer-regression-suite/spec.md b/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/reference-application-viewer-regression-suite/spec.md
new file mode 100644
index 00000000000..c3a7eee3953
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/specs/reference-application-viewer-regression-suite/spec.md
@@ -0,0 +1,25 @@
+## ADDED Requirements
+
+### Requirement: Versionless identity regression coverage
+The pinned Reference Application suite SHALL exercise representative concrete view models and collection rows whose rich metadata omits `version` while preserving stable semantic identity.
+It MUST distinguish corrected versionless projection behavior from retained polymorphic-union and opaque-route gaps.
+
+#### Scenario: Versionless preparation target is exercised
+- **WHEN** the browser prepares representative action or property interaction semantics that return concrete versionless values
+- **THEN** defaults, choices, autocomplete, validation, and successful submission remain usable without a missing-version GraphQL error
+- **AND** invalid, cancelled, and stale interactions retain their established behavior
+
+#### Scenario: Versionless property or action result is rendered
+- **WHEN** a representative concrete view model is returned as a property value or typed action outcome
+- **THEN** its advertised identity and title remain renderable and navigable through existing semantic contracts
+- **AND** no `_meta.version` selection is submitted for its versionless metadata type
+
+#### Scenario: Versionless collection row is rendered
+- **WHEN** a representative concrete view-model collection is activated
+- **THEN** its row window reaches the supported ready or documented partial state with stable identities and requested columns
+- **AND** a missing version field is not the cause of any row error
+
+#### Scenario: Unrelated retained gaps remain classified
+- **WHEN** the same qualification run reaches a polymorphic union target or a long opaque composite bookmark
+- **THEN** the suite retains the corresponding bounded known-gap assertion until its focused change is implemented
+- **AND** does not count it as evidence that concrete versionless identity failed
diff --git a/openspec/changes/fix-webcomponent-versionless-identity-preparation/tasks.md b/openspec/changes/fix-webcomponent-versionless-identity-preparation/tasks.md
new file mode 100644
index 00000000000..80ff5b2f3db
--- /dev/null
+++ b/openspec/changes/fix-webcomponent-versionless-identity-preparation/tasks.md
@@ -0,0 +1,62 @@
+## 1. Confirm executable versionless shapes
+
+- [ ] 1.1 Capture targeted introspection for representative versioned entity and versionless view-model metadata types.
+- [ ] 1.2 Identify concrete Reference Application property, preparation, choice or autocomplete, action-result, and collection-row targets that omit `version`.
+- [ ] 1.3 Record retained interface or union and opaque-route targets that remain outside this correction.
+
+## 2. Centralize metadata selection
+
+- [ ] 2.1 Add a shared helper that derives the advertised metadata subset for a concrete object type.
+- [ ] 2.2 Preserve `id`, `logicalTypeName`, `title`, and `version` only when each field is advertised.
+- [ ] 2.3 Return a bounded projection when an object type or metadata description is unavailable.
+- [ ] 2.4 Keep semantic identity minimum validation separate from optional version handling.
+- [ ] 2.5 Reuse the helper from direct interaction result selection without changing scalar, enum, resource, or union behavior.
+
+## 3. Correct property and preparation projections
+
+- [ ] 3.1 Replace unconditional object-valued property metadata selection with the shared advertised selection.
+- [ ] 3.2 Verify versioned property references continue to retain returned versions.
+- [ ] 3.3 Verify versionless property references remain renderable and convertible to pending identity values.
+- [ ] 3.4 Verify action and property defaults, choices, autocomplete, validity, and datatype preparation omit absent metadata fields.
+- [ ] 3.5 Preserve preparation cancellation, stale-response, validation, and protected-value behavior.
+
+## 4. Correct collection-row projections
+
+- [ ] 4.1 Derive the base row selection from the concrete collection element type.
+- [ ] 4.2 Merge advertised row metadata with declared semantic columns without duplicate or speculative fields.
+- [ ] 4.3 Preserve versioned row hydration, windows, ordering, offsets, lazy activation, and partial errors.
+- [ ] 4.4 Support versionless concrete row hydration when `id` and `logicalTypeName` are advertised and returned.
+- [ ] 4.5 Retain bounded non-navigable or partial behavior for abstract rows that require the later union-projection change.
+
+## 5. Add focused foundation coverage
+
+- [ ] 5.1 Add versioned and versionless metadata-selection fixtures and unit tests.
+- [ ] 5.2 Add object-valued property tests proving absent `version` is not selected.
+- [ ] 5.3 Add preparation, choices, and autocomplete tests for concrete versionless values.
+- [ ] 5.4 Add versioned and versionless collection-row selection and hydration tests.
+- [ ] 5.5 Add missing metadata-description and missing identity-minimum tests.
+- [ ] 5.6 Add regression assertions that unions remain bounded and no version or bookmark is invented.
+
+## 6. Qualify against the pinned Reference Application
+
+- [ ] 6.1 Add or refine deterministic target-catalogue entries for versionless preparation and row journeys.
+- [ ] 6.2 Convert missing-version browser assertions to successful typed interactions only where executable evidence supports the correction.
+- [ ] 6.3 Preserve invalid, cancelled, stale, responsive, accessibility, CSP, console, external-request, and route-disposal assertions.
+- [ ] 6.4 Preserve explicit union-projection and opaque-route known gaps.
+- [ ] 6.5 Update capability inventory classifications only where executable evidence changes.
+- [ ] 6.6 Verify clean and incremental inventory generation remain byte-identical.
+
+## 7. Document compatibility and isolation
+
+- [ ] 7.1 Document the identity-versus-version contract and concrete metadata selection algorithm.
+- [ ] 7.2 Record representative introspection and operation evidence with reproducible commands.
+- [ ] 7.3 Verify no public GraphQL, Causeway element, event, route, HTMX, Vaadin, dependency, CSP, or asset-policy contract changes.
+
+## 8. Run final qualification gates
+
+- [ ] 8.1 Run the complete foundation Node suite and web-component foundation and HTMX Maven tests.
+- [ ] 8.2 Run relevant GraphQL model and integration tests.
+- [ ] 8.3 Run the full Petclinic integration and Playwright suites.
+- [ ] 8.4 Run the Reference Application clean package, integration, capability inventory, and Playwright suites.
+- [ ] 8.5 Run strict CSP, accessibility, keyboard, responsive, theme, external-request, console-error, page-error, and overflow gates.
+- [ ] 8.6 Run applicable RAT checks, strict OpenSpec validation, `git diff --check`, and production-isolation verification.