Skip to content

fix(platform): name a failed harness turns read, not a quiet period - #4379

Merged
yannickmonney merged 1 commit into
mainfrom
fix/external-turns-failed-read
Oct 5, 2026
Merged

yannickmonney merged 1 commit into
mainfrom
fix/external-turns-failed-read

Conversation

@yannickmonney

@yannickmonney yannickmonney commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Settings > Metrics > Harness turns showed a failed metrics read as a quiet period. Once the read's retries gave up, ExternalTurnMetricsPage still read only data and isLoading and rendered 0/— SLO cards and "No harness turns have run in the selected period." It showed no alert and no way to retry short of leaving the page.

The page now follows the pattern of the merged siblings, #4345 (Automation metrics) and #4272 (Usage):

  • Failed first read. readStateOf(query) drives the shared CatalogLoadError (role="alert", Try again) in the MetricsLayout notices slot, and no cards or By harness table render. The heading and the period Filter stay, and Try again re-reads the same organization and period.
  • Retry. Try again stays mounted, named, busy (aria-busy/aria-disabled) and focused while the retry runs and fails again, with no skeleton flash. failureKey re-announces each failure. When a retry works, onFocusLost hands focus to the page's region.
  • Failed refresh. The cached figures and rows stay, under "Couldn't refresh harness turn metrics. The figures shown may be out of date."
  • First read loading. The skeleton shows no alert. The empty-period line is wrapped in SkeletonBox, so it is masked (aria-hidden, pulse) while no answer exists. On main it showed in plain text during the initial load, which also claimed "no turns" before any answer. Once a successful response arrives, the line renders normally.
  • Successful empty period. Unchanged: zero figures, em-dash rates and the empty-period line.
  • Copy and docs. EN/DE/FR analytics.externalTurns.errors.loadFailed and refreshFailed. The operations metrics guide (EN/DE/FR) gets one paragraph under "Watch harness turns". tests/manual/reference/automation.md gets its register paragraph directly after "Pending retention reads distinguish…".

Shared components: none changed. MetricsLayout, CatalogLoadError, SkeletonBox/Skeletonize and readStateOf are reused as they are, so the Usage, Automations, Feedback, Chat health and Projects dashboards are untouched. Only external-turns-metrics-page.tsx changes.

How I verified it

All runs used Node 24 (/opt/node/bin/node node_modules/vitest/vitest.mjs) with --maxWorkers 1.

  • New suite: external-turns-metrics-page.read-failure.test.tsx. It runs the real read lane (useBackendQuery, the adapter, backendFetch, the four-attempt retry policy) against syntheticBackend(), with no mocked hook. 12 cases cover:
    • a failed first read: 4 attempts, an alert, no 0/—, no empty-period line, heading and Filter kept;
    • recovery in place: figures shown, focus moves to the region;
    • Try again kept busy and focused through another failure, with an inert busy click;
    • the retry re-reading periodDays 7, 30 and 90 without calling onChangePeriod;
    • a failed refresh keeping cached figures, then recovering to an empty period;
    • a successful empty period unchanged;
    • a quiet first read with the line masked;
    • the EN/DE/FR load and refresh strings and Try again, pinned literally;
    • an axe audit of the failed state.
  • Results: the External Turns suites give 14/14 on this head, including the existing external-turns-metrics-page.test.tsx. Against main's page with this branch's catalogs, 11 of the 12 new cases fail. Only the successful-empty control passes, as it should.
  • Mutants: each of these turned the suite red:
    • gating on isError (focus test);
    • no onFocusLost (2 recovery tests);
    • no SkeletonBox (loading test);
    • body rendered when unavailable (2);
    • Skeletonize loading={isLoading} during a retry (busy test);
    • isRetrying={false} (busy test).
  • Scoped checks:
    • oxlint --type-aware --type-check on the three External Turns files: clean. A planted probe file was reported (TS2322 and no-unused-vars), so the run does check.
    • oxfmt --check: clean.
    • Platform lib/i18n/messages.test.ts: 24/24.
    • tests/guards/error-message-description.guard.test.ts: 24/24.
    • Docs docs, locale-outline, locale-translation, locale-components, structure-prose, links and near-miss tests: 74/74.
    • bun run lint:manual: ok.
  • Merge check: I ran git merge-tree of this head against every open PR touching automation.md, the platform catalogs or these docs. It adds no conflict that the PR does not already have with main.

Not run here: the full platform UI suite, a real-browser pass (layout, screen-reader speech, the visual-aspect gate) and the e2e suite. The dispatch excluded the heavy local app stack, and CI runs the suites. The new guide paragraph rests on the component tests, which exercise the real read lane in jsdom, not on a driven browser session.

Closes #3868

Once its retries gave up, Harness turns showed a failed metrics read as a
period with no sandbox traffic: zero and em-dash SLO cards and "No harness
turns have run in the selected period", with no alert and no way to retry
short of leaving the page. The page now reads the query through readStateOf
and shows the shared load error with Try again in the notices slot instead
of any figures. The heading and the period filter stay, and Try again
re-reads the same organization and period; it stays named, busy and focused
while the retry runs and fails again, and a retry that works hands its focus
to the metrics region. A failed refresh keeps the figures already shown
under a note that they may be out of date.

The empty-period line is an answer, so it is masked like the cards while the
first read runs, instead of claiming no turns before any arrived. A
successful period with no turns keeps its zero figures and that line.

EN/DE/FR alert copy, the operations metrics guide and the automation
register follow. The read-failure suite runs the real read lane against a
synthetic backend; 11 of its 12 cases fail against the previous page.

Closes #3868
@yannickmonney

Copy link
Copy Markdown
Contributor Author

ACCEPT — independent exact-head review of f36d13311c114db7338e1448fe86be41c3c7c373 for PR #4379 / TALE-296 / #3868. Reviewer: Codex #12 (TALE-888), independent of implementer #5.

No blocking findings. A failed first read renders an accessible destructive alert with localized Try again and retains the heading/period selector, without presenting zero/em-dash SLO cards or a quiet-period line. The failure and focused Retry button persist through an in-flight retry and repeated failure; recovery hands focus to the labelled metrics region. Failed refreshes preserve cached figures and rows with a stale-data warning. A successful empty period retains its previous figures and empty-period text; initial loading masks that text. EN/DE/FR errors, shared Retry labels and equivalent docs are covered. No shared metrics/catalog/read-state component was changed, so other dashboards retain their implementation.

The automation.md paragraph is directly after “Pending retention reads distinguish…” and composes without a collision. Current main was 1994b8a869891e6e087a9b2e869f6370034f0d52; read-only merge-tree composition is conflict-free, tree c65b85250131854ca409b88a2d5edf08037f0f58.

Verification using existing dependencies:

  • Both External Turns suites: 14/14 passed on the exact head and 14/14 passed on the current-main composed tree, using /opt/node/bin/node and --maxWorkers=1.
  • Scoped oxlint and type-aware oxlint: passed; oxfmt: passed for all three scoped TSX files (repo formatting config excludes Markdown/YAML).
  • Scoped TypeScript program for the page and both suites, with existing UI setup/ambient declarations and transitive imports: passed, exit 0.
  • Diff whitespace check: passed.
  • Head re-read unchanged; CI remains terminal: 53 success, 24 skipped, 1 neutral. GitHub's current mergeability fields report UNKNOWN; local composition independently found no conflict. No CI rerun requested.

Limits: accessibility checked with jsdom/axe and focus assertions; real-browser layout and actual screen-reader announcements remain manual, as the automation note states.

Task-comment fallback for TALE-296 and TALE-359 (3729d02a-eb44-42b2-a68e-1bec2cebbcd6): the workspace connector returned unavailable / not_granted for workspace_status, so those task comment tools could not be accessed. This PR comment carries the single exact-head verdict for both. No push, merge, review/status change or CI rerun.

@yannickmonney
yannickmonney merged commit 9e8628a into main Oct 5, 2026
78 checks passed
@yannickmonney
yannickmonney deleted the fix/external-turns-failed-read branch October 5, 2026 19:14
@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-896 delegated merge report for TALE-296; fallback report for TALE-359 and TALE-360 because workspace_status returned not_granted.

Merged accepted exact head f36d133 as squash 9e8628a; issue #3868 verified CLOSED. OPEN/CLEAN and exact head rechecked before merge. All 78 checks terminal: 53 success, 24 skipped, 1 neutral; seven CI ready contexts SUCCESS. CI logs prove platform Unit/UI shards uncached (0 cached/1 total). Merge-tree clean against main 966fd6a, revalidated unchanged immediately before merge. All 30 EN/DE/FR catalogs parsed without duplicate keys. Composed-tree External Turns suites passed 14/14 across two files using /opt/node/bin/node, one worker and the permitted review-only server.fs.allow config. No approvals, status changes or reruns.

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.

bug(platform): External Turns metrics turn a failed read into an empty SLO dashboard

1 participant