fix(dashboard): orchestration canvas fase 3 — canvas polish (#12392) - #12983
Merged
Merged
Conversation
buildSourceStatuses stamped nowIso on every failing source at every poll, so the stale indicator reported "since the last poll" instead of the first failure — and, because snapshotContentKey serializes sources, the snapshot identity churned on every tick while any source was down. The failing branches now reuse the staleSince already held by that source in the previous status list, via the functional setStatuses updater (no ref read during render, no setState inside an effect body). Refs #12392
buildRootAndSourceEdges only materialized a placeholder SourceNode when the failing source had no node at all. A source that already had work nodes and then started failing (or went offline) kept its healthy-looking SourceNode forever: no ⚠, no stale styling, no `sourceStale` line — the operator saw a normal source while it was actually broken. Now every non-ok/offline source is flagged: when its SourceNode is missing the placeholder is created as before; when it exists, the node is replaced by a copy carrying `sourceIssue` and `staleSince`. The copy (never a mutation) keeps the function pure — the original object is still referenced by the caller's `parts`, the same trap the droppedByState aliasing fix covered. Tests: three cases in tests/unit/ui/orchestrationModel.test.ts — existing node starting to fail (flags set, work nodes kept, no duplicate node, input object untouched), existing node going offline (no invented staleSince), and a healthy source staying free of both fields. Refs #12392
Seven pointwise fixes on the Orchestration Canvas, each covered by a test: 1. Debounce x chip race: every chip/clear write in OrchestrationToolbar now cancels the pending search timer first. Left armed, it fired ~300ms later with a setParams closed over the pre-chip query string and silently reverted the chip. 2. The search input carries an aria-label (searchPlaceholder) — the placeholder alone is not an accessible name. 3. parseCsvSet trims each token, so `?state=running, failed` parses like the unpadded form instead of dropping the padded value. 4. toggleCsv was duplicated in the toolbar and the page client; both now import the single definition from the new model/urlParams.ts (pure, never mutates its inputs). 5. AgentsTab tells "nothing running" apart from "the filter matched nothing": with an active filter and no work node it renders noMatches + a clear-filters button instead of the setup CTAs, which would be wrong advice there. 6. Particle cap: orchestrationToFlow stamps `particles` on every edge and turns it off above PARTICLE_EDGE_CAP (40) simultaneously active edges — StatusEdge then renders the colored stroke without its 3 SMIL particles per edge. 7. The drawer's error banner clears when an action succeeds, so a recovered failure does not stay on screen. Only `noMatches` is added to en.json here; the other locales are task B4. Refs #12392
Real translations for orchestration.noMatches in the 41 non-English locales, each one written against that file's own neighbouring keys (emptyTitle, stateRunning, searchPlaceholder) so the wording for "task" and "filter" matches what the locale already uses. No i18n:sync-ui, no __MISSING__ left. Adds the changelog fragment for the nine PR-B fixes. Closes #12392
This was referenced Sep 8, 2026
4 of 5 tasks
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…zapw#12392) (diegosouzapw#12983) * fix(dashboard): keep the first failure timestamp in sourceStale buildSourceStatuses stamped nowIso on every failing source at every poll, so the stale indicator reported "since the last poll" instead of the first failure — and, because snapshotContentKey serializes sources, the snapshot identity churned on every tick while any source was down. The failing branches now reuse the staleSince already held by that source in the previous status list, via the functional setStatuses updater (no ref read during render, no setState inside an effect body). Refs diegosouzapw#12392 * fix(dashboard): flag a source that starts failing after it had data buildRootAndSourceEdges only materialized a placeholder SourceNode when the failing source had no node at all. A source that already had work nodes and then started failing (or went offline) kept its healthy-looking SourceNode forever: no ⚠, no stale styling, no `sourceStale` line — the operator saw a normal source while it was actually broken. Now every non-ok/offline source is flagged: when its SourceNode is missing the placeholder is created as before; when it exists, the node is replaced by a copy carrying `sourceIssue` and `staleSince`. The copy (never a mutation) keeps the function pure — the original object is still referenced by the caller's `parts`, the same trap the droppedByState aliasing fix covered. Tests: three cases in tests/unit/ui/orchestrationModel.test.ts — existing node starting to fail (flags set, work nodes kept, no duplicate node, input object untouched), existing node going offline (no invented staleSince), and a healthy source staying free of both fields. Refs diegosouzapw#12392 * fix(dashboard): canvas polish batch (diegosouzapw#12392) Seven pointwise fixes on the Orchestration Canvas, each covered by a test: 1. Debounce x chip race: every chip/clear write in OrchestrationToolbar now cancels the pending search timer first. Left armed, it fired ~300ms later with a setParams closed over the pre-chip query string and silently reverted the chip. 2. The search input carries an aria-label (searchPlaceholder) — the placeholder alone is not an accessible name. 3. parseCsvSet trims each token, so `?state=running, failed` parses like the unpadded form instead of dropping the padded value. 4. toggleCsv was duplicated in the toolbar and the page client; both now import the single definition from the new model/urlParams.ts (pure, never mutates its inputs). 5. AgentsTab tells "nothing running" apart from "the filter matched nothing": with an active filter and no work node it renders noMatches + a clear-filters button instead of the setup CTAs, which would be wrong advice there. 6. Particle cap: orchestrationToFlow stamps `particles` on every edge and turns it off above PARTICLE_EDGE_CAP (40) simultaneously active edges — StatusEdge then renders the colored stroke without its 3 SMIL particles per edge. 7. The drawer's error banner clears when an action succeeds, so a recovered failure does not stay on screen. Only `noMatches` is added to en.json here; the other locales are task B4. Refs diegosouzapw#12392 * chore(dashboard): canvas polish i18n + changelog Real translations for orchestration.noMatches in the 41 non-English locales, each one written against that file's own neighbouring keys (emptyTitle, stateRunning, searchPlaceholder) so the wording for "task" and "filter" matches what the locale already uses. No i18n:sync-ui, no __MISSING__ left. Adds the changelog fragment for the nine PR-B fixes. Closes diegosouzapw#12392
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fase 3 do Orchestration Canvas, PR-B: os nove itens de dívida da issue #12392, todos com causa já diagnosticada nas reviews da Fase 2. Nenhuma feature nova.
Spec:
_tasks/superpowers/specs/2026-09-03-orchestration-canvas-fase3-design.md· Plano:_tasks/superpowers/plans/2026-09-03-orchestration-canvas-fase3.md.Os dois itens de fundo
staleSinceavançava a cada poll (hooks/useOrchestrationSnapshot.ts).buildSourceStatusescarimbavanowIsoem toda fonte que falhasse, em todo tick. Duas consequências: a linha de stale dizia "parado desde o último poll" por mais tempo que a fonte estivesse fora, e — comosnapshotContentKeyserializa a lista de fontes — a identidade do snapshot mudava a cada 5s enquanto qualquer fonte estivesse quebrada, refazendo o render. Agora as ramificações de falha reusam ostaleSinceque aquela fonte já tinha no status anterior, via o updater funcional desetStatuses(sem ref lido em render, sem setState em corpo de efeito).Fonte que TINHA dados e passa a falhar não sinalizava (
model/mergeSnapshot.ts).buildRootAndSourceEdgessó marcavasourceIssuena fonte sem nó nenhum, materializando um placeholder. Uma fonte que caía no meio da sessão mantinha o SourceNode com cara de saudável para sempre. Agora toda fonte!ok/offlineé sinalizada: sem nó, cria o placeholder como antes; com nó, o nó é substituído por uma cópia{ ...node, sourceIssue, staleSince }— nunca mutado, porque o objeto original segue referenciado porpartse a função é pura.Os sete pontuais
setParamsfechado sobre a query anterior e revertia o chip em silêncio.aria-labelno input de busca: o placeholder sozinho não é nome acessível.parseCsvSetfaztrimpor token, então?state=running, failedparseia como a forma sem espaço em vez de descartar o valor.toggleCsvestava duplicado na toolbar e no page client; passou paramodel/urlParams.ts, definido uma vez.AgentsTabdistingue "nada rodando" de "o filtro não casou nada": com filtro ativo e zero work nodes mostranoMatches+ botão de limpar filtros, em vez dos CTAs de configuração, que ali seriam conselho errado.orchestrationToFlowdesligaparticlese oStatusEdgerenderiza só o traço colorido, sem as 3 partículas SMIL por aresta.Divergências entre plano e código, resolvidas pelo código
toggleCsv(raw: string | null, value: string). As duas cópias reais usam(current: ReadonlySet<T>, value: T)e nenhum caller tem o CSV cru em mãos. Ficou na forma baseada emSet; comportamento observável idêntico (lista vazia ⇒null, param sai da URL).orchestrationToFlownão tinha contagem de arestas ativas — foi criada aqui, sobre as arestas visíveis, que é o que o operador enxerga.SourceNodeexistente escrevi sósourceIssue/staleSince, nãosublabel: o haystack de busca defilterSnapshot.tsélabel + sublabel + id, e escrever "error" ali faria a fonte casar com a query "error".Testes
TDD em todas as tasks — teste escrito, visto falhar, depois passar.
useOrchestrationSnapshot.test.tsx(staleSince fixado em dois polls seguidos, re-carimbado após recuperação, identidade estável do snapshot),orchestrationModel.test.ts(nó existente que passa a falhar, com prova de pureza do objeto de entrada),orchestrationUrlParams.test.ts(novo),orchestrationTabs/Drawer/Nodes/ToFlow/Page. Duas asserçõesdeepEqualpré-existentes foram estendidas comparticles, nenhuma removida ou enfraquecida.i18n:
noMatchestraduzida de verdade nos 41 locales, cada uma escrita contra as chaves vizinhas do próprio arquivo (emptyTitle,stateRunning,searchPlaceholder). Semi18n:sync-ui, sem__MISSING__.Gates
typecheck:corelimpo ·check:dashboard-typecheckOK (206 erros pré-existentes, dentro do baseline) ·check:cyclesOK ·test:vitest465/465 · lint exit 0 nos 17 arquivos tocados · i18n glossary/ratio/ui-coverage/value-drift PASS.Quatro vermelhos vêm do tip de
release/v3.8.51, nenhum desta PR:check:docs-alli18n:checkdocs/mudaram desde a última traduçãotests/unit/zai-web-silent-empty-repro.test.tsbad_gatewaysem atualizar o testelintopen-sse/services/combo.ts,tlsClientBase.ts,useProviderConnections.ts,catalogCache.tsNenhum desses arquivos é tocado por esta PR.
Closes #12392
Atualização sobre os vermelhos herdados (reproduzidos no tip limpo): as quatro shards de
Unit Tests fast-pathtambém estão vermelhas na base. Prova de isolamento: a #12983 toca só UI do dashboard, i18n e changelog, e apresenta exatamente o mesmo conjunto de falhas da #12988, que toca rotas A2A/Conductor. Rodando no checkout principal emd6f31501, sem nenhuma alteração aplicada:Inventário por shard e o resto da evidência estão em #12732 (comment). Nenhum dos arquivos envolvidos é tocado por esta PR.