cisd/docs/plans/2026-03-10-init-execute-async-resume-plan.md
2026-03-11 14:23:11 +08:00

7.9 KiB

Init Execute Async Resume Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Make /api/v1/init/tasks/{taskId}/execute return immediately after accepting work, then run init steps in the background and resume from the first non-SUCCESS step on retry.

Architecture: Keep the existing task table and step table as the source of truth. Split InitService.executeTask into an accept-and-dispatch path plus a background execution path, and use existing step statuses to determine where to resume. Add a lightweight in-process guard so one taskId cannot be started twice concurrently in the same application instance.

Tech Stack: Spring Boot service/controller layer, Java concurrency utilities, MyBatis-style repositories already present in the module, JUnit 5


Task 1: Lock down current behavior with service tests

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java
  • Test: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java

Step 1: Write the failing tests

Add tests that assert:

  • executeTask(taskId) returns before a deliberately blocking executor finishes all steps
  • retry skips steps already marked SUCCESS and resumes from the first FAILED step
  • residual RUNNING steps are treated as the resume point on retry
  • duplicate execute calls for the same running task do not launch a second execution

Step 2: Run tests to verify they fail

Run: mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test Expected: FAIL because execution is still synchronous and always starts from step 1.

Step 3: Write minimal test scaffolding

Extend the in-memory executor/repository helpers in the test file so they can:

  • block on a latch or sleep
  • count executed step codes
  • fail a chosen step once and succeed on retry

Do not change production code in this step.

Step 4: Run tests to verify RED is stable

Run: mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test Expected: FAIL with assertion mismatches that prove the new behavior is not implemented yet.

Step 5: Commit

Skip commit in the current dirty workspace per user instruction.

Task 2: Introduce async dispatch in InitService

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/service/InitService.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java

Step 1: Implement minimal async acceptance path

Refactor InitService so executeTask(taskId):

  • loads task and steps
  • returns a snapshot response quickly
  • submits real work to a background executor

Use a small in-process dispatcher owned by InitService:

  • ExecutorService or equivalent
  • active-task map keyed by taskId

Step 2: Keep the real execution logic in a dedicated method

Create a private method such as executeTaskInBackground(taskId) that:

  • reloads task and steps from repository
  • updates statuses
  • runs steps serially
  • persists each step result immediately
  • updates final task status

Step 3: Run focused tests

Run: mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test Expected: PASS

Step 4: Refactor for readability

Extract helpers only if needed:

  • build response snapshot
  • mark task running
  • remove active-task entry in finally

Do not add extra features.

Step 5: Commit

Skip commit in the current dirty workspace per user instruction.

Task 3: Implement resume-from-first-non-success semantics

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/service/InitService.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java

Step 1: Implement step selection logic

Before running steps in the background:

  • convert residual RUNNING steps to FAILED
  • skip every step already marked SUCCESS
  • start execution at the first non-SUCCESS step

Preserve current semantics after the resume point:

  • mark current step RUNNING
  • on success mark SUCCESS
  • on failure mark FAILED and stop

Step 2: Preserve response counters

Ensure successSteps in InitTaskExecuteResponse reflects the current number of successful steps, including successes from earlier runs.

Step 3: Run focused tests

Run: mvn -q -Dtest=InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep test Expected: PASS

Step 4: Run the earlier async tests again

Run: mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep test Expected: PASS

Step 5: Commit

Skip commit in the current dirty workspace per user instruction.

Task 4: Align controller and API-facing semantics

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/controller/InitInternalController.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/dto/InitTaskExecuteResponse.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java

Step 1: Make the API comments and DTO semantics match async behavior

Update JavaDoc/comments so they describe:

  • execute endpoint only accepts and starts background work
  • polling should use existing detail/step/log APIs

If needed, add a field only if tests prove it is necessary. Prefer reusing the current DTO unchanged.

Step 2: Run focused verification

Run: mvn -q -Dtest=InitServiceTest test Expected: PASS for all service tests, including the pre-existing create/query/execute cases.

Step 3: Review the controller surface

Check that:

  • route path stays unchanged
  • response wrapper stays unchanged
  • no new endpoint is introduced unnecessarily

Step 4: Commit

Skip commit in the current dirty workspace per user instruction.

Task 5: Final verification

Files:

  • Verify only

Step 1: Run init service tests

Run: mvn -q -Dtest=InitServiceTest test Expected: PASS

Step 2: Run init executor regression tests touched by earlier behavior changes

Run: mvn -q -Dtest=ConfigurableInitStepExecutorTest#shouldInjectDatabaseCredentialsWhenExecutingStandardDbApplyCommand,ConfigurableInitStepExecutorTest#shouldExposeStandardInitScriptTemplates,ConfigurableInitStepExecutorTest#shouldStartStandardCmspCmtpWithConfiguredCommand test Expected: PASS

Step 3: Run compile

Run: mvn -q -DskipTests compile Expected: PASS

Step 4: Review focused diff

Run: git diff -- src/main/java/com/cisd/tms/modules/init/service/InitService.java src/main/java/com/cisd/tms/modules/init/controller/InitInternalController.java src/main/java/com/cisd/tms/modules/init/dto/InitTaskExecuteResponse.java src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java docs/plans/2026-03-10-init-execute-async-resume-design.md docs/plans/2026-03-10-init-execute-async-resume-plan.md Expected: only async-dispatch, resume logic, and related documentation changes.

Step 5: Commit

Skip commit in the current dirty workspace per user instruction.