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/06/09 07:46:46 UTC
(causeway) 08/15: CAUSEWAY-4016: proposes replay background command guard
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4034
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 4791598842780c7b8c994f352dc67bef080c2dc3
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Jun 8 19:31:12 2026 +0100
CAUSEWAY-4016: proposes replay background command guard
---
.../.openspec.yaml | 2 +
.../design.md | 55 ++++++++++++++++++
.../proposal.md | 27 +++++++++
.../command-replay-background-completion/spec.md | 42 ++++++++++++++
.../specs/replayable-command-actions/spec.md | 65 ++++++++++++++++++++++
.../tasks.md | 28 ++++++++++
6 files changed, 219 insertions(+)
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/.openspec.yaml b/openspec/changes/prevent-replay-with-pending-background-commands/.openspec.yaml
new file mode 100644
index 00000000000..e8d4ccfe905
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-06-08
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/design.md b/openspec/changes/prevent-replay-with-pending-background-commands/design.md
new file mode 100644
index 00000000000..4c7ccf89398
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/design.md
@@ -0,0 +1,55 @@
+## Context
+
+Command replay executes `ReplayableCommand` instances through the replay manager collection actions and through the individual `replayOrRetry` action.
+A replayed command can invoke domain behavior that schedules `ExecuteIn.BACKGROUND` command log entries, just as the original recorded command did.
+The recording side now prevents the recorder from advancing while background commands from earlier work remain pending.
+Replay needs the same sequencing guarantee so replayed commands do not advance past background work that has not yet reached durable state.
+
+The command-log repository already provides `findBackgroundAndNotYetStarted()` for pending background command detection.
+That query can be reused by replay UI action guards and by replay loops without introducing persistence-specific behavior.
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- Stop multi-command replay after a replayed command creates pending background commands.
+- Disable single replay actions while pending background commands exist from earlier replayed work.
+- Tell the replay user to wait until pending background commands have executed and committed before continuing replay.
+- Reuse existing command-log repository abstractions so JDO and JPA behavior stays consistent.
+- Cover selected replay, single replay, and continuation-after-wait behavior with tests.
+
+**Non-Goals:**
+
+- Do not change how background commands are scheduled, executed, retried, or marked complete.
+- Do not automatically run, poll for, or wait on background commands inside replay actions.
+- Do not change export YAML or replay result mapping semantics.
+- Do not change the recording-side guard introduced by the previous change.
+
+## Decisions
+
+- Add a reusable replay pending-background guard close to the replay domain services/actions.
+ `CommandReplayManager` collection actions and `ReplayableCommand#replayOrRetry` should use the same message and same repository query.
+ Alternative considered: duplicate checks in each mixin, but that risks diverging messages and behavior.
+
+- Check for pending background commands between iterations of selected replay.
+ The selected replay action should replay a command, then stop the loop if that command leaves pending background commands.
+ Alternative considered: check only before the loop starts, but that misses background commands created by an earlier command in the same selected replay run.
+
+- Disable single-command replay when pending background commands exist.
+ This applies to the replayable command action and should also be considered for replay manager single-next style actions so the replay user cannot bypass the sequencing rule through another replay entry point.
+ Alternative considered: allow invocation and fail inside `act`, but disablement gives immediate UI feedback and matches the existing replay-state guard style.
+
+- Treat any not-yet-started background command as a blocker.
+ This mirrors the recording guard and is the safest interpretation for replay consistency.
+ Alternative considered: only block background commands tied to the replayed command's parent interaction, but existing repository support and the user's safety requirement favor a conservative first implementation.
+
+## Risks / Trade-offs
+
+- [Risk] A pending background command unrelated to the replay flow may block replay.
+ Mitigation: keep the behavior conservative for correctness and narrow the query later only if real-world usage shows false positives.
+
+- [Risk] Stopping selected replay after a command succeeds may look like a partial replay failure.
+ Mitigation: the command that created background work remains successful; the UI message should explain that replay paused so the user can wait and continue.
+
+- [Risk] Tests need to simulate both replay and background execution, which can be integration-heavy.
+ Mitigation: reuse existing command-log background service integration fixtures and focused command replay tests rather than adding broad end-to-end coverage.
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/proposal.md b/openspec/changes/prevent-replay-with-pending-background-commands/proposal.md
new file mode 100644
index 00000000000..2007cbe5559
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/proposal.md
@@ -0,0 +1,27 @@
+## Why
+
+Replay users can currently continue replaying commands while background commands created by an earlier replayed action are still pending execution.
+This can make replay advance past durable asynchronous effects that the original recording user had to wait for, causing later replayed commands to run against incomplete state.
+
+## What Changes
+
+- Stop multi-command replay after a replayed command creates one or more pending background commands.
+- Require the replay user to wait until those background commands have executed and committed before continuing replay.
+- Disable single-command `replayOrRetry` while pending background commands from earlier replayed work still exist.
+- Reuse the existing pending background command detection used by recording support.
+- Leave recording-side pending-background behavior unchanged.
+
+## Capabilities
+
+### New Capabilities
+- `command-replay-background-completion`: Ensures replay cannot advance past pending background commands created by replayed actions.
+
+### Modified Capabilities
+- `replayable-command-actions`: The single-command replay-or-retry action is disabled while pending background commands must be completed first.
+
+## Impact
+
+- Affects `CommandReplayManager` selected replay flow and `ReplayableCommand#replayOrRetry` disablement.
+- Uses command-log repository pending-background queries, with implementation likely in command-log applib and covered by JDO/JPA integration tests.
+- Adds regression tests for selected replay stopping, single replay disablement, and replay continuation after pending background commands complete.
+- No new dependencies are expected.
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/specs/command-replay-background-completion/spec.md b/openspec/changes/prevent-replay-with-pending-background-commands/specs/command-replay-background-completion/spec.md
new file mode 100644
index 00000000000..c09f11acbe6
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/specs/command-replay-background-completion/spec.md
@@ -0,0 +1,42 @@
+## ADDED Requirements
+
+### Requirement: Selected replay pauses when background commands become pending
+When replaying multiple selected commands, the system SHALL stop replaying additional selected commands after a replayed command creates at least one pending background command.
+A background command SHALL be considered pending when it is persisted with `ExecuteIn.BACKGROUND` and has not yet started.
+The system MUST leave commands replayed before the pause in their resulting replay states.
+The system MUST allow the replay user to continue replaying remaining eligible commands after the pending background commands have executed and committed.
+
+#### Scenario: Selected replay stops after command creates pending background work
+- **GIVEN** a replay user selected multiple replayable commands
+- **AND** an earlier selected command schedules a background command during replay
+- **WHEN** the scheduled background command remains pending execution after that selected command is replayed
+- **THEN** the selected replay loop stops before replaying later selected commands
+- **AND** the replay user is told to wait for pending background commands to complete before continuing replay
+
+#### Scenario: Selected replay continues when no background work is pending
+- **GIVEN** a replay user selected multiple replayable commands
+- **WHEN** each replayed command completes without leaving pending background commands
+- **THEN** the selected replay loop continues to the next selected command until the selection is exhausted or a command replay fails
+
+#### Scenario: Selected replay can continue after background work completes
+- **GIVEN** selected replay paused because a replayed command created a pending background command
+- **AND** that background command has executed and committed
+- **WHEN** the replay user starts replay for remaining eligible commands
+- **THEN** the system allows replay to continue
+
+### Requirement: Replay manager single replay is disabled while background commands are pending
+The replay manager SHALL disable single-command replay entry points while at least one background command remains pending execution.
+The disablement message MUST tell the replay user to wait until pending background commands have executed and committed before continuing replay.
+When no background commands are pending, existing replay manager disablement rules SHALL continue to apply.
+
+#### Scenario: Replay next is disabled while background work is pending
+- **GIVEN** at least one background command remains pending execution
+- **WHEN** the framework evaluates a replay manager action that replays the next eligible command
+- **THEN** the action is disabled
+- **AND** the disablement message instructs the replay user to wait for pending background commands to complete before continuing replay
+
+#### Scenario: Replay next remains enabled after background work completes
+- **GIVEN** previously pending background commands have executed and committed
+- **AND** there is at least one pending or failed command eligible for replay
+- **WHEN** the framework evaluates a replay manager action that replays the next eligible command
+- **THEN** the action is not disabled because of background command completion
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/specs/replayable-command-actions/spec.md b/openspec/changes/prevent-replay-with-pending-background-commands/specs/replayable-command-actions/spec.md
new file mode 100644
index 00000000000..e0d62d59f6c
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/specs/replayable-command-actions/spec.md
@@ -0,0 +1,65 @@
+## MODIFIED Requirements
+
+### Requirement: Replay or retry action is enabled only for replay states
+The system SHALL expose a replay-or-retry action for replayable commands.
+The replay-or-retry action SHALL be enabled when the replay state is `PENDING` and no background commands are pending execution.
+The replay-or-retry action SHALL be enabled when the replay state is `OK` and no background commands are pending execution.
+The replay-or-retry action SHALL be enabled when the replay state is `FAILED` and no background commands are pending execution.
+The replay-or-retry action MUST be disabled when the replay state is `UNDEFINED`.
+The replay-or-retry action MUST be disabled when the replay state is `EXPORTED`.
+The replay-or-retry action MUST be disabled when the replay state is `EXCLUDED`.
+The replay-or-retry action MUST be disabled when at least one background command remains pending execution.
+If invocation is attempted while the replay-or-retry action is disabled, the system MUST NOT replay the command.
+
+#### Scenario: Pending command can be replayed
+- **GIVEN** a replayable command has replay state `PENDING`
+- **AND** no background commands are pending execution
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is enabled
+
+#### Scenario: Successful command can be replayed again
+- **GIVEN** a replayable command has replay state `OK`
+- **AND** no background commands are pending execution
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is enabled
+
+#### Scenario: Failed command can be retried
+- **GIVEN** a replayable command has replay state `FAILED`
+- **AND** no background commands are pending execution
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is enabled
+
+#### Scenario: Newly recorded command cannot be replayed
+- **GIVEN** a replayable command has replay state `UNDEFINED`
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is disabled
+
+#### Scenario: Exported command cannot be replayed from the command view
+- **GIVEN** a replayable command has replay state `EXPORTED`
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is disabled
+
+#### Scenario: Excluded command cannot be replayed
+- **GIVEN** a replayable command has replay state `EXCLUDED`
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is disabled
+
+#### Scenario: Replay is disabled while background work is pending
+- **GIVEN** a replayable command has replay state `PENDING`
+- **AND** at least one background command remains pending execution
+- **WHEN** the framework evaluates the replay-or-retry action
+- **THEN** the replay-or-retry action is disabled
+- **AND** the disablement message instructs the replay user to wait for pending background commands to complete before continuing replay
+
+#### Scenario: Direct invocation is guarded for non-replay state
+- **GIVEN** a caller bypasses UI disablement
+- **AND** a replayable command has replay state `UNDEFINED`
+- **WHEN** the caller invokes replay-or-retry directly
+- **THEN** the system does not replay the command
+
+#### Scenario: Direct invocation is guarded for pending background work
+- **GIVEN** a caller bypasses UI disablement
+- **AND** a replayable command has replay state `PENDING`
+- **AND** at least one background command remains pending execution
+- **WHEN** the caller invokes replay-or-retry directly
+- **THEN** the system does not replay the command
diff --git a/openspec/changes/prevent-replay-with-pending-background-commands/tasks.md b/openspec/changes/prevent-replay-with-pending-background-commands/tasks.md
new file mode 100644
index 00000000000..e367c25af33
--- /dev/null
+++ b/openspec/changes/prevent-replay-with-pending-background-commands/tasks.md
@@ -0,0 +1,28 @@
+## 1. Test Coverage
+
+- [ ] 1.1 Add tests for selected replay where an earlier selected command creates a pending background command and the replay loop stops before later selected commands execute.
+- [ ] 1.2 Add tests for selected replay where no background command is pending and the loop continues through selected commands as before.
+- [ ] 1.3 Add tests showing selected replay can continue after pending background commands have executed and committed.
+- [ ] 1.4 Add tests for individual `ReplayableCommand#replayOrRetry` disablement while pending background commands exist.
+- [ ] 1.5 Add tests for direct individual replay invocation or helper method behavior so pending background work cannot be bypassed outside UI disablement.
+- [ ] 1.6 Ensure replay/background tests cover the shared command-log applib behavior through JDO and JPA persistence where integration fixtures are required.
+
+## 2. Pending Background Replay Guard
+
+- [ ] 2.1 Introduce a reusable replay guard/helper that checks `CommandLogEntryRepository#findBackgroundAndNotYetStarted()` and returns a consistent wait-for-background-commands message.
+- [ ] 2.2 Wire the guard into `ReplayableCommand` or its `replayOrRetry` mixin so `disableAct` disables replay while background commands are pending.
+- [ ] 2.3 Ensure direct replay attempts do not execute a command while the replay pending-background guard is active.
+- [ ] 2.4 Wire the guard into replay manager single-next style actions so replay cannot proceed through manager entry points while background commands are pending.
+
+## 3. Selected Replay Loop
+
+- [ ] 3.1 Add a guard check before selected replay starts so existing pending background commands prevent the selected loop from running.
+- [ ] 3.2 Add a guard check after each selected command replay attempt and stop the loop when the just-replayed command leaves pending background commands.
+- [ ] 3.3 Preserve existing stop-on-replay-failure behavior and replay-state updates for commands already replayed before the pause.
+- [ ] 3.4 Ensure selected replay uses the same user-facing wait message as single-command replay disablement.
+
+## 4. Verification
+
+- [ ] 4.1 Run targeted command-log replay/background tests for the affected modules.
+- [ ] 4.2 Run `openspec validate "prevent-replay-with-pending-background-commands" --strict`.
+- [ ] 4.3 Run `openspec status --change "prevent-replay-with-pending-background-commands"` and confirm the change is apply-ready.