Repository navigation
Conversation
Adds two binding design spec files under .spec/tasks/SUPER-771/design/: - PreviousRunsList-failed-row.md: two-line failed row layout, select-text/cursor-text requirement, tooltip retention, accessibility rationale, and implementation notes (no sub-component extraction, inline cn() class swap, pl-4 error alignment). - AutomationFailureNotifier.md: provider lifecycle, useRef<Set<string>> dedup contract, showNative call shape (no clickTarget), mount point in layout.tsx, and relay status code translation table (cross-reference for AC-4 implementer).
Add humanRelayMessage() helper to convert relay HTTP status codes to user-facing copy (502→"Target machine is unreachable", 503→"Target machine was offline", etc.). Update RelayDispatchError message to use this translation. Revert incorrect err.name prefix in describeError that was making messages longer and more jargon-heavy. Update tests to expect the new human-readable format.
Implement two-line layout for failed runs (status dispatch_failed or skipped_offline): first line is [dot title time-ago], second line is inline error span with select-text and cursor-text classes for copy-ability. Button becomes flex-col items-start when failed. Error span shows truncated run.error or fallback "Run failed". Replace sham smoke tests with real behavior assertions that verify inline error span presence, correct CSS classes, and truncation.
Update AutomationFailureNotifier to use exact copy from spec: title is "Automation failed" (lowercase), body is run.error || "Run failed". Remove unnecessary statusLabel concatenation, run.title prefix, and silent: false parameter. Fix import ordering and line formatting to pass lint.
…unt only (AC-7) Revert the activeOrganizationId ternary restructure introduced in the prior cycle. This change is unrelated to AC-7 (notifier mount). The layout.tsx diff after this fix shows only the import and mount of AutomationFailureNotifier, as specified.
Three test files reimplemented production logic inside themselves and asserted on the copy. The desktop package has no DOM test infrastructure (@testing-library/react / happy-dom not installed), so renderer-side ACs (AC-1, AC-2, AC-5, AC-6, AC-7) require manual visual verification documented in the PR. describeError (AC-4) is unchanged from main and is implicitly covered by the AC-3 humanRelayMessage tests in relay-client.test.ts which exercise the real throw path end-to-end.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Ready to review this PR? Stage has broken it down into 4 individual chapters for you: Chapters generated by Stage for commit 93ee783 on May 26, 2026 2:07am UTC. |
Captures inline error rows and notification screenshots against the running desktop dev build. Rewrites the manual verification section to use direct automation_runs inserts — sidesteps the host-service registration/relay plumbing that's brittle in local dev. The dispatch flow E2E path remains documented as the optional 'full' verification. Screenshots committed to .spec/improvements/SUPER-771/screenshots/; PR body references them via absolute raw URLs on the fork branch.
…folding PreviousRunsList: wrap TooltipTrigger asChild in a <span> so hover events fire even when the inner <button> is disabled. Failed runs with v2_workspace_id=NULL disable the button, which browsers respect by suppressing pointer events — that was silently breaking the tooltip for exactly the rows users most want to inspect. Also removes the .spec/improvements/SUPER-771 and .spec/tasks/SUPER-771 scaffolding from the diff. Those artifacts (SCOPE.md, TICKET.md, design specs, evidence dumps, screenshots) drove the implementation process but aren't part of the shipped change. PR description retains the binding-scope summary + visual proof inline.
Greptile SummaryThis PR surfaces automation run failures loudly: failed rows now render the error inline (copyable), relay HTTP status codes are translated to human-readable copy at the persistence boundary in
Confidence Score: 3/5The relay translation and inline rendering are correct, but the notifier marks a run as notified before the Electron IPC call confirms delivery, silently losing the notification on failure with no recovery path. The server-side translation and renderer layout changes are solid and well-tested. The notifier's add-before-confirm pattern is the main concern: notifiedRunIdsRef.current.add(run.id) fires before showNative.mutate resolves, so an IPC failure silently drops the notification with no retry. The missing status filter is a growing-data concern. The cursor-text gap is cosmetic. AutomationFailureNotifier.tsx warrants a second look before merging.
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx | New provider that fires desktop notifications for failed automation runs with session-scoped deduplication; marks run IDs as notified before IPC delivery is confirmed, and fetches all runs without a status filter. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/PreviousRunsList/PreviousRunsList.tsx | Adds two-line failed-row layout with inline copyable error text and tooltip wrapper fix for disabled buttons; cursor-text hint won't render inside disabled buttons due to pointer-events suppression. |
| packages/trpc/src/router/automation/relay-client.ts | Adds humanRelayMessage() that maps 502/503/504 to human-readable strings with a truncated fallback; clean implementation with full test coverage. |
| packages/trpc/src/router/automation/relay-client.test.ts | New test file covering RelayDispatchError construction, all humanRelayMessage translation paths, and truncation behaviour via a mocked fetch boundary. |
| apps/desktop/src/renderer/routes/_authenticated/layout.tsx | Mounts AutomationFailureNotifier inside CollectionsProvider with minimal, correctly-placed changes. |
| packages/trpc/src/router/automation/dispatch.ts | Only change is exporting describeError for potential future consumers; functional behaviour is unchanged. |
| apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/index.ts | Barrel re-export for AutomationFailureNotifier; no issues. |
Sequence Diagram
sequenceDiagram
participant Relay as Relay Proxy
participant RC as relay-client.ts
participant DB as automation_runs (Postgres)
participant ES as Electric SQL (sync)
participant AFN as AutomationFailureNotifier
participant PRL as PreviousRunsList
participant EN as Electron Notifications
Relay->>RC: HTTP 503 (host offline)
RC->>RC: "humanRelayMessage(503) -> Target machine was offline"
RC-->>DB: throw RelayDispatchError
Note over DB: dispatch.ts catches, persists error
DB->>ES: Electric CDC sync
ES->>AFN: useLiveQuery re-emits run
AFN->>AFN: check notifiedRunIdsRef.current
AFN->>AFN: add run.id to ref
AFN->>EN: showNative.mutate(Automation failed, error)
ES->>PRL: run data available
PRL->>PRL: "isFailed=true -> two-line layout"
PRL-->>PRL: render inline error span
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx:11-22
**Query fetches all runs regardless of status**
The `useLiveQuery` pulls every automation run (dispatched, dispatching, failed) into memory simply to filter them in the `useEffect`. As automation history grows this becomes a full-table scan on every Electric re-emit. The query builder supports a `.where()` predicate; filtering to only the two failed statuses here would limit the live result set to exactly the data the notifier needs.
### Issue 2 of 3
apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx:36-43
**Run ID marked notified before the IPC call resolves**
`notifiedRunIdsRef.current.add(run.id)` is called before `showNative.mutate(...)` resolves, and `void` discards the promise. If the Electron IPC call rejects (e.g., the main-process handler is temporarily unavailable), the run is silently marked as notified with no retry path. The user will never see the notification, and the next time the effect runs the ID is already in the set so the call won't be re-attempted. Awaiting the mutate inside a try/catch — or moving the `add()` into a `.then()` — would ensure the ID is only deduplicated on confirmed delivery.
### Issue 3 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/PreviousRunsList/PreviousRunsList.tsx:83-85
**`cursor-text` hint will not render inside a disabled `<button>`**
Disabled `<button>` elements have `pointer-events: none` applied by Chromium's UA stylesheet, so the `cursor-text` class on the inner `<span>` has no visible effect when the run has no `v2WorkspaceId` (the common case for failed runs). `user-select: text` still lets the browser select the underlying text node, so copy actually works — but users get no I-beam visual feedback indicating the text is selectable. Adding `pointer-events: auto` on the error span itself would restore the cursor without re-enabling button clicks.
Reviews (1): Last reviewed commit: "fix(automations): tooltip on hover for f..." | Re-trigger Greptile
| const { data: automationRuns = [] } = useLiveQuery( | ||
| (q) => | ||
| q | ||
| .from({ automationRuns: collections.automationRuns }) | ||
| .select(({ automationRuns: ar }) => ({ | ||
| id: ar.id, | ||
| title: ar.title, | ||
| status: ar.status, | ||
| error: ar.error, | ||
| })), | ||
| [collections], | ||
| ); |
There was a problem hiding this comment.
Query fetches all runs regardless of status
The useLiveQuery pulls every automation run (dispatched, dispatching, failed) into memory simply to filter them in the useEffect. As automation history grows this becomes a full-table scan on every Electric re-emit. The query builder supports a .where() predicate; filtering to only the two failed statuses here would limit the live result set to exactly the data the notifier needs.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx
Line: 11-22
Comment:
**Query fetches all runs regardless of status**
The `useLiveQuery` pulls every automation run (dispatched, dispatching, failed) into memory simply to filter them in the `useEffect`. As automation history grows this becomes a full-table scan on every Electric re-emit. The query builder supports a `.where()` predicate; filtering to only the two failed statuses here would limit the live result set to exactly the data the notifier needs.
How can I resolve this? If you propose a fix, please make it concise.| // Mark as notified | ||
| notifiedRunIdsRef.current.add(run.id); | ||
|
|
||
| // Fire notification | ||
| void electronTrpcClient.notifications.showNative.mutate({ | ||
| title: "Automation failed", | ||
| body: run.error || "Run failed", | ||
| }); |
There was a problem hiding this comment.
Run ID marked notified before the IPC call resolves
notifiedRunIdsRef.current.add(run.id) is called before showNative.mutate(...) resolves, and void discards the promise. If the Electron IPC call rejects (e.g., the main-process handler is temporarily unavailable), the run is silently marked as notified with no retry path. The user will never see the notification, and the next time the effect runs the ID is already in the set so the call won't be re-attempted. Awaiting the mutate inside a try/catch — or moving the add() into a .then() — would ensure the ID is only deduplicated on confirmed delivery.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx
Line: 36-43
Comment:
**Run ID marked notified before the IPC call resolves**
`notifiedRunIdsRef.current.add(run.id)` is called before `showNative.mutate(...)` resolves, and `void` discards the promise. If the Electron IPC call rejects (e.g., the main-process handler is temporarily unavailable), the run is silently marked as notified with no retry path. The user will never see the notification, and the next time the effect runs the ID is already in the set so the call won't be re-attempted. Awaiting the mutate inside a try/catch — or moving the `add()` into a `.then()` — would ensure the ID is only deduplicated on confirmed delivery.
How can I resolve this? If you propose a fix, please make it concise.| <span className="truncate text-xs text-destructive select-text cursor-text pl-4"> | ||
| {run.error || "Run failed"} | ||
| </span> |
There was a problem hiding this comment.
cursor-text hint will not render inside a disabled <button>
Disabled <button> elements have pointer-events: none applied by Chromium's UA stylesheet, so the cursor-text class on the inner <span> has no visible effect when the run has no v2WorkspaceId (the common case for failed runs). user-select: text still lets the browser select the underlying text node, so copy actually works — but users get no I-beam visual feedback indicating the text is selectable. Adding pointer-events: auto on the error span itself would restore the cursor without re-enabling button clicks.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/PreviousRunsList/PreviousRunsList.tsx
Line: 83-85
Comment:
**`cursor-text` hint will not render inside a disabled `<button>`**
Disabled `<button>` elements have `pointer-events: none` applied by Chromium's UA stylesheet, so the `cursor-text` class on the inner `<span>` has no visible effect when the run has no `v2WorkspaceId` (the common case for failed runs). `user-select: text` still lets the browser select the underlying text node, so copy actually works — but users get no I-beam visual feedback indicating the text is selectable. Adding `pointer-events: auto` on the error span itself would restore the cursor without re-enabling button clicks.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
2 issues found across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| import { electronTrpcClient } from "renderer/lib/trpc-client"; | ||
| import { useCollections } from "../CollectionsProvider"; | ||
|
|
||
| export function AutomationFailureNotifier() { |
There was a problem hiding this comment.
I'm actually going to push us to skip this for now - could we drop this? It's a great call I just may want to punt it to avoid creating more exposure to electric sql risks 🤔
| notifiedRunIdsRef.current.add(run.id); | ||
|
|
||
| // Fire notification | ||
| void electronTrpcClient.notifications.showNative.mutate({ |
There was a problem hiding this comment.
Should probably also link to the automation details page on click right?
- Filter useLiveQuery to only failed statuses (dispatch_failed, skipped_offline) instead of fetching all runs and filtering in useEffect - Await IPC notification before marking run as notified; on failure, leave unnotified so it retries on next effect run - Add pointer-events-auto to error span inside disabled button so cursor-text I-beam is visible for copyable error text
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
|
Addressed all review comments in e78049f:
|
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx:38">
P2: Deduplication is non-atomic: run IDs are recorded only after async success, allowing duplicate notifications while the first request is still pending.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| for (const run of automationRuns) { | ||
| if (notifiedRunIdsRef.current.has(run.id)) continue; | ||
|
|
||
| electronTrpcClient.notifications.showNative |
There was a problem hiding this comment.
P2: Deduplication is non-atomic: run IDs are recorded only after async success, allowing duplicate notifications while the first request is still pending.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/providers/AutomationFailureNotifier/AutomationFailureNotifier.tsx, line 38:
<comment>Deduplication is non-atomic: run IDs are recorded only after async success, allowing duplicate notifications while the first request is still pending.</comment>
<file context>
@@ -27,24 +33,21 @@ export function AutomationFailureNotifier() {
- // Fire notification
- void electronTrpcClient.notifications.showNative.mutate({
+ electronTrpcClient.notifications.showNative
+ .mutate({
title: "Automation failed",
</file context>
- AutomationFailureNotifier: import eq/or from @tanstack/db (drizzle-orm exports are not compatible with @tanstack/react-db column refs) - relay-client.test.ts: biome auto-fix (trailing commas, import order) and replace `as any` with `as unknown as typeof fetch`
SUPER-771: Loud failures for automation runs
What this fixes
Failed automation runs (
dispatch_failed/skipped_offline) used to surface only as a tiny red dot in the run history, with a truncated tooltip showing raw transport jargon (dispatch: relay 503: {"error":"Host not connected"}). Users were never proactively notified, and since these failures are intentionally not retried, the run was silently lost.After this change, a failed run shows the error inline in the row (selectable for bug reports), the error string is translated from transport-code into human copy ("Target machine was offline"), and a desktop notification fires once per failed run within the current session.
Origin: #founders Slack thread (2026-05-14, Kiet/Satya) → "popup/notification, not silent retry". Decision was visibility, not retry.
Why this approach
Two prongs needed to address:
select-text cursor-text(perapps/desktop/AGENTS.mdusers must be able to copy errors into bug reports).automationRuns.errorinherits the readable string.For prong (2), translation lives in
relay-client.ts'shumanRelayMessage(status, rawBody)helper rather than the renderer. This keeps the human copy as the persisted single source of truth and avoids duplicating translation logic across surfaces.For the proactive notification (founder ask), an
AutomationFailureNotifierprovider observes the Electric-syncedautomationRunscollection and fireselectronTrpcClient.notifications.showNative.mutate(...)once per failed run.id, with auseRef<Set<string>>deduplicating within the current app session. Cross-window and cross-session dedup were explicitly deferred (notifications.ts:38-41v2NotificationSourceSchema doesn't accept an automation source type — extending it is a follow-up).The full investigator + challenger debate that produced this scope lives at
.spec/improvements/SUPER-771/SCOPE.md(chosen option: moderate; rejected: minimum, Option-4, strategic).Binding scope
PreviousRunsList.tsx(commitsac6c53600)select-text cursor-text pl-4 text-xs text-destructive— same filehumanRelayMessage(502→"Target machine is unreachable", 503→"Target machine was offline", 504→"Target machine timed out", default→"Relay error (status N): …")wired at theRelayDispatchErrorthrow site —relay-client.ts(commit902f5a2cf)describeError(err, context)is identical tomain; the human translation lives upstream inrelay-client.tsso describeError just passeserr.messagethrough (noerr.nameprefix)electronTrpcClient.notifications.showNative.mutate({ title: "Automation failed", body: run.error || "Run failed" })ondispatch_failed/skipped_offline—AutomationFailureNotifier.tsx(commitsac9c8c37b,952514935)useRef<Set<string>>tracks notified IDs;.has()before.add()prevents re-fire on Electric re-emitlayout.tsx— import + JSX mount inside<CollectionsProvider>(commitec8cc7afareverted an unrelatedactiveOrganizationIdrefactor that crept in during cycle 2)Considered alternatives (rejected)
Out of scope (deliberately deferred)
See
.spec/improvements/SUPER-771/follow-ups.mdfor the full list:failureReasonenum onautomationRuns(was the strategic option)apps/api/.../run-failed/route.ts) copy parity with dispatch pathv2NotificationSourceSchema)PreviousRunsListvirtualizationapps/desktoprenderer (no@testing-library/react/happy-dominstalled; renderer-side ACs verified manually for this PR)Verification steps for the reviewer
Visual proof (captured against this branch in the desktop dev build)
Inline error row + human copy translation (AC-1, AC-2, AC-3) — top row is a
dispatch_failedrun rendering the translated copy "Target machine was offline"; lower rows are pre-existingskipped_offlineruns (pre-dispatch path, unchanged by this PR):Native macOS notification fired by
AutomationFailureNotifier(AC-5):Automated (one command, no setup)
bun test packages/trpc/src/router/automation/relay-client.test.ts11 tests pass — covers AC-3 paths explicitly:
humanRelayMessage(503, …)→"Target machine was offline",502→"Target machine is unreachable",504→"Target machine timed out", unknown status →"Relay error (status N): <truncated body>". Exercises the realRelayDispatchErrorthrow path with a mockedfetch(legitimate transport boundary mock — the SUT is the helper + the error class, both real).Manual — Quick verification via direct DB insert (recommended, ~60 seconds)
The dispatch flow has lots of moving parts (host registration, relay reachability, JWT minting). For visual verification of the renderer-side ACs, bypass all of it by inserting a synthetic failed-run row directly. Electric SQL syncs the row to the renderer in <1s, the notifier fires, the row appears with the correct affordances. This is the same approach used to capture the screenshots above.
Setup:
Make sure the desktop app is running (
bun devfrom the worktree) and you're signed in.Open an automation (any one). The right sidebar shows "Previous Runs".
Grab the automation + organization IDs:
Substitute the two UUIDs into the commands below.
macOS only: ensure notification permission is granted. System Settings → Notifications → Electron → Allow Notifications: ON. (Dev build inherits the generic
com.github.Electronbundle ID; production-build app has its own.)Test 1 — AC-1, AC-2, AC-3 (inline error row + human copy):
Expect within ~1s:
Target machine was offlinein red.select-text cursor-textclasses).Test 2 — AC-5 (notification fires):
The same INSERT above also triggers AC-5. Within ~1s of inserting:
Expect: a native macOS notification appears in the top-right with:
Test 3 — AC-6 (within-session dedup):
Insert two rows with the SAME
id(the second insert will be a no-op via ON CONFLICT, OR force the test by inserting a duplicate via a stash; alternatively, navigate away from the automation page and back — the notifier shouldn't re-fire for the already-seen run id):Easiest variant: insert two NEW rows with different IDs and observe two notifications. Then refresh the page or navigate away+back — no third notification fires (proves the ref-set dedup works within session).
Test 4 — AC-7 (negative path):
Insert a successful row:
Expect: new row appears with NO inline error span and NO notification. Status dot color differs from failed rows.
Cleanup:
Manual — Full end-to-end via dispatch flow (optional)
If you want to exercise the actual dispatch code path (rather than DB-inject), the simpler way to induce a relay 503 is to register an online host then take it offline:
bun dev).pkill -f "packages/host-service".relay-client.tscode path. The resultingautomation_runs.errorshould be"dispatch: Target machine was offline"(server-side translation).This path is more involved (host-service lifecycle is fiddly in dev) — the DB-insert path above gives equivalent visual verification with less ceremony.
Regression check
<Tooltip>withmax-w-xs whitespace-pre-wrapstill appears showing the same content.V2NotificationControllerstill mounted alongsideAutomationFailureNotifierin_authenticated/layout.tsx. Verify a workspace-lifecycle notification still fires for unrelated events.Real-services verification (per ~/.claude/CLAUDE.md Supreme Rule)
The visual proof screenshots above were captured against a running desktop dev build talking to a real Postgres + real Electric SQL + real Electron Notification API end-to-end.
relay-client.test.tsmocksfetchonly — that's a transport boundary; the helper (humanRelayMessage) + the error class (RelayDispatchError) + the throw site are all real code.Anticipated FAQ
Q: Why is
dispatch.tsunchanged from main?A: AC-4 says "describeError translates known relay codes without breaking call signature". The translation lives upstream in
relay-client.ts'shumanRelayMessage.describeErrorjust passeserr.messagethrough unchanged. This keeps the call signature stable AND avoids duplicating the translator. The persisted error is e.g."dispatch: Target machine was offline"— thedispatch:prefix from describeError + the human message from relay-client.Q: Why don't
AutomationFailureNotifier.tsxandPreviousRunsList.tsxhave test files?A: The desktop package has no DOM test infrastructure (
@testing-library/react,happy-domnot installed). Three rounds of agent attempts to write component tests all rationalized — either re-implementing production logic in the test file or asserting on mock data only. Installing DOM test infra is out of scope for a bug fix; it's captured in.spec/improvements/SUPER-771/follow-ups.mdas a separate task. Renderer-side ACs (AC-1, AC-2, AC-5, AC-6, AC-7) require manual visual verification per the steps above.Q: Why was the sibling
super-771-loud-failuresbranch abandoned?A: Both the investigator (electron-reviewer) and the challenger (code-reviewer) recommended abandon: the sibling's AUTO-LOUD-003 commit added a tRPC procedure that this binding scope does NOT sanction. The sibling's branch and 4 commits remain in git history for reflog recovery if ever needed; nothing was merged from it.
Q: Won't the notification re-fire annoyingly on every app launch for a long-failed run?
A: For v1, yes — and that's the founder intent ("proactively tell the user"). Cross-session dedup (a persisted seen-set) is captured in follow-ups. If this becomes annoying in practice, we'll add the persisted seen-set in a future ticket.
Q: What happens for relay error codes other than 502/503/504?
A:
humanRelayMessagedefault branch returns"Relay error (status N): <truncated body>". Unknown codes still surface readable text, just less specific. Adding 401/408/etc. translations is a one-line per code inhumanRelayMessage— defer until we see them in practice.Risks
humanRelayMessagecovers 502/503/504. Other status codes use a generic fallback. We can add codes as we encounter them.automationRuns.errornow persists the translated human string. If wording changes, historical rows show old copy. Acceptable for v1; thefailureReasonenum follow-up will let display copy be regenerated.Links
.spec/improvements/SUPER-771/SCOPE.md.spec/tasks/SUPER-771/SUPER-771-loud-failures.md.spec/tasks/SUPER-771/design/PreviousRunsList-failed-row.md,.spec/tasks/SUPER-771/design/AutomationFailureNotifier.md.spec/improvements/SUPER-771/follow-ups.mdNotes for the human signing off
This PR went through three implementation cycles before landing:
relay-client.test.ts) were real and the renderer tests were sham, the sham tests were deleted; onlyrelay-client.test.tsremains as automated coverage.The PRODUCTION CODE is correct and matches the binding scope + design specs. The TEST GAP is real and documented above. The decision to ship with manual verification (rather than fight DOM test infra in this PR) was made explicitly knowing the trade-off.