From b6b6c8fcf69e5105a307471564597faf15b34d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ren=C3=A9=20White=20Enciso?= Date: Tue, 31 Mar 2026 18:53:28 -0600 Subject: [PATCH] feat(kitchen-ops-bff): bridge kitchen state into shared flow --- docs/api/kitchen-ops-workflows.md | 2 ++ docs/roadmap/feature-epics.md | 7 ++-- docs/runbooks/containerization.md | 3 +- .../Adapters/KitchenWorkflowServiceClient.cs | 32 ++++++++++++++++--- .../TransitionKitchenWorkItemRequest.cs | 3 +- .../KitchenWorkflowServiceClientTests.cs | 8 ++--- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/docs/api/kitchen-ops-workflows.md b/docs/api/kitchen-ops-workflows.md index ec608c7..c328f11 100644 --- a/docs/api/kitchen-ops-workflows.md +++ b/docs/api/kitchen-ops-workflows.md @@ -30,4 +30,6 @@ This BFF exposes kitchen board, claim or release, transition, and priority workf ## Notes - `kitchen-service` currently exposes claim but not a dedicated release contract, so the release route reuses the claim validation path and projects a release-oriented response for the BFF edge. +- Transition requests now forward `ContextId` to `kitchen-service` so kitchen actions land in the correct shared restaurant lifecycle context. +- The BFF keeps temporary edge-state compatibility for existing web clients by translating `Cooking|Ready|Served` to the canonical kitchen-service states `Preparing|ReadyForPickup|Delivered`. - Correlation IDs are preserved through Thalos session checks and kitchen-service calls. diff --git a/docs/roadmap/feature-epics.md b/docs/roadmap/feature-epics.md index ae056ea..0c50f62 100644 --- a/docs/roadmap/feature-epics.md +++ b/docs/roadmap/feature-epics.md @@ -9,11 +9,10 @@ kitchen-ops-bff - Epic 3: Improve observability and operational readiness for demo compose environments. ## Domain-Specific Candidate Features -- Order lifecycle consistency and state transitions. -- Kitchen queue and dispatch optimization hooks. -- Kitchen work-item claim, release, transition, and priority workflows aligned with kitchen-service. +- Kitchen board projection over linked restaurant tickets. +- Kitchen work-item claim, release, transition, and priority workflows aligned with canonical kitchen-service state transitions. +- Cross-app state continuity from customer or waiter submission through kitchen execution and POS readiness. - Operations control-plane policies (flags, service windows, overrides). -- POS closeout and settlement summary alignment. ## Documentation Contract Any code change in this repository must include docs updates in the same branch. diff --git a/docs/runbooks/containerization.md b/docs/runbooks/containerization.md index 9d55318..1198f1d 100644 --- a/docs/runbooks/containerization.md +++ b/docs/runbooks/containerization.md @@ -39,5 +39,6 @@ docker run --rm -p 8080:8080 --name kitchen-ops-bff agilewebs/kitchen-ops-bff:de - Integration artifact path: `greenfield/demo/restaurant/docker-compose.yml` ## Known Limitations -- Kitchen-ops now delegates dashboard and work-item actions to `kitchen-service`, but the upstream kitchen workflow adapter still serves deterministic demo data rather than database-backed state. +- Kitchen-ops now delegates dashboard and work-item actions to `kitchen-service`, which projects persisted kitchen ticket state and syncs order progression back into the shared restaurant lifecycle. +- Temporary edge-state translation remains in place for existing web clients until the Stage 47 kitchen web task adopts the canonical kitchen-service states directly. - Demo PostgreSQL seeds validate integration contracts and smoke determinism, but do not yet imply full persistence implementation parity. diff --git a/src/Kitchen.Ops.Bff.Application/Adapters/KitchenWorkflowServiceClient.cs b/src/Kitchen.Ops.Bff.Application/Adapters/KitchenWorkflowServiceClient.cs index dd518fe..84f988c 100644 --- a/src/Kitchen.Ops.Bff.Application/Adapters/KitchenWorkflowServiceClient.cs +++ b/src/Kitchen.Ops.Bff.Application/Adapters/KitchenWorkflowServiceClient.cs @@ -75,7 +75,11 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch { using var response = await httpClient.PostAsJsonAsync( "internal/kitchen/orders/transition", - new TransitionKitchenWorkItemPayload(request.OrderId, request.TicketId, request.TargetState), + new TransitionKitchenWorkItemPayload( + request.OrderId, + request.TicketId, + MapKitchenServiceState(request.TargetState), + request.ContextId ?? "demo-context"), cancellationToken); response.EnsureSuccessStatusCode(); @@ -88,8 +92,8 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch return new TransitionKitchenWorkItemResponse( payload.OrderId, payload.TicketId, - payload.PreviousState, - payload.CurrentState, + MapEdgeState(payload.PreviousState), + MapEdgeState(payload.CurrentState), payload.Transitioned, payload.Error); } @@ -155,7 +159,27 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch string ClaimedBy, string Message); - private sealed record TransitionKitchenWorkItemPayload(string OrderId, string TicketId, string TargetState); + private static string MapKitchenServiceState(string edgeState) => edgeState switch + { + "Cooking" => "Preparing", + "Ready" => "ReadyForPickup", + "Served" => "Delivered", + _ => edgeState + }; + + private static string MapEdgeState(string serviceState) => serviceState switch + { + "Preparing" => "Cooking", + "ReadyForPickup" => "Ready", + "Delivered" => "Served", + _ => serviceState + }; + + private sealed record TransitionKitchenWorkItemPayload( + string OrderId, + string TicketId, + string TargetState, + string ContextId); private sealed record TransitionKitchenWorkItemResponsePayload( string OrderId, diff --git a/src/Kitchen.Ops.Bff.Contracts/Requests/TransitionKitchenWorkItemRequest.cs b/src/Kitchen.Ops.Bff.Contracts/Requests/TransitionKitchenWorkItemRequest.cs index 4207c95..9c24493 100644 --- a/src/Kitchen.Ops.Bff.Contracts/Requests/TransitionKitchenWorkItemRequest.cs +++ b/src/Kitchen.Ops.Bff.Contracts/Requests/TransitionKitchenWorkItemRequest.cs @@ -4,4 +4,5 @@ public sealed record TransitionKitchenWorkItemRequest( string OrderId, string TicketId, string TargetState, - string UpdatedBy); + string UpdatedBy, + string? ContextId = null); diff --git a/tests/Kitchen.Ops.Bff.Application.UnitTests/KitchenWorkflowServiceClientTests.cs b/tests/Kitchen.Ops.Bff.Application.UnitTests/KitchenWorkflowServiceClientTests.cs index d9a7eae..f4dc5f7 100644 --- a/tests/Kitchen.Ops.Bff.Application.UnitTests/KitchenWorkflowServiceClientTests.cs +++ b/tests/Kitchen.Ops.Bff.Application.UnitTests/KitchenWorkflowServiceClientTests.cs @@ -65,7 +65,7 @@ public sealed class KitchenWorkflowServiceClientTests { var adapter = new KitchenWorkflowServiceClient(CreateClient(""" { - "orderId": "CO-1001", + "orderId": "ORD-1001", "ticketId": "KT-1001", "previousState": "Queued", "currentState": "Preparing", @@ -75,11 +75,11 @@ public sealed class KitchenWorkflowServiceClientTests """)); var response = await adapter.TransitionWorkItemAsync( - new TransitionKitchenWorkItemRequest("CO-1001", "KT-1001", "Preparing", "chef-maya"), + new TransitionKitchenWorkItemRequest("ORD-1001", "KT-1001", "Cooking", "chef-maya", "demo-context"), CancellationToken.None); Assert.True(response.Transitioned); - Assert.Equal("Preparing", response.CurrentState); + Assert.Equal("Cooking", response.CurrentState); } [Fact] @@ -131,7 +131,7 @@ public sealed class KitchenWorkflowServiceClientTests "items": [ { "workItemId": "WK-1001", - "orderId": "CO-1001", + "orderId": "ORD-1001", "ticketId": "KT-1001", "tableId": "T-08", "station": "hot-line",