# Fase 7 — Rollout e Produção **Programa:** Work Orders Board **Objetivo:** Piloto SHOC, rollout gradual, monitoramento de coexistência, sunset Blazor WO, cutover Lambda→API e deprecação legado. **Depende de:** [Fase 0](../phase-0/README.md) … [Fase 6](../phase-6/README.md) concluídas e validadas em staging. --- ## Gates - **Entrada:** [phase-7-gates-signoff.md](phase-7-gates-signoff.md) - **Saída:** piloto UAT, 100% SHOC, zero P1 × 2 semanas, Blazor sunset, Lambda cutover, ops health ativo --- ## Runbook de rollout (ordem obrigatória) 1. Fechar gates de entrada Fase 7 2. Deploy API com ops health + ingest (ingest desabilitado em prod inicialmente) 3. SHOC: feature flag board em staging → piloto 1–2 dispatchers em prod 4. UAT dispatchers ([roteiros abaixo](#uat-dispatchers)) 5. Rollout gradual SHOC até 100% 6. Habilitar dual-run Lambda (`ingest` + Dynamo) 7. Validar drift → cutover Lambda só API 8. `Sync:Enabled=false` 9. `BlazorWorkOrderSunset:Enabled=true` 10. `LegacyEndpoints:DeprecationEnabled=true` + data Sunset 11. Remover Sync/legado após período de aviso (30 dias) --- ## Endpoints Fase 7 ### `GET /api/workorders/ops/health` Saúde operacional para coexistência multi-consumidor. **Auth:** Admin JWT ```json { "lastWeekRolledRunUtc": "2026-06-23T00:05:12Z", "lastPastDueCacheRunUtc": null, "lastWeekRolledError": null, "lastPastDueCacheError": null, "syncRejectedLast24h": 3, "fieldLockCount": 142, "syncEnabled": true, "ingestEnabled": true, "legacyDeprecationEnabled": false, "legacySunsetDate": null, "checkedAtUtc": "2026-06-25T12:00:00Z" } ``` Ver [cloudwatch-alerts.md](cloudwatch-alerts.md). --- ### `POST /api/workorders/ingest` Ingest direto idempotente (substitui ponte DynamoDB para WOs). **Auth:** header `X-Ingest-Key` (config `WorkOrderIngest:ApiKey`) **Body (single ou batch):** ```json { "externalWorkOrderId": "ext-wo-12345", "description": "HVAC unit not cooling", "woStatus": "new", "severity": "2", "customer": "Acme Corp", "siteCode": "BK5", "building": "Building A", "address": "123 Main St", "dueDate": "2026-07-01T00:00:00Z", "dateReported": "2026-06-20T10:00:00Z", "scheduledStart": null, "sourceEmailS3Key": null, "createdAt": "2026-06-20T10:00:00Z" } ``` **Batch:** ```json { "items": [ { "externalWorkOrderId": "...", "description": "..." } ] } ``` **Response:** ```json { "created": 1, "updated": 0, "results": [{ "externalWorkOrderId": "ext-wo-12345", "workOrderId": 42, "created": true }] } ``` Mapeamento completo: [lambda-ingest-discovery.md](lambda-ingest-discovery.md) --- ## Matriz de endpoints | Tipo | Exemplos | Política Fase 7 | |------|----------|-----------------| | Board (SHOC) | `GET board`, `PATCH {id}/board`, `POST board` | **Produção** | | Slide-over | `GET {id}/detail`, completion-doc, media | **Produção** | | Ingest | `POST ingest` | Dual-run → produção | | Sync | `POST api/Sync/WorkOrders` | Desligar após cutover (`Sync:Enabled`) | | Legado | `GetWorkOrderList`, `AddWorkorder`, `ChangeStatus` | Deprecation headers | | Jobs | `POST jobs/week-rolled` | Ops Admin | --- ## Configuração ### API (`Api.SeaHavenIndustries`) ```json { "FrontendBaseUrl": "https://shoc.seahaven.com", "WorkOrderIngest": { "Enabled": true, "ApiKey": "${WORKORDER_INGEST_API_KEY}" }, "Sync": { "Enabled": true }, "LegacyEndpoints": { "DeprecationEnabled": false, "SunsetDate": "2026-12-31" } } ``` Env vars: ver [.env.example](../../../.env.example) ### Blazor (`SeaHavenIndustries`) ```json { "BlazorWorkOrderSunset": { "Enabled": false, "ShocBaseUrl": "https://shoc.seahaven.com" } } ``` Quando `Enabled=true`: nav Work Orders aponta para SHOC; mutações EF bloqueadas; banner nas páginas WO. --- ## UAT dispatchers Roteiro manual — marcar cada item antes de expandir rollout. | # | Cenário | Pass | |---|---------|------| | 1 | Login SHOC; carregar semana (Mon–Fri + Unscheduled) | [ ] | | 2 | Filtros: dispatcher, My WOs, tipo, busca contextual | [ ] | | 3 | Inline edit célula (status, assignee, due) | [ ] | | 4 | Conflito 409 — outro usuário editou; mensagem clara | [ ] | | 5 | Criar WO wizard (Incomplete → Scheduled auto) | [ ] | | 6 | Cancel soft; campos read-only pós-cancel | [ ] | | 7 | Advanced search cross-week | [ ] | | 8 | Slide-over: detail, comments, audit, media | [ ] | | 9 | Upload completion doc; docStatus no board | [ ] | | 10 | Badge PastDue / CarriedOver após segunda-feira | [ ] | **Sign-off UAT** | Dispatcher | Data | PO | |------------|------|-----| | | | | --- ## Piloto e rollout SHOC Controle **somente no frontend** (feature flags). Backend não filtra dispatchers. Critérios para avançar: - Zero P1 na semana do piloto - `GET board` latência aceitável (Tier S) - Vendor Portal regression verde --- ## Sunset Blazor Ver [data-ownership-model.md](../phase-0/data-ownership-model.md) §3. 1. Congelar `WorkorderService` (bugfix only) até data PO 2. Habilitar `BlazorWorkOrderSunset:Enabled` 3. Smoke final [smoke-checklist.md](../phase-0/smoke-checklist.md) Blazor EF --- ## Cutover Lambda Ver [lambda-ingest-discovery.md](lambda-ingest-discovery.md). --- ## Testes automatizados ```powershell dotnet test SeaHavenIndustries.Tests --filter "FullyQualifiedName~WorkOrderPhase7" ``` Suite: `WorkOrderPhase7CoexistenceTests.cs` --- ## Critérios de aceite Fase 7 - [x] `GET /api/workorders/ops/health` - [x] `POST /api/workorders/ingest` com API key - [x] `Sync:Enabled` feature flag - [x] Legacy deprecation middleware - [x] Blazor sunset config + guard mutações - [x] Documentação gates, UAT, Lambda mapping - [ ] Piloto prod (ops/PO) - [ ] 100% dispatchers SHOC (ops/PO) - [ ] Zero P1 × 2 semanas (ops/PO) - [ ] Lambda cutover executado (ops) --- ## Comunicação | Audiência | Mensagem | Quando | |-----------|----------|--------| | Dispatchers piloto | Novo board SHOC; suporte dedicado | Início piloto | | Todos dispatchers | Rollout gradual; treinamento | Durante rollout | | Usuários Blazor | WO migrou para SHOC; link direto | Sunset Blazor | | Engenharia | Sync desligado; usar ingest API | Pós-cutover |