9.3 KiB
WMS-Native Tidy Recovery Design
Date: 2026-07-23
Status: Approved Supersedes: the WCS task-state recovery portions of2026-07-22-ft546-tidy-and-batch-replay-design.md
Context
A tidy step is executed as a normal WMS move job with in_or_out=28. The WMS
creates that job, completes inventory movement inside alg.usp_complete_move_job,
archives it to mydo_job_finish, advances alg.tidy_step, and releases workflow
locks through the transactional jobFinish callback path.
The current restart reconciler also polls a WCS tidyTaskState endpoint every
five seconds. The production WCS does not implement that endpoint; only the
replay simulator does. More importantly, WCS task state is not the authoritative
source for WMS job or tidy-workflow completion.
Goals
- Make WMS persisted state the only authority for tidy completion and restart recovery.
- Preserve normal
in_or_out=28job creation and callback execution. - Repair a locally provable completed step idempotently after restart.
- Pause a workflow only when WMS-local evidence is missing or contradictory.
- Retain active locks for
PAUSED_RECOVERYworkflows. - Remove the unused WCS task-state HTTP dependency and configuration.
Non-Goals
- Do not change the WCS vehicle snapshot endpoint; manual capacity still needs live vehicle availability.
- Do not infer physical completion from vehicle telemetry.
- Do not auto-complete a move without a matching WMS finish job and matching inventory/space evidence.
- Do not repair unrelated legacy WMS/WCS endpoint mismatches in this change.
- Do not change outbound-relocation workflow recovery.
Considered Approaches
A. WMS-native evidence recovery (selected)
Use alg.tidy_workflow, alg.tidy_step, mydo_job, mydo_job_finish,
bas_stock, bas_cargo_space, and alg.tidy_space_lock. This keeps one source
of truth, requires no new WCS API, and can distinguish safe repair from a true
local inconsistency.
B. Keep WCS state as advisory data
Query WCS only after a timeout and compare it with WMS state. This improves cross-system diagnostics but still requires a new production WCS endpoint, defines a second state contract, and cannot replace WMS transactional evidence.
C. Callback-only recovery
Keep locks and wait forever for a callback. This is simple but leaves orphaned or partially imported records with no deterministic diagnosis or repair path.
Approach A is selected. WCS task querying can be introduced later as operational telemetry without making it a completion authority.
Authoritative Data
For each active tidy workflow, recovery evaluates its first incomplete step and the deterministic move-job ID:
TDY-<workflowId>-<stepId>
The recovery projection contains:
- workflow ID, workflow status, origin, and active lock count;
- step ID, step number, step status, source space, target space, and tray code;
- matching active
mydo_jobpresence, status, type, source, target, and tray; - matching
mydo_job_finishpresence, status, type, source, target, and tray; - stock location and source/target cargo-space occupancy.
An active or finished job is a match only when all of these values agree with the tidy step:
job_id, in_or_out=28, tray_code, cargo_space_id, target_cargo_space_id
Recovery Decision Table
| WMS-local evidence | Action |
|---|---|
Workflow already PAUSED_RECOVERY |
Leave unchanged; do not poll or release locks. |
All workflow steps are COMPLETED, and no deterministic tidy job remains active |
Idempotently complete the workflow and release its locks. |
Matching active move job has status 1, 2, or 3; stock remains at source; source contains the tray; target does not contain it |
Leave the step and locks active; normal dispatch/callback processing continues. |
| Matching active move job exists but stock or cargo-space evidence is not in the expected pre-move state | Pause with reason RECOVERY_ACTIVE_EVIDENCE_INVALID; retain locks. |
No active job; matching finish job has status 4; stock is at target; source is empty; target contains the tray |
Replay only the tidy step/workflow advancement using callback stage RECOVERY_FINISHED. Do not execute inventory movement again. |
| Active or finished job exists but its type, tray, source, or target differs | Pause the workflow and active step with reason RECOVERY_JOB_MISMATCH; retain locks. |
| Finish job exists but status or inventory/space evidence is not terminal and consistent | Pause with reason RECOVERY_FINISH_EVIDENCE_INVALID; retain locks. |
| Neither matching active nor matching finish job exists | Pause with reason RECOVERY_JOB_MISSING; retain locks. |
Database/query failure is not evidence of inconsistency. The transaction rolls back, the scheduler logs the failure, and the workflow remains unchanged until the next reconciliation cycle.
Components
TidyWorkflowRecoveryService
The service remains the restart and periodic reconciliation boundary, but it
depends only on WmsAlgorithmMapper and TidyWorkflowTransaction. It never
performs HTTP. It evaluates mapper projections inside the WMS transaction
manager and applies exactly one decision per workflow per cycle.
TidyRecoveryEvidence
A new immutable mapper projection carries the authoritative fields listed above. It contains data only; decision logic stays in the recovery service.
TidyWorkflowTransaction
Normal MOVE_FINISHED and OUTBOUND_FINISHED callback behavior remains
unchanged. A locally proven finish job is advanced through the same idempotent
path with callback stage RECOVERY_FINISHED. The special UNKNOWN callback
branch is removed; only the recovery service may enter PAUSED_RECOVERY.
Mapper SQL
Replace the WCS-task-oriented recovery selection with WMS-local evidence SQL. The query must use the deterministic job ID and left joins to both active and finish job tables. Finished evidence must also project stock and cargo-space state. Existing complete-step, complete-workflow, release-lock, pause-workflow, and pause-step operations remain the mutation boundary.
Configuration and deleted types
Delete:
WcsTaskState;WcsTaskStateClient;tidy-wcs-task-state-urlandtidy-wcs-task-state-timeout-msproperties;FORTUCKY_TIDY_WCS_TASK_STATE_URLand timeout overrides.
Keep tidy-wcs-snapshot-url and WcsVehicleSnapshotClient unchanged.
Concurrency and Idempotency
- Recovery runs in one WMS transaction.
- The deterministic job ID prevents a second move job for the same step.
- Callback-event insertion remains idempotent by workflow, step, job, and stage.
- A finish-job repair never calls
alg.usp_complete_move_job; inventory is not moved twice. - Concurrent duplicate callbacks may observe an already completed step, but next-job creation and resource release remain idempotent.
PAUSED_RECOVERYnever releases locks automatically.
Replay Changes
Remove the simulator /sim/task-state and /sim/unknown-task endpoints and the
FORTUCKY_TIDY_WCS_TASK_STATE_URL replay override.
The replay will add two WMS-local restart cases while the database is protected by the existing verified backup:
- Repairable completion: stop WMS after creating a tidy move job, complete
the move through
alg.usp_complete_move_jobwithout advancing the tidy step, restart WMS, and prove the step/workflow advances without a second inventory move. - Missing local job: stop WMS, remove the active replay move job while
leaving its workflow/step/locks, restart WMS, and prove
PAUSED_RECOVERY, reasonRECOVERY_JOB_MISSING, and retained locks.
Both fixture mutations are run-scoped, journaled, and removed by the existing database restore. They are test setup, not evidence patching.
Tests and Acceptance
Unit and mapper tests must prove:
- active matching job remains active and causes no mutation;
- matching terminal finish evidence advances exactly once;
- completed steps with no remaining active tidy job finish the workflow and release locks;
- active jobs with invalid pre-move inventory evidence pause safely;
- missing job pauses workflow and step while retaining locks;
- mismatched or invalid finish evidence pauses with the correct reason;
- an already paused workflow causes no repeated action;
- database/client configuration contains no WCS task-state dependency;
- normal and duplicate completion callbacks still pass.
Replay acceptance must prove:
- normal automatic and manual tidy behavior remains unchanged;
- repairable local completion is recovered after restart;
- missing local job enters
PAUSED_RECOVERYwith retained locks; - no task-state HTTP request appears in the event journal;
- database restoration still reports
mismatches=[].
Documentation
The existing 2026-07-22 design and plan remain historical records. Add a supersession note to their WCS task-state recovery sections, update the handoff document to describe WMS-native recovery, and generate a new final replay report instead of rewriting old evidence.
Risks
- Legacy or manually edited finish rows may look terminal but disagree with inventory. The design pauses rather than guessing.
- A workflow can remain paused until an operator repairs or reconciles the local inconsistency. Automatic lock release is intentionally prohibited.
- The replay uses controlled SQL mutations to reproduce crash boundaries; every such mutation must be logged and covered by final baseline restoration.