From 012044f0bffb8cb84db4ecac71d5da58e6463804 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Fri, 8 May 2026 02:59:31 +0200 Subject: [PATCH] fix(form-builder): FormFailureRetryService writes failure_response_code + apply_completed_at on retry failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per ARCH-BINDINGS §7.1 v1.2 retry-service asymmetry note + RFC-WS-6 §Q3 v1.3 addition 2. recordFailure() now mirrors ApplyBindingsOnFormSubmit's outer-transaction failure path: 1. failure_response_code via FormBindingExceptionClassifier::classify($e). Same classification logic as the listener — single behaviour-change point per the v1.3-delta D1 design. 2. apply_completed_at = now() — closes the asymmetry where the listener wrote this column on both happy and failure paths but the retry service only wrote it on the success path. recordSuccess() unchanged — already writes apply_completed_at via the shared transaction block in retry(). Co-Authored-By: Claude Opus 4.7 --- .../FormBuilder/FormFailureRetryService.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/api/app/Services/FormBuilder/FormFailureRetryService.php b/api/app/Services/FormBuilder/FormFailureRetryService.php index 7361bf83..55a4c91a 100644 --- a/api/app/Services/FormBuilder/FormFailureRetryService.php +++ b/api/app/Services/FormBuilder/FormFailureRetryService.php @@ -8,6 +8,7 @@ use App\Enums\FormBuilder\ApplyStatus; use App\Exceptions\FormBuilder\FailureNotRetriableException; use App\Exceptions\FormBuilder\ParentSubmissionGoneException; use App\FormBuilder\Bindings\FormBindingApplicator; +use App\FormBuilder\Bindings\FormBindingExceptionClassifier; use App\Models\FormBuilder\FormSubmission; use App\Models\FormBuilder\FormSubmissionActionFailure; use App\Models\FormBuilder\FormSubmissionActionFailureRetryAttempt; @@ -30,6 +31,13 @@ use Throwable; * exception details, increment retry_count. Parent's own * exception_class / exception_message stay audit-immutable — * they represent the FIRST failure. + * + * v1.3-delta D2 (per ARCH-BINDINGS §7.1 v1.2 + RFC-WS-6 §Q3 v1.3 addition 2): + * - recordFailure now mirrors ApplyBindingsOnFormSubmit's outer-txn path: + * writes failure_response_code via FormBindingExceptionClassifier and + * apply_completed_at = now() (closes the asymmetry where the listener + * wrote this column on both happy and failure paths but the retry + * service only wrote it on the success path). */ final readonly class FormFailureRetryService { @@ -125,9 +133,18 @@ final readonly class FormFailureRetryService ->whereKey($failure->id) ->update(['retry_count' => DB::raw('retry_count + 1')]); + // Per ARCH-BINDINGS §7.1 v1.2 retry-service asymmetry note + + // RFC-WS-6 §Q3 v1.3 addition 2 — mirror ApplyBindingsOnFormSubmit's + // outer-transaction failure path: same failure_response_code + // classifier + apply_completed_at write. Single behaviour-change + // point per the v1.3-delta D1 design. FormSubmission::query() ->whereKey($submission->id) - ->update(['apply_status' => ApplyStatus::FAILED->value]); + ->update([ + 'apply_status' => ApplyStatus::FAILED->value, + 'apply_completed_at' => now(), + 'failure_response_code' => FormBindingExceptionClassifier::classify($e), + ]); return $attempt; });