Skip to content

feat(pipeline-monitor): per-scope progress tab — live ETA + stalled detection (FDD-OPS-015 UI)#8

Merged
nascimentolimaandre-cloud merged 1 commit intomainfrom
feat/ops-015-pipeline-jobs-ui
Apr 29, 2026
Merged

feat(pipeline-monitor): per-scope progress tab — live ETA + stalled detection (FDD-OPS-015 UI)#8
nascimentolimaandre-cloud merged 1 commit intomainfrom
feat/ops-015-pipeline-jobs-ui

Conversation

@nascimentolimaandre-cloud
Copy link
Copy Markdown
Owner

Summary

Fecha a entrega do FDD-OPS-015 — backend (PR #6) shipou a API /pipeline/jobs, esta PR ship a UI tab "Per-scope" no Pipeline Monitor. Operadores agora respondem "is BG project still progressing or stuck?" em segundos via interface visual, sem precisar curl/grep.

What you see

4ª aba "Per-scope" no route /pipeline-monitor:

Header summary

Counts ao vivo: running (spinner), stalled (warning), done, failed — operator olhada de 1s.

Tabela (1 row por scope ativo/recente)

Coluna Conteúdo
Scope label parsed: jira:project:BG → "BG" + source badge "JIRA"
Entity issues, pull_requests, deployments, sprints
Progress items_done / items_estimate + % + colored bar
Status badge com icon (running spin, stalled alert, done check, failed X) + tooltip de erro
Rate items/sec formatado (5/s, 84.2/s, 1.2k/s)
ETA formatado human (90s, 5m 23s, 2h 14m, "done")
Last activity relative (12s ago, 3m ago, 2h ago)

Filters

  • By entity_type (issues / prs / deploys / sprints / all)
  • By status (running / done / failed / paused / all)

Polling

5s refetchInterval, 2s staleTime — live updates sem refresh.

Stalled detection (operator signal)

Quando backend reporta isStalled=true (status='running' AND no progress for >60s), a row recebe:

  • 🟡 Background tinted warning (subtle yellow)
  • ⚠ AlertCircle icon
  • "STALLED" badge replacing normal status

Operator vê em segundos qual scope precisa de atenção — sem precisar dar curl em /pipeline/jobs.

No-estimate graceful handling

Se backend não conseguiu pre-flight count (timeout/source unsupported), itemsEstimate=null:

  • Progress bar mostra stripe indeterminado (15% width como hint visual)
  • Pct label exibe "?"
  • ETA exibe "—"

UI não trava; mostra "fetching, taxa X/s, total desconhecido".

Anti-surveillance

ProgressJobSchema.strict() em tests rejeita author/assignee injetados no payload — matches o invariant em metrics-inconsistencies §8.9. Schema completamente metadata-only.

Files

Frontend

  • types/pipeline.ts: ProgressJob, ProgressJobStatus, ProgressJobPhase
  • lib/api/pipeline.ts: fetchPipelineJobs(params) com query filtering
  • hooks/usePipeline.ts: usePipelineJobs (5s polling, 2s stale)
  • components/pipeline/PerScopeJobs.tsx (novo, ~330 linhas)
  • routes/_dashboard/pipeline-monitor.tsx: tab "Per-scope" wired

Tests

  • tests/contract/schemas/pipeline-jobs.schema.ts: Zod schema
  • tests/contract/pipeline-jobs-contract.test.ts: 13 tests
Test Cobre
A-G Shape validity (running, no-estimate, stalled, failed, done, empty, multi-job array)
H-I Anti-surveillance (rejects author/assignee extras)
J-M Defensive bounds (negative items, >100 pct, unknown phase/status enums)

Validation

Check Status
TypeScript build (npx tsc --noEmit)
ESLint (no new warnings introduced)
Frontend tests (npx vitest run) 163/163 (13 novos + 150 anteriores)
Live API smoke test (curl /pipeline/jobs) ✅ 10+ scopes returned, isStalled correctly computed
Wire-shape matches schema ✅ camelCase JSON, all fields parsed

Stats

  • 7 arquivos, +789 / -2 linhas
  • 1 componente novo (PerScopeJobs.tsx)
  • 2 contract test files (schema + 13 tests)
  • 4 file edits (types, api, hook, route)
  • 1 nova tab no Pipeline Monitor (4ª: Visão geral, Pipeline, Times, Per-scope)

Test plan

  • CI roda 4 gates verdes
  • cd packages/pulse-web && npx vitest run tests/contract/pipeline-jobs-contract.test.ts → 13 verde
  • npm run devhttp://localhost:5173/pipeline-monitor → clicar tab "Per-scope"
  • Esperar 5s, observar polling refresh; counts atualizando
  • Mudar filter "All entities" → "Issues" → tabela filtra
  • Mudar filter "All statuses" → "Failed" → mostra apenas failed (ou empty state)
  • Em sync-worker stuck/VPN-drop, verificar que stalled badge aparece após >60s

Dependencies

🤖 Generated with Claude Code

…d detection (FDD-OPS-015 UI)

Closes the FDD-OPS-015 deliverable list — backend (PR #6) shipped the API,
this PR ships the operator-facing UI tab "Per-scope" no Pipeline Monitor.

WHAT YOU SEE

A 4ª aba "Per-scope" no `/pipeline-monitor` route mostra **live**:

  - Sumário no header: counts de "running", "stalled", "done", "failed"
  - Tabela com 1 row por scope ativo/recente:
      Scope (jira:project:BG / github:repo:foo/bar)
      Entity type
      Progress bar com itemsDone / itemsEstimate + percentage
      Status badge (running spinner, stalled warning, done check, failed X)
      Rate (items/sec)
      ETA formatado (90s, 5m 23s, 2h 14m)
      Last activity (relative: "12s ago", "3m ago")
  - Filters: by entity_type (issues/prs/deploys/sprints) + by status
  - Polling cada 5s (live update, stale at 2s)

STALLED DETECTION

Quando backend reporta `isStalled=true` (status='running' AND no progress
for >60s), a row ganha:
  - Background tinted warning (subtle yellow)
  - AlertCircle icon
  - "STALLED" badge replaces normal status

Operator vê em segundos qual scope precisa de atenção.

NO-ESTIMATE GRACEFUL HANDLING

Se backend não conseguiu pre-flight count (timeout, source unsupported),
itemsEstimate=null:
  - Progress bar mostra stripe indeterminado (15% width como hint)
  - Pct label exibe "?"
  - ETA exibe "—"

Não trava UI; operador vê "fetching, taxa X/s, total desconhecido".

ANTI-SURVEILLANCE

Schema Zod strict() em testes rejeita `author`/`assignee` no payload —
matches o invariant em metrics-inconsistencies §8.9.

FILES

Frontend:
  - types/pipeline.ts: ProgressJob, ProgressJobStatus, ProgressJobPhase
  - lib/api/pipeline.ts: fetchPipelineJobs (com query params)
  - hooks/usePipeline.ts: usePipelineJobs (5s polling, 2s staleTime)
  - components/pipeline/PerScopeJobs.tsx (NEW, ~330 lines)
  - routes/_dashboard/pipeline-monitor.tsx: 4ª tab "Per-scope"

Tests:
  - tests/contract/schemas/pipeline-jobs.schema.ts (Zod schema)
  - tests/contract/pipeline-jobs-contract.test.ts (13 tests):
      A-G: shape validity (running/no-estimate/stalled/failed/done/empty/array)
      H-I: anti-surveillance (rejects author/assignee)
      J-M: defensive bounds (negative items, >100 pct, unknown enums)

VALIDATION

  ✅ TypeScript build clean (npx tsc --noEmit)
  ✅ ESLint clean (no new warnings)
  ✅ 163/163 frontend tests pass (13 novos + 150 anteriores)
  ✅ Live API smoke test: 10+ scopes returned, isStalled correctly computed
  ✅ JSON wire-shape matches schema (verified via curl during dev)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nascimentolimaandre-cloud nascimentolimaandre-cloud merged commit 3821c64 into main Apr 29, 2026
4 checks passed
@nascimentolimaandre-cloud nascimentolimaandre-cloud deleted the feat/ops-015-pipeline-jobs-ui branch April 29, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant