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
SUCCESSand resumes from the firstFAILEDstep - residual
RUNNINGsteps 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:
ExecutorServiceor 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
RUNNINGsteps toFAILED - skip every step already marked
SUCCESS - start execution at the first non-
SUCCESSstep
Preserve current semantics after the resume point:
- mark current step
RUNNING - on success mark
SUCCESS - on failure mark
FAILEDand 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.