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/09/24 16:22:33 UTC
(causeway) 39/41: CAUSEWAY-4066: proposes application primer observations
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4066
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 15473f9ad5b8db0540a6830d24cc816e3c21bb9e
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Sep 24 16:34:59 2026 +0100
CAUSEWAY-4066: proposes application primer observations
---
.../observe-application-primers/.openspec.yaml | 2 +
.../changes/observe-application-primers/design.md | 94 +++++++++++++
.../observe-application-primers/proposal.md | 31 +++++
.../specs/application-primer-observation/spec.md | 153 +++++++++++++++++++++
.../changes/observe-application-primers/tasks.md | 30 ++++
5 files changed, 310 insertions(+)
diff --git a/openspec/changes/observe-application-primers/.openspec.yaml b/openspec/changes/observe-application-primers/.openspec.yaml
new file mode 100644
index 00000000000..75289e4b73f
--- /dev/null
+++ b/openspec/changes/observe-application-primers/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-09-24
diff --git a/openspec/changes/observe-application-primers/design.md b/openspec/changes/observe-application-primers/design.md
new file mode 100644
index 00000000000..c013d1a0f06
--- /dev/null
+++ b/openspec/changes/observe-application-primers/design.md
@@ -0,0 +1,94 @@
+## Context
+
+`ActionExecutor` dispatches matching action primers synchronously immediately before invoking the domain action method.
+`EntityPage.onNewRequestCycle()` dispatches matching view primers synchronously before Wicket page preparation, guarded so that a page is primed once per request lifecycle.
+`PrimingRegistryDefault` owns the immutable ordered registrations and invokes each matching callback directly.
+Primer callbacks are intended to preload objects into the active ORM persistence context and can therefore account for significant Java-agent JDBC activity.
+Today that activity inherits the enclosing action, interaction, or HTTP context without a boundary identifying the individual primer callback.
+
+The observation profile already provides a framework-owned `CausewayObservationIntegration` backed by a no-op registry when observation is inactive.
+The Java agent remains responsible for HTTP and JDBC instrumentation, propagation, sampling, and export.
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- Create one semantic observation around every matching action or view primer callback that is actually invoked.
+- Isolate the duration and automatic JDBC descendants of each callback.
+- Use domain-facing action and object identities with existing bounded naming rules.
+- Preserve natural parentage, deterministic callback order, synchronous execution, and failure propagation.
+- Preserve inactive-profile behavior and bounded static metadata privacy.
+
+**Non-Goals:**
+
+- Do not create an aggregate priming parent around all matching callbacks.
+- Do not expose primer implementation classes, lambda names, registration order, targets, arguments, or loaded values.
+- Do not change the public priming SPI, matching rules, registration lifecycle, Wicket visit guard, or callback order.
+- Do not add observations when no primer matches.
+- Do not count primer observations against the Causeway Wicket observation budget.
+- Do not add an SDK, exporter, sampler, HTTP span, or JDBC span.
+
+## Decisions
+
+### Observe each callback at the registry dispatch boundary
+
+`PrimingRegistryDefault` will wrap each `RegisteredActionPrimer.prime(...)` and `RegisteredViewPrimer.prime(...)` invocation separately.
+This is the narrowest boundary that knows a callback actually matched and preserves one observation per registration.
+The registry will obtain `CausewayObservationIntegration` through the existing service registry and will retain unchanged behavior when the integration is absent or no-op.
+
+Instrumenting `ActionExecutor` and `EntityPage` was rejected because those call sites surround the entire lookup and dispatch opportunity, producing one aggregate observation rather than one observation per callback.
+Instrumenting application implementations was rejected because it would require application authors to adopt tracing code and would not provide consistent framework semantics.
+Using `ApplicationSpanService` was rejected because primers are framework-invoked lifecycle callbacks and need stable framework names rather than application-selected suffixes.
+
+### Use separate stable names and bounded semantic contextual names
+
+Action-primer observations will use stable name `causeway.priming.action` and contextual name `prime action <logical-member-identifier>`.
+View-primer observations will use stable name `causeway.priming.view` and contextual name `prime view <logical-object-type>`.
+Contextual names will use the existing case-preserving namespace fallback and deterministic 50-character truncation policy.
+
+An action-primer observation will carry the complete domain-facing `causeway.object.type` and `causeway.action.id` identifiers.
+A view-primer observation will carry the complete domain-facing `causeway.object.type` identifier.
+The registry will retain canonical action identity from metamodel validation rather than derive identity from a primer implementation class.
+
+Including an implementation class or lambda-generated name was rejected because such names can be unstable, proxy-dependent, operationally noisy, and unrelated to the domain operation being primed.
+Adding an application-supplied primer label was rejected because it would expand the public SPI solely for telemetry and introduce another cardinality and privacy contract.
+
+### Preserve natural parentage without synthetic scopes
+
+Action-primer observations will naturally be children of the current action-invocation observation because action priming executes within that invocation before the action method.
+View-primer observations will naturally be children of the current HTTP, interaction, or other request context and siblings of later page-preparation and page-render observations because view priming occurs before Wicket page preparation begins.
+No action, page-preparation, or aggregate priming parent will be fabricated.
+
+When several primers match one key, their observations will be sequential siblings with the same semantic name and canonical attributes.
+Their separate durations and descendants identify callback boundaries without exposing registration order or implementation identity.
+
+### Preserve failure and no-op semantics
+
+Each observation will record a callback exception and close before the original exception propagates unchanged.
+The existing fail-fast callback behavior remains unchanged, so a failing primer prevents later callbacks and the guarded action or page preparation from continuing as it does today.
+When observation is inactive, the no-op path will invoke each callback exactly once without exporting metadata.
+
+### Keep Java-agent work beneath the semantic callback
+
+Automatic JDBC work initiated synchronously by a primer will inherit the active primer observation.
+Causeway will not create a duplicate JDBC observation or alter Java-agent context propagation.
+Non-primer work before or after the callback will remain beneath its existing natural parent.
+
+## Risks / Trade-offs
+
+- **[Risk] Multiple primers for one key create repeated sibling names.** → Preserve one span per callback as explicitly required and rely on separate timing and child structure rather than unstable implementation labels.
+- **[Risk] A frequently invoked primer increases trace volume.** → Emit observations only for actual registered callback invocations and do not add an aggregate parent.
+- **[Risk] A callback failure can leave observation scope active.** → Use the established synchronous observation closure pattern and test success, runtime failure, error, and no-op cleanup.
+- **[Risk] Contributed actions could expose implementation identity.** → Resolve and retain the same domain-facing metamodel action identity used for registration and action invocation.
+- **[Risk] Operators could mistake view priming for Wicket preparation.** → Document that view priming precedes page preparation and appears as its sibling under the natural request context.
+
+## Migration Plan
+
+No application migration is required.
+Deployments with the `observation` profile will begin exporting one additional semantic observation per invoked primer callback.
+Operators should account for the new stable names in span-volume estimates and may filter them by canonical action or object type.
+Rollback removes the semantic observations without affecting priming execution or Java-agent JDBC instrumentation.
+
+## Open Questions
+
+None.
diff --git a/openspec/changes/observe-application-primers/proposal.md b/openspec/changes/observe-application-primers/proposal.md
new file mode 100644
index 00000000000..d6e55cb8c1a
--- /dev/null
+++ b/openspec/changes/observe-application-primers/proposal.md
@@ -0,0 +1,31 @@
+## Why
+
+Action and view primers deliberately perform preloading work that can trigger significant JDBC activity, but that work currently appears directly beneath the enclosing action or request and cannot be distinguished from surrounding framework work.
+A semantic observation around every invoked primer will expose its duration and automatic JDBC descendants while preserving the existing priming lifecycle and privacy boundaries.
+
+## What Changes
+
+- Create one Causeway observation for every matching `ActionPrimer` callback that is actually invoked.
+- Create one Causeway observation for every matching `ViewPrimer` callback that is actually invoked.
+- Use stable observation names, bounded domain-facing contextual names, and complete canonical action or object-type attributes.
+- Preserve natural action, interaction, HTTP, and Wicket parentage without creating observations when no primer matches.
+- Record callback failures and propagate them unchanged while preserving inactive-observation behavior.
+- Keep primer targets, arguments, values, bookmarks, users, tenants, implementation class names, registration order, and other instance data out of observation names and attributes.
+- Preserve Java-agent ownership of JDBC spans, propagation, sampling, and export.
+
+## Capabilities
+
+### New Capabilities
+
+- `application-primer-observation`: Defines per-callback semantic observations for action and view primers, including naming, parentage, lifecycle, failure handling, and privacy.
+
+### Modified Capabilities
+
+None.
+
+## Impact
+
+- Priming dispatch in `core/metamodel`, principally `PrimingRegistryDefault`.
+- Existing action and Wicket view priming tests, plus focused observation and Java-agent compatibility coverage.
+- Tracing operations documentation and trace hierarchy guidance.
+- No public priming API signature change, no new dependency, and no change to primer matching or execution order.
diff --git a/openspec/changes/observe-application-primers/specs/application-primer-observation/spec.md b/openspec/changes/observe-application-primers/specs/application-primer-observation/spec.md
new file mode 100644
index 00000000000..66594abfd9a
--- /dev/null
+++ b/openspec/changes/observe-application-primers/specs/application-primer-observation/spec.md
@@ -0,0 +1,153 @@
+## ADDED Requirements
+
+### Requirement: One observation per invoked action primer
+
+When observation is active, the framework SHALL create one `causeway.priming.action` observation around each matching `ActionPrimer` callback that it invokes.
+The framework SHALL NOT create an action-primer observation when no primer matches, and SHALL NOT create an aggregate observation around several matching callbacks.
+
+#### Scenario: One action primer matches
+
+- **WHEN** an ordinary or contributed domain action has one matching registered action primer
+- **THEN** the framework invokes the primer exactly once within one `causeway.priming.action` observation
+- **AND** the observation closes before the domain action method begins
+
+#### Scenario: Several action primers match
+
+- **WHEN** several action primers match one domain-facing action key
+- **THEN** the framework invokes them in the existing deterministic order within separate sequential sibling observations
+- **AND** it creates no additional aggregate priming observation
+
+#### Scenario: No action primer matches
+
+- **WHEN** an action has no matching registered action primer or is excluded from priming by existing association-access rules
+- **THEN** the framework creates no action-primer observation
+- **AND** action execution remains unchanged
+
+### Requirement: One observation per invoked view primer
+
+When observation is active, the framework SHALL create one `causeway.priming.view` observation around each matching `ViewPrimer` callback that it invokes.
+The framework SHALL preserve the existing once-per-request page visit guard and SHALL NOT create an aggregate observation around several matching callbacks.
+
+#### Scenario: One view primer matches
+
+- **WHEN** an entity page begins a request lifecycle and one view primer matches its exact logical type
+- **THEN** the framework invokes the primer exactly once within one `causeway.priming.view` observation
+- **AND** the observation closes before Wicket page preparation begins
+
+#### Scenario: Several view primers match
+
+- **WHEN** several view primers match one exact logical type
+- **THEN** the framework invokes them in the existing deterministic order within separate sequential sibling observations
+- **AND** it creates no additional aggregate priming observation
+
+#### Scenario: View primer is not invoked
+
+- **WHEN** no view primer matches or the existing page visit guard excludes a repeated lifecycle visit
+- **THEN** the framework creates no view-primer observation
+
+### Requirement: Stable and domain-facing primer names
+
+Action-primer observations SHALL use stable name `causeway.priming.action` and contextual name `prime action <logical-member-identifier>`.
+View-primer observations SHALL use stable name `causeway.priming.view` and contextual name `prime view <logical-object-type>`.
+Contextual names SHALL preserve declared casing, reduce the logical-type namespace before truncation when necessary, and remain within the existing deterministic 50-character limit.
+
+#### Scenario: Ordinary action primer is observed
+
+- **WHEN** a primer is invoked for `demo.Customer#updateName`
+- **THEN** its contextual name is `prime action demo.Customer#updateName`
+- **AND** its stable name remains `causeway.priming.action`
+
+#### Scenario: Contributed action primer is observed
+
+- **WHEN** a primer is invoked for a contributed action
+- **THEN** its contextual name uses the domain-facing action identity
+- **AND** it does not expose the mixin implementation identity
+
+#### Scenario: View primer is observed
+
+- **WHEN** a primer is invoked for logical type `demo.Customer`
+- **THEN** its contextual name is `prime view demo.Customer`
+- **AND** its stable name remains `causeway.priming.view`
+
+#### Scenario: Primer name exceeds the bound
+
+- **WHEN** a primer's full contextual name would exceed 50 characters
+- **THEN** the framework applies the existing namespace fallback and deterministic truncation policy
+- **AND** the complete canonical attributes remain available
+
+### Requirement: Canonical bounded primer metadata
+
+Each action-primer observation SHALL carry the complete domain-facing `causeway.object.type` and `causeway.action.id` attributes.
+Each view-primer observation SHALL carry the complete domain-facing `causeway.object.type` attribute.
+Primer observations MUST NOT attach implementation class names, generated lambda names, registration order, target identity, arguments, values, bookmarks, object titles, users, tenants, transaction identifiers, interaction identifiers, or generated component paths.
+
+#### Scenario: Action-primer metadata is exported
+
+- **WHEN** an action primer is observed
+- **THEN** its canonical attributes identify the logical target type and complete domain-facing action
+- **AND** they contain no action argument or target-instance data
+
+#### Scenario: View-primer metadata is exported
+
+- **WHEN** a view primer is observed
+- **THEN** its canonical attribute identifies the complete logical object type
+- **AND** it contains no bookmark, title, target-instance, user, tenant, or component-path data
+
+#### Scenario: Repeated sibling primers are exported
+
+- **WHEN** several primers match the same action or view key
+- **THEN** their observations use the same semantic name and canonical attributes
+- **AND** the framework does not distinguish them using implementation or sequence metadata
+
+### Requirement: Natural primer parentage and Java-agent ownership
+
+A primer observation SHALL inherit the naturally current action, interaction, HTTP, or other trace context.
+Causeway SHALL NOT fabricate an action, page-preparation, or aggregate priming parent.
+Automatic JDBC spans initiated synchronously by a primer SHALL remain Java-agent-owned descendants of that primer observation.
+
+#### Scenario: Action primer performs JDBC work
+
+- **WHEN** an action primer performs database work during an observed action invocation
+- **THEN** the primer observation is a child of the current action-invocation observation
+- **AND** Java-agent JDBC spans are descendants of the primer observation
+
+#### Scenario: View primer performs JDBC work
+
+- **WHEN** a view primer performs database work before entity-page preparation
+- **THEN** the primer observation inherits the current request or interaction context
+- **AND** it is a sibling of later page-preparation and page-render observations rather than their child
+- **AND** Java-agent JDBC spans are descendants of the primer observation
+
+#### Scenario: Primer executes without a Causeway semantic parent
+
+- **WHEN** a primer is invoked with only an automatic or externally supplied current context
+- **THEN** its observation inherits that context without fabricating a Causeway parent
+
+### Requirement: Primer failure and cleanup semantics
+
+The framework SHALL record an escaping primer failure on the observation for that callback, SHALL close its scope, and SHALL propagate the original failure unchanged.
+Observation MUST NOT retry, suppress, wrap, or convert a primer failure and MUST NOT alter the existing fail-fast callback sequence.
+
+#### Scenario: Action primer fails
+
+- **WHEN** an observed action primer throws a runtime exception or error
+- **THEN** its observation records the failure and closes
+- **AND** the same failure propagates through action execution
+- **AND** the action method and any later matching primer are not invoked
+
+#### Scenario: View primer fails
+
+- **WHEN** an observed view primer throws a runtime exception or error
+- **THEN** its observation records the failure and closes
+- **AND** the same failure propagates through the Wicket request
+- **AND** page preparation and any later matching primer do not continue normally
+
+### Requirement: Safe inactive behavior
+
+When Causeway observation is inactive, primer matching and callback execution SHALL remain unchanged and no primer observation or metadata SHALL be exported.
+
+#### Scenario: Observation profile is inactive
+
+- **WHEN** one or more action or view primers match while the observation integration is no-op
+- **THEN** each callback is invoked exactly once in the existing order
+- **AND** no primer observation, scope, or metadata is exported
diff --git a/openspec/changes/observe-application-primers/tasks.md b/openspec/changes/observe-application-primers/tasks.md
new file mode 100644
index 00000000000..015a23de7b6
--- /dev/null
+++ b/openspec/changes/observe-application-primers/tasks.md
@@ -0,0 +1,30 @@
+## 1. Primer Observation Model
+
+- [ ] 1.1 Add stable action-primer and view-primer observation names and bounded contextual-name construction using the existing semantic naming policy.
+- [ ] 1.2 Define canonical action-primer metadata for complete domain-facing object type and action identifiers.
+- [ ] 1.3 Define canonical view-primer metadata for the complete domain-facing object type.
+- [ ] 1.4 Retain the validated domain-facing action identity with each registered action primer without exposing primer implementation identity.
+
+## 2. Per-Callback Observation Dispatch
+
+- [ ] 2.1 Resolve the optional Causeway observation integration at the `PrimingRegistryDefault` dispatch boundary without changing the public priming SPI.
+- [ ] 2.2 Wrap every matching action-primer callback in its own `causeway.priming.action` observation.
+- [ ] 2.3 Wrap every matching view-primer callback in its own `causeway.priming.view` observation.
+- [ ] 2.4 Preserve deterministic callback order, fail-fast behavior, original exception propagation, and observation scope cleanup.
+- [ ] 2.5 Preserve no-match, excluded association-access, repeated Wicket visit, absent-integration, and no-op behavior without creating synthetic or aggregate observations.
+- [ ] 2.6 Verify that action primers remain children of action invocation and view primers remain natural request-level siblings of later Wicket preparation and rendering.
+
+## 3. Focused Tests
+
+- [ ] 3.1 Test zero, one, and multiple matching action primers, including one sibling observation per invoked callback and no aggregate parent.
+- [ ] 3.2 Test zero, one, and multiple matching view primers together with the existing once-per-request page visit guard.
+- [ ] 3.3 Test ordinary and contributed action naming, complete canonical attributes, namespace fallback, deterministic truncation, and case preservation.
+- [ ] 3.4 Test callback success, runtime exception, error, fail-fast ordering, scope cleanup, and unchanged exception identity.
+- [ ] 3.5 Test inactive and absent observation integration paths invoke every applicable callback exactly once without exporting observations.
+- [ ] 3.6 Test metadata privacy, including absence of implementation classes, lambda names, registration order, targets, arguments, values, bookmarks, titles, users, tenants, and generated component paths.
+
+## 4. Compatibility and Operations Evidence
+
+- [ ] 4.1 Extend Java-agent compatibility coverage to verify action-primer and view-primer parentage and Java-agent-owned JDBC descendants without duplicate HTTP or JDBC spans.
+- [ ] 4.2 Update the tracing operations guide with stable names, contextual names, hierarchy examples, multiple-primer behavior, privacy, and view-primer placement before page preparation.
+- [ ] 4.3 Run focused metamodel, Wicket, and Java-agent compatibility tests, run `git diff --check`, validate the OpenSpec change strictly, and record commands and results in `validation.md`.