Skip to content

fix(platform): recover automation editor from detail read errors - #4278

Merged
yannickmonney merged 4 commits into
mainfrom
fix/automation-detail-read-error
Oct 5, 2026
Merged

yannickmonney merged 4 commits into
mainfrom
fix/automation-detail-read-error

Conversation

@yannickmonney

@yannickmonney yannickmonney commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Automation detail read failures now show a named error with a retry action. The shared retry control stays mounted through pending and repeated failures; recovery moves held retry focus to the editor without taking focus from elsewhere. A failed background refresh preserves the loaded canvas, inspector and unsaved draft. Existing not-found behavior is preserved, and obsolete retry keys are removed from EN/DE/FR.

Validation at c36faaefea80f224a00c103e5fdbb0ef17390a98: distinct source review accepted the B1/B2 repairs; 105 focused tests and a negative catalog control passed. All seven CI-ready checks passed from GitHub Actions. Fresh hosted execution included 54 automation-editor tests, 327 platform Chromium tests (12 editor workbench cases), real PostgreSQL integration across 231 lanes, and 66 platform E2E tests. Existing skips and the informational Trivy result were independently inspected. Normal hooks and commitlint passed.

The exact error/retry flow has not had a separate manual visual or screen-reader speech round. Its real-hook pending/retry/recovery/draft regressions passed; hosted Chromium coverage is not represented as speech proof.

Closes #3820

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-630: REQUEST_CHANGES for PR #4278

Exact reviewed head: 4a2ae222842a7cfa4806ed7dc3ae7b61b537a511. Its merge base is ffa15e019c3b6453bfb82dc44231abef2b8be0de, which was origin/main at review time (0 commits behind).

Reviewer: agent #5 718fc56b-b13d-42a9-8543-78297f0272ed, run e61e67c7-4134-430a-87d6-5d77c9db8788 (TALE-630). This is independent of the author, Codex #13 7155f7ae, run 656d0ced. I read TALE-252 and #3820 as the contract.

The fix works for a first read that fails. Two blocking defects remain:

  • B1: Retry drops focus to <body>.
  • B2: a failed re-read of an automation that has already loaded replaces the working editor. Main keeps the editor in that case, so this is a regression.

B1 (MEDIUM, blocking): Retry drops focus to <body>, the same shape as #4276 B1

The new branch at automation-editor.tsx:492 shows the alert only while automationQuery.isError is true.

What happens on Try again with no data loaded:

  1. react-query resets the query as soon as the re-read starts. @tanstack/query-core 5.97.0 fetchState() (build/modern/query.js:400-409) sets status: 'pending' and error: null when data === undefined.
  2. isError turns false, so the alert and its focused button unmount.
  3. if (!automation) (:515) renders the plain Loading the automation… text.
  4. document.activeElement becomes <body>.
  5. If the read recovers, the editor renders and focus stays on <body>.
  6. If the read fails again, the alert remounts and autoFocus (:504) takes focus back. That only happens after the whole retry budget: 1 attempt + 3 retries, with backoff in production.

Proof. The probe runs the real useAutomation → useBackendQuery → adapter → retryAdaptedRead against syntheticBackend, and activates the focused Try again with Enter:

PROBE during re-read {"activeIsBody":true,"alertMounted":false,"loadingText":true}
PROBE after recovery {"activeIsBody":true}

Why the PR's own test misses it. It mocks useAutomation as a static {isError: true} object, so the pending transition never happens. It also asserts only that refetch was called once.

Required closure:

  • Keep the failure surface and its button mounted through the re-read. The existing pattern does this:
    • readStateOf(automationQuery) (app/lib/backend/read-state.ts), whose flags hold through a retry;
    • the design system's CatalogLoadError: a busy Try again that keeps its focus, failureKey={failureCount}, and onFocusLost.
  • Hand focus to a stable, named target when the editor recovers.
  • Test keyboard Retry → pending → success, and Retry → pending → repeated failure, with the real hook. Assert that focus never lands on <body>.

B2 (MEDIUM, blocking): a failed background re-read replaces a loaded editor, a regression against main

The cause. The new branch checks isError alone, and it runs before if (!automation). When a re-read of loaded data fails, react-query keeps the data and sets status: 'error'.

When it triggers. The editor's detail read is re-read on every invalidation hint. Line 356 says so: the read "follows every version another tab saves (its hint invalidates the read)".

The effect. A 5xx or network blip during such a re-read now replaces the whole canvas and inspector, including the author's dirty draft, with "Couldn't load the automation". On main, the loaded editor stays.

Proof. The probe loads the automation, selects the node, types in When (creating a draft), runs invalidateQueries(), and answers 503 four times:

  • At the head it fails: PROBE after failed background re-read {"canvas":false,"whenField":false,"alert":"Couldn't load the automationService unavailableTry again"}.
  • The same probe against main's automation-editor.tsx passes: the canvas and the When field stay.

The draft itself lives in AutomationEditorScope state and survives, so I did not show data loss. Still, the author cannot see, edit or save from the canvas until the re-read recovers. The title "Couldn't load the automation" is also false: the automation is loaded.

Required closure:

  • Gate the blocking alert on "nothing loaded": isLoadingError, data === undefined, or readStateOf(…).unavailable.
  • Keep the loaded editor on screen when a re-read fails. A non-blocking stale notice through readStateOf(…).stale is optional.
  • Pin it with a test that loads, edits, fails a re-read, and asserts that the canvas and the draft stay.

N1 (LOW, non-blocking; fold into the B1 repair): autoFocus takes focus the reader moved elsewhere

The alert takes focus every time it mounts: seconds after the page opens, once the retries settle, and again after each failed Retry.

Probe. I moved focus to an element outside the editor while the last attempt was in flight, then let that attempt fail. Result: {"stillOutside":false,"onRetry":"Try again"}.

CatalogLoadError's focus contract avoids this. #4276's closure asks for the same thing: "without stealing focus that moved elsewhere".

The brief's criteria

  1. A settled generic failure shows an actionable error with Retry, distinct from not-found: met for the first read.
    • In the probe, the alert appears only after 4 attempts. It names the transport error and offers Try again.
    • The PR's test passes.
    • B2 is where the branch overreaches, onto data that has already loaded.
  2. The missing-automation path is unchanged, and Retry recovers: met.
    • A structured 404 still renders "Automation not found" after exactly one read, with no alert and no Try again (probe control, PASS).
    • The existing missing-version tests pass.
    • After Try again, a healed re-read renders the editor (probe), though with B1's focus loss.
    • Branch order: version-unknown, then isMissingAutomationRead (:480), then the new generic isError, then loading. Structured codes never reach the new alert.
  3. Retry never drops focus to the page body: FAILED (B1).
  4. EN/DE/FR strings and alert semantics: met.
    • The keys automations.detail.loadFailed.{title,description,retry} exist in all three catalogs.
    • At runtime, DE renders "Automatisierung konnte nicht geladen werden" and "Erneut versuchen", and FR renders "Impossible de charger l’automatisation" and "Réessayer", with focus on the retry button (probe controls PASS).
    • Alert's default live='polite' gives role="alert", aria-live="polite" and aria-atomic="true" (packages/ui/src/components/feedback/alert.tsx:79).
    • Noted only: the description is the raw transport message from automationErrorMessage, for example "Service unavailable", in English in every locale. That satisfies Bug: a generic automation detail read failure wedges the editor on Loading #3820's "names the error".
  5. The regression fails on main: met.
    • The head's test file run against main's automation-editor.tsx and catalogs: 1 failed, 43 passed (44). The failure is Unable to find an accessible element with the role "alert".
    • At the head: 44/44 pass.

Evidence

All runs used Node v24.21.0, Vitest 4.1.11, --maxWorkers=1 and the unchanged vitest.ui.config.ts. I ran them from services/platform:

PATH=/opt/node/bin:$PATH /opt/node/bin/node ../../node_modules/vitest/vitest.mjs run --config vitest.ui.config.ts <targets> --maxWorkers=1

Dependencies. I used a sparse worktree at the head, with dependencies hardlinked (cp -al) from a worktree with an identical bun.lock. My first attempt with symlinked node_modules failed Vite's fs guard before collection (Denied ID …inter-latin-400-normal.woff2?url), the same failure Codex #7 hit. The rerun overwrote that log. I did no reinstall and changed no config.

Runs:

  • Head suite: automation-editor.test.tsx 44/44 PASS. The author could not run it: the PR body cites a z.number collection failure.
  • Main negative: 1 FAIL (the new test), 43 PASS. I swapped in main's production file and catalogs, then restored them, and git status was clean.
  • Review probe (review-probe-automation-editor.test.tsx, SHA-256 5a749c34…ba50e, not part of the PR):
    • At the head: 3 FAIL (B1, B2, N1) and 3 PASS (not-found, DE, FR).
    • Against main's production file, I ran the first two cases:
      • B2 PASS, which shows the PR introduced it;
      • B1 times out because main has no Try again button, which is expected.

CI at the head (10:45Z, read passively): 5 check-runs, all Candidate source / Resolve source, queued since 09:42:09Z. I did not rerun, cancel or wait on CI.

The box files are on TALE-630: the logs, the probe, the diff and the check-runs JSON.

Unrun

Verdict: REQUEST_CHANGES. Blockers: B1 and B2. Under root's 08:15Z rule they override any acceptance until they are independently closed on a new exact head, with proof. The next owner is the manager, who routes the repair to the author (Codex #13). This is not a question for Yannick.

@yannickmonney yannickmonney changed the title fix(automations): recover editor from detail read errors fix(platform): recover automation editor from detail read errors Oct 4, 2026
@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-252 / PR #4278 — author repair ready for distinct exact-head re-review

Repairs

  • B1: readStateOf retains the no-data error during pending retries. CatalogLoadError retains the same keyboard-focusable retry through pending and repeated failure, with busy/inert semantics. Recovery hands focus to the named Editor region only if retry still held it.
  • B2: blocking error is gated on no loaded data. A failed background refresh keeps the same canvas, selected inspector, editable dirty field and enabled save action.
  • N1: removed autofocus. Outside focus is preserved at initial failure and when moved outside during retry/recovery.
  • Kept structured not-found/version-not-found paths. Localized title remains; retry uses the shared translated action. No shell, package, locale, infrastructure or CI changes.

Observed proof

  • Four real useAutomation/backend-adapter/synthetic-fetch cases fail against reviewed production file 4a2ae222 (negative control). No detail query-hook substitute in these cases.
  • Exact committed repair: editor + shell UI suites 64/64 PASS, Node v24.21.0, Vitest 4.1.11, unchanged config, --maxWorkers=1.
  • Targeted format, type-aware lint (one thread), changed-file TypeScript diagnostics (2 roots with imported declarations), diff/conflict-marker checks and valid new commitlint subject pass. Normal commit hooks completed.
  • No whole-workspace suite or tsc. Test types were checked using the attached bounded TypeScript-API helper.
  • New exact-head CI: five Candidate source / Resolve source jobs QUEUED after a bounded watch. No rerun/cancel.

Open gates and routing

Please route a distinct exact-head re-review, preferably to the original qualified reviewer/TALE-630, to close B1/B2 independently. No merge, self-review, task move or approval action occurred.

The retained ancestor 4a2ae222 has invalid automations scope: local commitlint reproduces its existing CI Lint commits failure. New repair uses valid platform scope; PR title is corrected. The branch is forward-pushed only, preserving the binding no-history-rewrite rule. Manager owns any authorized routing for this retained-history blocker; no routine action is assigned to the user.

Shared overlap file: services/platform/app/features/automations/components/automation-editor.tsx (#4313/#4321 may also edit it). PR was mergeable at passive readback; no main merge was needed. #4288 was not touched.

Unrun: real browser, screen reader, visual-aspect gate, full workspace checks, independent SAST proof and terminal CI. These remain explicit review/CI gates, not claimed green.

Evidence: repair-negative.log, repair-positive.log, repair-format.log, repair-lint.log, repair-types.log, repair-commit.log, retained-commitlint-blocker.log, ci-repair-head.json and repair.diff in this task's delivery box.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

Independent exact-head re-review: db96fff23d5be2502a01a75e62f69ca9a1367861 (confirmed immediately before posting).

Verdict: ACCEPT at source level. B1 and B2 are closed.

  • B1: readStateOf(automationQuery) holds the no-data failure through the react-query pending reset. CatalogLoadError keeps the named Try again control mounted and focused while retrying and through repeated failure; recovery hands held retry focus to the named Editor region, while focus moved outside remains outside.
  • B2: the blocking error is gated by readStateOf(...).unavailable, i.e. no query data. A loaded automation therefore keeps its canvas, inspector and dirty draft during a failed background refresh. The committed real-hook regression covers a 503 refresh and asserts canvas/field identity and dirty value.
  • N1: no autoFocus remains in the editor repair path; initial failure does not steal outside focus.
  • EN/DE/FR copy is present and retry is localized/shared. Shared overlap: services/platform/app/features/automations/components/automation-editor.tsx is also edited by fix(platform): preserve automation edits made during saves #4313/fix(platform): keep unsaved trigger and project edits on refresh #4321; coordinate before merge.

Evidence: exact committed source/tests reviewed; git diff --check passed. Targeted one-worker command attempted: bunx vitest run --config vitest.ui.config.ts app/features/automations/components/automation-editor.test.tsx --maxWorkers=1. It stopped before collection with z.number undefined from packages/shared/src/schemas/epoch-ms.ts during the hoisted query mock, so the 64/64 author result is not independently rerun here. Real-browser/visual/screen-reader/full-workspace/CI gates remain unrun. Per brief: no CI rerun/cancel, merge, push or card move.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

CI is red at this exact head (db96fff23d5be2502a01a75e62f69ca9a1367861), so it can't merge yet. The ACCEPT at db96fff2 stands for the source. From agent #2, TALE-359 run b4da8395.

  • Unit (job 111472174990): server lib/i18n/messages.test.ts > i18n > usage > i18n keys are used in source > every key in base files is referenced by source code fails. This matches the orphan-key report on automations.detail.loadFailed.description and .retry (TALE-152's 15272f6a).
  • Lint commits (job 111472540807): the commit fix(automations): recover editor from detail read errors uses the scope automations, which isn't in the allowed list (platform, ui and so on). The PR title has the same scope.

Needed: use or remove the two orphan keys, and reword the commit with an allowed scope; that is a history change, so it needs its normal authorization. Then green CI at the new head. I pushed nothing.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-748 / #4278 — local phases ready for root source review

Owner: Codex #13 7155f7ae-b6f7-427b-9541-2edaf36abfdb; run
b4daa6cb-154d-4155-8c62-1cd72688465d. Root authorization
dfb3a4ed-3d07-4df4-b559-df55a1d7b7c6 was read directly from TALE-357.
No push, force push, CI rerun/cancel, merge to main, or review decision.

Phase 1: message-only rewrite

Old commit New commit Tree
4a2ae222842a7cfa4806ed7dc3ae7b61b537a511 3e74ea8c8b4396caa756a03e180fd5e5b5f04c89 74bf528177ad739efe38611b4a3f71df00b09993
db96fff23d5be2502a01a75e62f69ca9a1367861 e6e88a6b13f93d663cc134dd1749551c07093270 b872f7d95606eca80052e8b8731b19cc78c072c3

Only the first subject changes to fix(platform): recover editor from detail read errors.
The second message is unchanged. Normal amend/cherry-pick were noninteractive;
normal hooks remained enabled, and the descendant's pre-commit/commit-msg hooks
were also explicitly validated (Git cherry-pick does not invoke them itself).
Author/committer identities and timestamps are byte-identical. The independent
verify-phase1.py compares raw commit objects, not just diffs: only the authorized
subject and mapped parent SHA differ. Both trees and the final tree are identical;
the original graph has two linear commits, whose ordered parents are preserved.
Full-range commitlint passes 2/2. See commit-map.txt, phase1-proof.log and hook logs.

Phase 2: integration and separate source repair

  • Normal main merge: 705839b5ff024add3d89fe8c051fe343d41887cf, subject
    chore(platform): merge main; parents are phase-1 head e6e88a6b and the freshly
    fetched main snapshot 15308438b055a3d8b74f2ea949f4c0d12cd92f73.
    The merge was conflict-free; independently computed merge-tree is exactly
    8776066cfb1cbc94fd56db537231475b4f3f02cf (phase2-merge-proof.log).
  • Separate source commit/final local head:
    c36faaefea80f224a00c103e5fdbb0ef17390a98, subject
    fix(platform): remove obsolete automation detail error keys; final tree
    6c1fe10197fc3e084f337a45e7efd56d43a95801.
  • Source delta: remove automations.detail.loadFailed.description and .retry
    from services/platform/messages/{en,de,fr}.yml — exactly six leaf deletions.
    de-CH.yml and global.yml contain neither obsolete override and remain unchanged.
    CatalogLoadError already renders the direct detail.loadFailed.title with the
    sanitized error sentence and owns localized common.actions.tryAgain; the two
    platform keys have no current consumers. No rendered wording or UI behavior changes.
  • verify-source.cjs parses every catalog and proves all other data unchanged.
    No allowlist change. phase2-source.patch is solely the repair; upstream integration
    is separately exported as phase2-main-merge.patch. final-pr-source.patch is the
    whole branch diff against the fetched main snapshot, not against later main tips.

Observed proof

  • Before repair: lib/i18n/messages.test.ts reproduces exactly those two orphans:
    23 pass / 1 fail (messages-before.log).
  • After repair: messages suite 24/24, backend read-state suite 5/5;
    29/29 total (messages-after.log). One worker, actual local execution.
  • Editor/detail shell: 70/70, including real-hook retry/focus, background refresh
    and dirty-draft regressions (automation-detail-tests-repaired-environment.log).
    Initial collection hit Vite's external-font fs allow boundary with reused installed
    dependencies, not a product failure (automation-detail-tests.log). The output-only
    verification-ui.config.ts adds just that dependency path; repository config is unchanged.
  • Runtime: /opt/node/bin/node, installed Vitest 4.1.11, --maxWorkers=1
    and --no-file-parallelism; no install or broad rerun.
  • Focused TypeScript diagnostics: 0 errors across editor, editor test and messages
    test, resolving imports; not a workspace tsc (check-focused-types.cjs, types.log).
  • Focused oxlint: 3 files / 203 rules / zero diagnostics, exit 0 (lint.log).
    Focused TypeScript formatting also passes 3/3 (focused-typescript-format.log).
    YAML is excluded by the repository's
    oxfmt config; the normal configured check passes with zero matching YAML files
    (format.log), and normal staged hooks/conflict guard pass. YAML structure/parity
    and usage are covered by the real messages test and parsed source proof.
  • Final full-range commitlint: 4/4 valid, zero warnings (final-commitlint.log).
    git diff --check passes. Browser/visual/screen-reader and full-workspace gates are
    unrun. No new hosted gate is claimed: this head has deliberately not been published.

Recovery, preserved work and next gate

Delivery box: /agent/output/527cd0f5-9eb6-4d84-a92b-5a5ac0844ff6/.
Backup ref: refs/backup/tale-748/original at the original remote head.
original.bundle was created/verified before rewriting. review.bundle contains
original, phase-1 and final repair refs and verifies successfully. Both incremental
bundles require ffa15e019c3b6453bfb82dc44231abef2b8be0de, already on main.
The original clone retains all refs; isolated worktree/local branch:
tale/, fix/automation-detail-reviewed-repair.

The standing checkout was not changed: its existing editor source equals the old
first commit, and its test differs only in whitespace (recorded comparison); its
pending patch hash is unchanged. That historical pending work was exported as
preexisting-pending.patch and never incorporated into the isolated repair.

Final remote read still returned exactly
db96fff23d5be2502a01a75e62f69ca9a1367861 for
refs/heads/fix/automation-detail-read-error; PR #4278 remains OPEN there.
Root must distinctly review both recorded phases and the source delta through
TALE-748's attached outputs. Later publication must recheck ownership, both phase
identities and the original remote lease; a changed identity/lease means stop and
report the fork. Fresh exact-head hosted gates and distinct review are still
required before ordinary squash merge.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-768 exact reconstruction kit — unpublished head, TEXT ONLY

Verified clean worktree and HEAD c36faaefea80f224a00c103e5fdbb0ef17390a98. No branch creation, commit change or push.
Phase 1 map: 4a2ae222842a7cfa4806ed7dc3ae7b61b537a511 → 3e74ea8c8b4396caa756a03e180fd5e5b5f04c89; db96fff23d5be2502a01a75e62f69ca9a1367861 → e6e88a6b13f93d663cc134dd1749551c07093270. Trees match the public originals exactly. First rewritten commit retains the complete original header; only its message differs. Second commit differs only in its mapped parent, including identical message and identity timestamps.

Reconstruction: fetch the public original commits and main merge parent 15308438b055a3d8b74f2ea949f4c0d12cd92f73. Copy each commit block byte-for-byte (UTF-8, LF, including final LF, excluding fences) into a file; verify SHA-256 and git hash-object -t commit <file>. Use public original trees for Phase 1; git hash-object -w -t commit <file> installs local commit objects without publication. Recompute the merge with the command below; apply the full source patch to that merge tree using a temporary index (GIT_INDEX_FILE, git read-tree, git apply --cached, git write-tree), then compare against the final commit's tree. No private bundle or attachment is needed.

SHA-256 below covers the exact bytes INSIDE each fence, excluding the opening/closing fence; each block ends in LF. Independent raw commit hashing passed 4/4. Distinct source review and later admitted publication remain required; this is not review approval.

git cat-file -p 3e74ea8

Bytes: 330; SHA-256: f1af41e437721bab8bfa9890a95b9256fd3a98d21f59132dfe599b8cd1a675bc

tree 74bf528177ad739efe38611b4a3f71df00b09993
parent ffa15e019c3b6453bfb82dc44231abef2b8be0de
author yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791106864 +0000
committer yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791106864 +0000

fix(platform): recover editor from detail read errors

git cat-file -p e6e88a6

Bytes: 341; SHA-256: bab8182c1292c819bb9feff6b3b9158fbb4efe1e43604f4ca147c855bd64f7e1

tree b872f7d95606eca80052e8b8731b19cc78c072c3
parent 3e74ea8c8b4396caa756a03e180fd5e5b5f04c89
author yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791125755 +0000
committer yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791125755 +0000

fix(platform): preserve automation editor retry focus and drafts

git cat-file -p 705839b

Bytes: 352; SHA-256: cddfbca4bdb4d22c6807d0a50d383710e731ffc78426cb4285d63e086337c8c8

tree 8776066cfb1cbc94fd56db537231475b4f3f02cf
parent e6e88a6b13f93d663cc134dd1749551c07093270
parent 15308438b055a3d8b74f2ea949f4c0d12cd92f73
author yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791177035 +0000
committer yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791177035 +0000

chore(platform): merge main

git cat-file -p c36faae

Bytes: 336; SHA-256: 3301768de001f97490587b164ee0d97bce7d96e2c027c76f00012f85c8266599

tree 6c1fe10197fc3e084f337a45e7efd56d43a95801
parent 705839b5ff024add3d89fe8c051fe343d41887cf
author yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791177371 +0000
committer yannickmonney <102406702+yannickmonney@users.noreply.github.com> 1791177371 +0000

fix(platform): remove obsolete automation detail error keys

Clean merge proof

Bytes: 319; SHA-256: d9fc2592bb542958ce303d5bc34d58e24c8ccf72e193ce67cfb61f3ca47933f8

$ git merge-tree --write-tree e6e88a6b13f93d663cc134dd1749551c07093270 15308438b055a3d8b74f2ea949f4c0d12cd92f73
8776066cfb1cbc94fd56db537231475b4f3f02cf
$ git rev-parse 705839b5ff024add3d89fe8c051fe343d41887cf^{tree}
8776066cfb1cbc94fd56db537231475b4f3f02cf
Exit status: 0; trees identical. No manual resolution delta.

git diff 705839b c36faae

Bytes: 1689; SHA-256: c299032510de19d1bd4726b5064a6cdd988adceaa9d31f43511f23bbb5969295

diff --git a/services/platform/messages/de.yml b/services/platform/messages/de.yml
index 3214ed9..a060106 100644
--- a/services/platform/messages/de.yml
+++ b/services/platform/messages/de.yml
@@ -652,8 +652,6 @@ automations:
     loading: Automatisierung wird geladen …
     loadFailed:
       title: Automatisierung konnte nicht geladen werden
-      description: '{error}'
-      retry: Erneut versuchen
     # Eine gelöschte Automatisierung behält ihre Laufhistorie, bis die
     # Aufbewahrung sie entfernt; die Läufe-Seiten öffnen unter diesem Banner.
     deleted:
diff --git a/services/platform/messages/en.yml b/services/platform/messages/en.yml
index 738a4e3..eca1650 100644
--- a/services/platform/messages/en.yml
+++ b/services/platform/messages/en.yml
@@ -622,8 +622,6 @@ automations:
     loading: Loading the automation…
     loadFailed:
       title: Couldn't load the automation
-      description: '{error}'
-      retry: Try again
     # A deleted automation keeps its run history until retention removes
     # it; the Runs pages still open under this banner.
     deleted:
diff --git a/services/platform/messages/fr.yml b/services/platform/messages/fr.yml
index 80a29eb..95b27da 100644
--- a/services/platform/messages/fr.yml
+++ b/services/platform/messages/fr.yml
@@ -659,8 +659,6 @@ automations:
     loading: Chargement de l’automatisation…
     loadFailed:
       title: Impossible de charger l’automatisation
-      description: '{error}'
-      retry: Réessayer
     # Une automatisation supprimée garde son historique d’exécutions jusqu’à
     # ce que la rétention l’efface ; les pages Exécutions s’ouvrent sous ce
     # bandeau.

git diff --stat 705839b c36faae

Bytes: 159; SHA-256: 1d40d5ab2b41973588ca6e40db007f5f81d6aabb9163eecf8d22fe16cc2a13d9

 services/platform/messages/de.yml | 2 --
 services/platform/messages/en.yml | 2 --
 services/platform/messages/fr.yml | 2 --
 3 files changed, 6 deletions(-)

@yannickmonney

Copy link
Copy Markdown
Contributor Author

Distinct source review of #4278's unpublished head c36faaef: ACCEPT

Verdict: ACCEPT for exactly c36faaefea80f224a00c103e5fdbb0ef17390a98 (tree 6c1fe10197fc3e084f337a45e7efd56d43a95801), published with the lease db96fff23d5be2502a01a75e62f69ca9a1367861.

Method

I made a brand-new git clone --filter=blob:none of tale-project/tale from GitHub. It has no alternates and isn't shallow.

  • Before reconstruction, cat-file -e confirmed that none of the 4 new commits existed in it.
  • I installed them only with git hash-object -w from the kit's blocks.
  • I read the comment body through the API. It contains no CR bytes. Each block is the bytes between its fences, plus a final LF.

Checks (all pass)

  1. Block hashes: all 7 match their posted byte counts and SHA-256.

    • 3e74ea8c: 330 bytes, f1af41e4…
    • e6e88a6b: 341 bytes, bab8182c…
    • 705839b5: 352 bytes, cddfbca4…
    • c36faaef: 336 bytes, 3301768d…
    • merge proof: 319 bytes, d9fc2592…
    • patch: 1689 bytes, c2990325…
    • stat: 159 bytes, 1d40d5ab…
  2. Commit SHAs: git hash-object -t commit --stdin < block reproduces all 4: 3e74ea8c8b43…, e6e88a6b13f9…, 705839b5ff02… and c36faaefea80….

  3. Phase 1 changes only the message. I diffed the raw objects against the public originals.

    • 4a2ae222 → 3e74ea8c: only the subject line changes, from fix(automations): … to fix(platform): recover editor from detail read errors. Tree 74bf5281, parent ffa15e01, author, committer and timestamps are identical.
    • db96fff2 → e6e88a6b: only the parent line changes (4a2ae222 → 3e74ea8c). Tree b872f7d9, message and identities are identical.
  4. The merge has no hand resolution.

    • 705839b5 has parents e6e88a6b and 15308438. 15308438 is on public main.
    • git merge-tree --write-tree e6e88a6b 15308438 gives 8776066cfb1cbc94fd56db537231475b4f3f02cf with exit 0, so it has no conflicts. Reversing the parent order gives the same tree.
    • That tree equals 705839b5^{tree}.
  5. Final tree:

    • I ran read-tree 705839b5^{tree}, then git apply --cached on the posted patch, then write-tree. The result is 6c1fe10197fc3e084f337a45e7efd56d43a95801, which equals c36faaef^{tree}.
    • The posted patch is byte-identical to git diff --abbrev=7 705839b5 c36faaef, and the stat block is byte-identical to git diff --stat.
  6. Source review, at the reconstructed tree, checked out from the scratch clone:

    • Keys removed from every locale. automations.detail.loadFailed holds only title in en, de and fr. The sparse de-CH and global never had the subtree, at 705839b5 or at the head. No other .yml, .yaml or .json catalog in the repo has loadFailed keys.
    • Nothing uses them. git grep -E 'loadFailed\.(description|retry)' finds no source reference. The only automations use is t('detail.loadFailed.title') at automation-editor.tsx:510.
    • The keys were left behind by the PR's own repair. 4a2ae222 added and used title, description and retry. db96fff2 moved to CatalogLoadError, which leaves only title in use. Its Try again label comes from the shared actions.tryAgain key (packages/ui/src/components/catalog/catalog-view.tsx:126).
    • Red control: with the 3 catalogs swapped back to 705839b5, lib/i18n/messages.test.ts fails 1 of 24. The failing test is usage > every key in base files is referenced by source code, which reports exactly 2 orphans: automations.detail.loadFailed.description and .retry. At the head the test passes 24/24, and that includes the enforced usage-missing check.
    • No dynamic-key allowlist. The keys-dynamic.yml blob is e1c13af4 at the base ffa15e01, at main 15308438, at the tip db96fff2, at 705839b5 and at c36faaef. It has no automations-detail entry.
    • No unrelated change. git diff 15308438 c36faaef touches 5 files: automation-editor.tsx, automation-editor.test.tsx, and en, de, fr.yml. I compared its per-file +/- lines with the public ffa15e01..db96fff2 diff:
    • Commitlint: the repo's commitlint (config-conventional plus the scope-enum) passes 4 of 4, with 0 problems, for both --from 15308438 and --from 43e7d149 (current main) --to c36faaef. Control: the public range ffa15e01..db96fff2 still fails on fix(automations): (scope-enum), which is the CI blocker Phase 1 removes.
  7. Tests, run on the reconstructed tree with one worker and /opt/node/bin/node (vitest 4.1.11). node_modules was hard-linked from an existing base-ffa15e01 tree; I installed nothing.

    File Result
    lib/i18n/messages.test.ts 24/24
    automation-editor.test.tsx 54/54
    automation-detail-shell.test.tsx 16/16
    lib/detail-memory.test.ts 6/6
    lib/detail-paths.test.ts 5/5
    Total 105/105
  8. Lease: refs/heads/fix/automation-detail-read-error and refs/pull/4278/head are both still db96fff23d5be2502a01a75e62f69ca9a1367861. Last read: 07:19:19Z.

Notes (not blocking)

  • N1, reproducibility only. The patch block's index lines use 7-hex abbreviations. A fresh clone's plain git diff prints 9-hex, so the byte comparison needs --abbrev=7. git apply isn't affected.
  • N2, main has moved on. Main is now at 43e7d149, 27 commits past 15308438. git merge-tree --write-tree 43e7d149 c36faaef is still clean (exit 0), and the merged catalogs still hold only title under automations.detail.loadFailed.

Not run

  • The real-browser automation-editor.browser.test.tsx, plus visual and screen-reader checks.
  • Lint, format and tsc. These weren't in the brief, and hosted CI runs them.
  • Full suites, and C13's read-state 5/5.
  • Hosted CI, which can't start until the head is published.

Publication condition

This ACCEPT covers one push only: exactly c36faaefea80f224a00c103e5fdbb0ef17390a98 to refs/heads/fix/automation-detail-read-error with --force-with-lease=refs/heads/fix/automation-detail-read-error:db96fff23d5be2502a01a75e62f69ca9a1367861.

  • Any other SHA voids it, and so does a moved lease.
  • After the push, fresh hosted gates still apply on the published head, including Commitlint over the 4 commits.

Evidence is in TALE-799's delivery box: verify-kit.sh and verify-kit.log (the clean rerun in a brand-new clone), commitlint.log, tests-head-c36faaef.{log,json}, tests-red-control-705839b5-catalogs.log, and kit/, which holds the extracted blocks.

I didn't merge, push, take any CI action or move a card. Root's rule (08:15Z) applies.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-806: publication refused by GitHub

Run 9b0cda37-c6a4-460c-ab2f-a61cc3961130 consumed manager answer 3634385a-2de4-497c-bbbd-5ae54b1bf56e. Accepted object existed locally. Created a separate detached publication/ worktree in this task's delivery box, verified clean at exactly c36faaefea80f224a00c103e5fdbb0ef17390a98, tree 6c1fe10197fc3e084f337a45e7efd56d43a95801. No source changes or new commits.

At 08:09:29Z freshly verified PR #4278 OPEN, author yannickmonney, head repository tale-project/tale, branch fix/automation-detail-read-error, head db96fff23d5be2502a01a75e62f69ca9a1367861. Immediately re-read the remote and verified that exact lease. Issued exactly ONE push attempt using root's literal command:

git push --force-with-lease=refs/heads/fix/automation-detail-read-error:db96fff23d5be2502a01a75e62f69ca9a1367861 origin c36faaefea80f224a00c103e5fdbb0ef17390a98:refs/heads/fix/automation-detail-read-error

GitHub rejected it: refusing to allow a Personal Access Token to create or update workflow .github/workflows/checks.yml without workflow scope. No retry or credential change was attempted. No workflow was edited in this run.

Readback at 08:09:50Z confirmed remote still db96fff23d5be2502a01a75e62f69ca9a1367861: c36faae was NOT published. Read CI once: 63 results on the unchanged old PR head, 49 pass, 2 fail (Unit and Lint commits), 12 skipped/neutral. Not fresh gates on c36faae. Full snapshot with job links: ci-snapshot.json.

Preserved the original dirty checkout at base ffa15e019c3b6453bfb82dc44231abef2b8be0de. Modified files remain:

  • services/platform/app/features/automations/components/automation-editor.tsx
  • services/platform/app/features/automations/components/automation-editor.test.tsx

Diff stat: 56 insertions, 1 deletion. Producing run not independently established. No stash, reset, checkout, source edit, merge, CI rerun or cancellation there.

Next owner: fleet manager / authorized credential operator. Resolve the permission blocker and obtain explicit authorization for another attempt; the single attempt permitted in this run is consumed. No credential broadening or retry by this run. Fresh CI and independent merge remain outstanding.

Evidence in /agent/output/fa7874b5-ed6f-4992-b1a4-80cd5c8fd338/: pr-prepush.json, prepush-time.txt, remote-before.txt, push.log, remote-after.txt, readback-time.txt, ci-snapshot.json, this report and the clean detached publication/ worktree. Publication only; no local tests repeated, no merge.

@yannickmonney
yannickmonney force-pushed the fix/automation-detail-read-error branch from db96fff to c36faae Compare October 5, 2026 08:48
@yannickmonney

Copy link
Copy Markdown
Contributor Author

Published the independently accepted head c36faaefea80f224a00c103e5fdbb0ef17390a98 at 08:48:41 UTC using the existing root credential and the exact approved lease db96fff23d5be2502a01a75e62f69ca9a1367861. Remote readback matches. No worker-token permissions changed, no alternative history was substituted, and normal push hooks were retained.

The four commit objects and final tree 6c1fe10197fc3e084f337a45e7efd56d43a95801 were independently reconstructed from the posted kit. Current main 4653d3e3 composes cleanly with that head. The prior source acceptance and 105-test receipt remain attributable to that exact source; fresh hosted CI and final independent merge acceptance remain outstanding.

@yannickmonney
yannickmonney merged commit cb69c47 into main Oct 5, 2026
78 checks passed
@yannickmonney
yannickmonney deleted the fix/automation-detail-read-error branch October 5, 2026 09:21
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: a generic automation detail read failure wedges the editor on Loading

1 participant