Compare commits

...

2 Commits

Author SHA1 Message Date
José René White Enciso
6d4251b3b3 docs(kitchen-ops-bff): align lifecycle board reads 2026-03-31 19:59:32 -06:00
José René White Enciso
b6b6c8fcf6 feat(kitchen-ops-bff): bridge kitchen state into shared flow 2026-03-31 18:53:28 -06:00
6 changed files with 43 additions and 15 deletions

View File

@ -30,4 +30,7 @@ This BFF exposes kitchen board, claim or release, transition, and priority workf
## Notes ## 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. - `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.
- Board reads now rely on the lifecycle-driven kitchen ticket materialization in `kitchen-service`, which means newly accepted restaurant orders can appear without a stack reset.
- 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. - Correlation IDs are preserved through Thalos session checks and kitchen-service calls.

View File

@ -9,11 +9,10 @@ kitchen-ops-bff
- Epic 3: Improve observability and operational readiness for demo compose environments. - Epic 3: Improve observability and operational readiness for demo compose environments.
## Domain-Specific Candidate Features ## Domain-Specific Candidate Features
- Order lifecycle consistency and state transitions. - Kitchen board projection over linked restaurant tickets.
- Kitchen queue and dispatch optimization hooks. - Kitchen work-item claim, release, transition, and priority workflows aligned with canonical kitchen-service state transitions.
- Kitchen work-item claim, release, transition, and priority workflows aligned with kitchen-service. - Cross-app state continuity from customer or waiter submission through kitchen execution and POS readiness.
- Operations control-plane policies (flags, service windows, overrides). - Operations control-plane policies (flags, service windows, overrides).
- POS closeout and settlement summary alignment.
## Documentation Contract ## Documentation Contract
Any code change in this repository must include docs updates in the same branch. Any code change in this repository must include docs updates in the same branch.

View File

@ -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` - Integration artifact path: `greenfield/demo/restaurant/docker-compose.yml`
## Known Limitations ## 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. - Demo PostgreSQL seeds validate integration contracts and smoke determinism, but do not yet imply full persistence implementation parity.

View File

@ -75,7 +75,11 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
{ {
using var response = await httpClient.PostAsJsonAsync( using var response = await httpClient.PostAsJsonAsync(
"internal/kitchen/orders/transition", "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); cancellationToken);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@ -88,8 +92,8 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
return new TransitionKitchenWorkItemResponse( return new TransitionKitchenWorkItemResponse(
payload.OrderId, payload.OrderId,
payload.TicketId, payload.TicketId,
payload.PreviousState, MapEdgeState(payload.PreviousState),
payload.CurrentState, MapEdgeState(payload.CurrentState),
payload.Transitioned, payload.Transitioned,
payload.Error); payload.Error);
} }
@ -155,7 +159,27 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
string ClaimedBy, string ClaimedBy,
string Message); 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( private sealed record TransitionKitchenWorkItemResponsePayload(
string OrderId, string OrderId,

View File

@ -4,4 +4,5 @@ public sealed record TransitionKitchenWorkItemRequest(
string OrderId, string OrderId,
string TicketId, string TicketId,
string TargetState, string TargetState,
string UpdatedBy); string UpdatedBy,
string? ContextId = null);

View File

@ -65,7 +65,7 @@ public sealed class KitchenWorkflowServiceClientTests
{ {
var adapter = new KitchenWorkflowServiceClient(CreateClient(""" var adapter = new KitchenWorkflowServiceClient(CreateClient("""
{ {
"orderId": "CO-1001", "orderId": "ORD-1001",
"ticketId": "KT-1001", "ticketId": "KT-1001",
"previousState": "Queued", "previousState": "Queued",
"currentState": "Preparing", "currentState": "Preparing",
@ -75,11 +75,11 @@ public sealed class KitchenWorkflowServiceClientTests
""")); """));
var response = await adapter.TransitionWorkItemAsync( 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); CancellationToken.None);
Assert.True(response.Transitioned); Assert.True(response.Transitioned);
Assert.Equal("Preparing", response.CurrentState); Assert.Equal("Cooking", response.CurrentState);
} }
[Fact] [Fact]
@ -124,14 +124,14 @@ public sealed class KitchenWorkflowServiceClientTests
private const string BoardPayload = """ private const string BoardPayload = """
{ {
"contextId": "demo-context", "contextId": "demo-context",
"summary": "Kitchen board shows queued, preparing, and ready lanes for the current service context.", "summary": "Kitchen board now reflects persisted tickets linked to shared restaurant orders.",
"lanes": [ "lanes": [
{ {
"lane": "queued", "lane": "queued",
"items": [ "items": [
{ {
"workItemId": "WK-1001", "workItemId": "WK-1001",
"orderId": "CO-1001", "orderId": "ORD-1001",
"ticketId": "KT-1001", "ticketId": "KT-1001",
"tableId": "T-08", "tableId": "T-08",
"station": "hot-line", "station": "hot-line",