Commit Graph

33607 Commits

Author SHA1 Message Date
ethernet
92b71c702c fix(plugins): compare update versions by ordering
Different text is not necessarily a newer version. Use the same
parsed comparison for approved feeds and PyPI. Parse each pair once.
Keep invalid versions unknown and explain them in the result.

The cadence test shows that an invalid feed cannot request an
automatic update. Git SHA and catalog-pin comparisons are unchanged.

Verification: 55 tests passed across checks, cadence, catalog and
transaction paths. Network replies were injected at existing seams.
No remote plugin was changed. Lint passed.
2026-09-09 22:44:11 -04:00
ethernet
20c3d2180e fix(plugins): request Python consent only for Python deps
A Node-only plugin reached Python consent after its own sidecar
question. Non-interactive installs therefore refused to enable it
for dependencies it did not declare.

Return after the Node step when no Python surface exists. Keep the
Node question and relevant Python refusals unchanged.

Verification: real local-Git install and PM admission, both Python
surface controls, and focused plugin suites: 60 passed, 0 failed.
Node execution is intercepted in the prompt test; no npm service used.
2026-09-09 22:37:22 -04:00
ethernet
534f322697 fix(memory): admit provider dependencies before setup
Legacy dependency declarations and modern provider projects must join
the existing PM union before setup selects the provider. Reuse the
manifest reader and candidate-member selector instead of maintaining
another dependency parser. Propagate refusal through the memory command
with exit 1 rather than saving a provider whose dependencies failed.

Verified through both setup routes with real offline uv resolution.
Conflicts and malformed declarations preserve config, runtime facts
and the selected environment. Successful legacy and modern admission
keeps active sibling and cross-profile dependencies importable.

Targeted gate: 62 passed, 2 host skips. Ruff, whitespace and added-comment
checks passed. No live provider account or full application run.
2026-09-09 22:08:50 -04:00
ethernet
948c9dbfc0 fix(files): scope backslash compensation to local Windows
The shared quoting helper doubled regex backslashes for remote shells
because the controller ran Windows. Apply compensation only when the
backend executes locally; serialized POSIX command text stays literal.
Path translation and file-write payloads remain unchanged.

Verified with the real LocalEnvironment argv path and JSON command text
delivered to a real Bash stdin. Tests compare received bytes for quotes,
metacharacters, newlines, empty values and backslash runs. The serialized
case fails before the fix while the local case already passes.

Integrated: 114 tests passed, 6 host skips. Ruff and whitespace checks pass.
No Docker/SSH service or POSIX host run is claimed. The rejected grep
multiline rewrite is not included.
2026-09-09 21:57:21 -04:00
ethernet
f2db4349e8 fix(pm): inventory features in the staged interpreter
The builder's imported modules could mark an empty dependency tree as
installed. Probe every anchor in one isolated target process. Require
all anchors for a multi-module extra, and process only the selected
tree's .pth files so editable packages retain their launch behavior.

The native builder passes its staged Python explicitly and stops before
manifest publication when the inventory fails. Correct the Hindsight
and Teams anchors using the namespaces in their locked wheels.

Verified: 31 tests passed, 3 host skips. A real target child records its
identity, and a caller mutation back to the builder Python fails the
regression. Both downloaded SDK wheels match uv.lock and pass inventory
and availability checks. Ruff and added-comment checks passed.

No full native package or signing run is claimed.
2026-09-09 21:47:54 -04:00
ethernet
1c4093fdc9 feat(release): stage commit builds with verified receipts
Commit builds use their own immutable namespace and the shared signed
transport. Publish each receipt after its files, and fetch shared files
once only when their receipt records agree.

Summary links retain the full object key. A completed row requires its
own validated receipt and listed object. Missing, corrupt and ambiguous
results remain distinct. Include both universal Windows bundles.

Verified: 85 tests passed across transfer, rendering, candidate and
promotion paths. The subprocess test fetches the rendered download URL
from loopback HTTP. Ruff and added-comment checks passed.

No live R2 writes, workflow dispatch or native package build ran.
The commit-build CLI and workflow changes remain separate drafts.
2026-09-09 21:29:34 -04:00
ethernet
3786bf1478 fix(release): create the revision anchor when absent
The version writer only replaced an existing assignment. Modules
without the field therefore lost the Git-count anchor used for
package distance reporting.

Append the missing assignment and retain updates to an existing one.
A real temporary repository verifies that the emitted count equals
the resulting release commit, with other version files still aligned.
The revision and canary gates passed 20 tests. No Nix build or real
release operation was run.
2026-09-09 21:03:17 -04:00
ethernet
b64fe7b366 fix(lsp): select the analyzed project's Python first
Pyright selected the Hermes interpreter ahead of an explicit or local
project environment. Keep PM as the fallback rather than hiding the
project's dependencies.

The regression drives the real server registry and spawn options with
disposable venvs, then executes each selected interpreter. Correct the
neighboring Windows pip fixture to patch the installer seam it uses.
No language-server subprocess or user project was changed.
2026-09-09 20:48:13 -04:00
ethernet
f0667b7048 fix(pm): find conventional bash without a PATH entry
The fallback required a prior PATH hit, so a normal Git for Windows
installation could not satisfy an empty-PATH terminal. Check the
existing conventional locations independently. Keep the staged tool
and usable PATH precedence unchanged.

The native test calls the actual local resolver and executes the
selected shell with an empty PATH and an unusable WindowsApps alias.
The focused shell gate passed. Other hosts retain their existing path.
2026-09-09 20:43:35 -04:00
ethernet
1445774a3b docs: describe runtime and backup contracts
Electron consumes launch paths from its baked stamp, not a fixed
Python-version directory. Backups preserve data and declarations,
not runtime downloads or browser profiles.

State both contracts in the build and migration guides. Keep the
backup exclusions and quick/full distinction aligned across English
and Chinese, with stable comparison anchors.

The bilingual site build, website typecheck, pinned diagram linter
and added-prose checks passed. All six affected routes and new
backup links were checked in rendered output. Unrelated locale-link
warnings remain. No native package acceptance is implied.
2026-09-09 20:32:13 -04:00
ethernet
910fd60f5b fix(desktop): repair owned dangling CLI links
lstat finds dangling links, so the provisioner skipped the command that
needed repair after a bundle moved. Replace only links that name the
same command in an absolute bundled payload path. Preserve live links,
foreign destinations and regular files.

Stage the replacement link beside the target before renaming it. Keep
the payload command intact and report cleanup errors. Handle each
command separately so one filesystem error does not skip later entries.

Real temporary symlinks reproduce the stale-link failure. The integrated
checks exercise replacement, preservation and an actual failed rename.
Both desktop typechecks and scoped lint pass. No native macOS package
was moved or launched.
2026-09-09 20:25:48 -04:00
ethernet
3991d4e8e6 fix(install): report every stage outcome once
Explicit failures exited before the dispatcher could emit its result.
Unchecked writes could instead reach the success log. Run each stage
in an errexit subshell and emit one EXIT result with the actual status.
Escape JSON text before reporting paths or diagnostic messages.

Accept the desktop's home argument, derive the default install path
from it, and export it to child processes. Explicit install paths win.

Verified actual Bash stage calls over disposable repositories and
failed writes, plus the real CLI's argument and platform boundaries.
No complete POSIX installation or native package acceptance is claimed.
2026-09-09 19:49:45 -04:00
ethernet
4f4f28c552 fix(setup): preserve exact bootstrap pin values
Read quoted lock fields directly instead of stripping delimiters with
an empty-matching expression. Bound target selection to the uv row so
missing targets cannot select unrelated artifacts. Preserve registry
arguments in Git Bash when detecting Windows ARM64.

Real cold-script tests intercept only the download boundary. They
verify the exact native pin and refuse a missing target before download.
A deliberate digest mismatch stops before any downloaded tool executes.
No full dependency installation or user-state change was performed.
2026-09-09 19:39:52 -04:00
ethernet
14669e7c19 fix(desktop): probe selected dependencies and retain repair errors
The import probe skipped PM activation. Foreign Python paths could
also conceal missing dependencies. Run bootstrap before dependency
imports and sanitize the final child environment, including caller
settings. Probe from the same checkout used by the backend.

Derive the bundled flag when serving each bootstrap snapshot instead
of storing it in setup events. Failed or unavailable repair calls keep
their error and leave the recovery controls usable without reloading.

Verified with real bootstrap modules, a disposable dependency selection,
and native Python through the Node probe. Renderer tests cover refused,
thrown and unavailable repair results, plus successful reload.
No installed desktop, signed package or live user state was operated.
2026-09-09 19:23:10 -04:00
ethernet
afa48bd413 fix(pm): hash directory links without following them
Directory symlinks contributed no bytes to realized-tree digests.
Windows junctions instead exposed their external target contents.
Hash both as link-target text and remove them from traversal.

Native directory-link and junction tests failed on the base. They now
verify retarget detection, unchanged digests after external writes,
and cycle termination. The existing PM authority and store suites
also pass. No payload facts or live stores were rewritten.
2026-09-09 18:46:37 -04:00
ethernet
4188301624 fix(build): propagate icon and queued-open failures
Icon generation reported errors but returned success. Aggregate target
write and verification failures into exit 1, while processing later
targets. Keep rendering dependencies in the isolated build group.

A synchronous open error consumed a slot. Returning that slot alone
still let a deferred throw suppress an earlier callback and stall the
queue. Schedule queue draining outside completed callbacks and retain
the original exception.

Verified real clean-source generation and structural checks for all
35 targets, plus a real write failure through the Node/uv runner.
Native Windows child tests cover immediate and deferred open errors.
POSIX descriptor-limit cases are explicit skips here, not passes.
No signed package or native macOS acceptance is claimed.
2026-09-09 18:38:26 -04:00
ethernet
f2a19b0e06 refactor: remove unused extraction copies
Use the existing session-export, transcription and DingTalk owners.
Remove unused setup/watchdog helpers and the no-op package migration
hook. No package overrides it; user-state migrations keep their own
existing owners. Keep version-change installation coverage and the
scheduled external plugin compatibility blocks.

Verified with the real adapter, transcription, session-snapshot and
PM core suites. No live messaging service or user-state operation.
2026-09-09 18:21:39 -04:00
ethernet
fd605dcacf fix(wake): use one engine family and honor selected capture
Move the pyopen adapter to the shared engine module and remove
shadowing definitions. Request audio-io only for local capture.
Pass the caller's resolved capture mode into engine construction,
so auto-selected client audio does not install microphone packages.

Verified through the listener entry and existing engine/detector tests
with disposable state. No live microphone or native SDK acceptance.
2026-09-09 17:59:26 -04:00
ethernet
bb824c381f merge: combine network retries with downloader safety
Keep the retry branch's bounded network policy under installation.
Preserve destination-local atomic publication, partial ownership,
representation checks, aggregate progress and operation-owned pause.
Keep the serial CDN fallback active across subsequent retry attempts.

Retain exact-origin authorization for index reads and safe redirects.
The retry policy does not retry hash, disk or certificate failures.

Verified with canonical native Windows ARM64 tests for both branches,
local-model download consumers, PM core and update resolution.
The commit-build draft remains uncommitted and unchanged.
2026-09-09 17:40:19 -04:00
ethernet
e4cc7f09d9 merge: integrate upstream catalog with PM publication
Keep upstream's reviewed catalog as the only plugin name index.
Catalog pins and custom update sources share staged PM validation.
Publish code and dependencies with recovery after process death.
Reject a concurrent enablement change before publishing disabled code.

Use the manifest loader's supported version in the installer. Keep
probe cooldowns for timeouts, not TLS failures that a CA change fixes.
Preserve the backup, uninstall, browser and memory-provider repairs.

Verified with the canonical runner on native Windows ARM64, real Git
repositories, local TLS endpoints and UV dependency generations.
Desktop catalog tests and both TypeScript checks pass. The full suite
and native release builds were not run. No remote push.
2026-09-09 16:49:27 -04:00
ethernet
9334ae34ec merge: integrate lm-pm downloads with runtime safety repairs
Keep the shared partial lock, representation-bound resume, connection cap,
and atomic destination publication. Add whole-plan progress and caller-owned
pause events without restoring the old mtime-only garbage collector.

Adapt incoming tests to the surviving partial-state owner and accurate HTTP
range responses. Real native CUDA installation paused, resumed, verified the
pinned archives, and executed the installed binary.

The desktop tests, renderer typecheck, lint, PM tests and local-runtime tests
ran against the integrated paths. No package release or channel write ran.
2026-09-09 15:25:56 -04:00
ethernet
bdb52ce711 fix(local-models): use pm pins and resumable component downloads
Use PM for engine binaries, dependent runtime archives, and model files.
Keep one operation-owned pause event through installation and download.
Report whole-plan bytes and retain paused jobs across desktop remounts.

Preserve the completed lm-pm worktree as its own integration parent.
The owning session verified focused Python and desktop tests, actual
Windows ARM64 CUDA downloads, and rendered pause/resume controls.
Combined verification with the safety repairs follows in the merge.
2026-09-09 15:17:09 -04:00
ethernet
1c8fae6180 fix(pm): preserve runtime and user state across failure paths
Keep downloads bound to their remote representation and publish through
atomic destination-local staging. Serialize shared partial ownership.

Keep explicit CA trust scoped to provider probes. Preserve checkpoint
history and edited files, validate all profile inputs before dependency
publication, and separate data removal from installed runtime ownership.

Exclude machine-specific PM state from portable transfers. Keep plugin
files and nested skill tools intact. Preserve native test isolation.

Focused native Windows receipts cover the individual repairs and their
integration. This commit does not claim a full-suite or release build.
2026-09-09 15:17:08 -04:00
Teknium
544169a41a perf(desktop): stop per-event full-timeline allocations on streamed tool events
Every tool.start / tool.progress / tool.complete ran three whole-timeline
passes that each allocated per part: `completeOpenStreamParts` (`map` with a
spread per row), the pending-row scan (`map` → `filter` → `map`, one wrapper
object per part), and `generatedImageEchoSources` (`flatMap` + a throwaway
array per non-image row) — then `dedupeGeneratedImageEchoesInParts` copied
the array again even when it stripped nothing, handing React a fresh
identity per event. Replace them with single in-place loops, and return the
input array from the dedupe when there are no echoes so a no-op stays a
no-op for the store.

Measured on the real `useMessageStream` hook (vitest/jsdom, 2,000 starts +
2,000 completions interleaved with text deltas): 618 ms → 138 ms; the
tool-only 2,000×2 case 252 ms → 67 ms. The remaining O(n) is one
`findIndex` per event over a ~4k array (~8 µs), well under the cost of the
copy React needs anyway, so no per-stream id index is introduced.

Behaviour is unchanged: same first-match `toolCallId` resolution, same
sparse/no-id contextual correlation, same ordering of parts.

Co-authored-by: Xipong <217837358+Xipong@users.noreply.github.com>
2026-09-09 12:13:14 -07:00
Xipong
7b639164cf fix(desktop): parallelize roster source requests 2026-09-09 12:13:14 -07:00
teknium1
a02a3b9808 test(desktop): trim MCP polling tests to two invariants per fix
Health checker: keep the 12-reconnect coalesce test and replace the
stop/disconnect drop rows with the invariant #106135 names — one fresh
follow-up still runs when the active sweep fails through the handled
config-error path (13 config reads on main, 2 after the fix). Log poller:
keep single-flight + resumed cadence, and shrink the replacement-lifecycle
test to "stop suppresses a late publish and leaves no timer". Both files
were run against origin/main source and failed (11 / 13 / 13 requests).
2026-09-09 12:11:25 -07:00
teknium1
8ac2c147a5 refactor(desktop): drop the serial-task layer from the MCP log poller
startCompletionPoll already keeps the pane single-flight and its stop
function already suppresses a late publish, so createSerialTask (a second
queue that never forwarded its AbortSignal to getLogs) and the AbortError
re-throw inside the poll callback were defense-in-depth on top of the fix.
A profile switch now tears the old lifecycle down and starts the new one
immediately; at most one stale read can still be in flight, and its result
is discarded. Replace the AbortController with a plain stopped flag.
2026-09-09 12:11:25 -07:00
Xipong
d93988233c fix: serialize MCP log polling 2026-09-09 12:11:25 -07:00
Xipong
3fbc408ff6 fix: preserve event delivery across scheduler lifecycle boundaries 2026-09-09 12:11:25 -07:00
Xipong
42f2067092 fix(desktop): coalesce MCP reconnect sweeps 2026-09-09 12:11:25 -07:00
Teknium
85e423482a fix(tools): kill the browser_exec CLI tree on timeout on Windows too
The salvaged fix only ran the CLI in its own session on POSIX and kept
plain subprocess.run on Windows — the one platform where the wedge in
#106244 is actually reproducible: CPython's run() retries an unbounded
communicate() after kill() there, so a grandchild holding the capture
pipes blocks the worker forever. On POSIX run() wait()s the PID and
returns promptly; the grandchild merely leaks (live-reproduced on Linux).

One code path for both platforms:
- _group_popen_kwargs: start_new_session=True on POSIX,
  CREATE_NEW_PROCESS_GROUP + hide flags on Windows (replaces the
  hide-only _windows_popen_kwargs).
- _kill_cli_process_group: os.killpg SIGKILL on POSIX, taskkill /T /F
  on Windows (same kwarg set as the sibling taskkill sites).
- The Popen decodes with encoding="utf-8", errors="replace" like every
  other subprocess call in this file (windows footgun rule).
- Drain test patches the kill helper instead of os.killpg so it runs on
  every host.

Windows behaviour is not live-verifiable on this Linux host.
2026-09-09 12:11:13 -07:00
Teknium
6788c3224f test(tools): trim the browser_exec group-kill tests to two invariants
Drop test_gone_group_still_surfaces_timeout: the killpg-race branch is a
contextlib.suppress and the test only pinned the exact communicate()
timeout sequence (a change-detector). The real-grandchild test and the
bounded-drain test remain — the two behaviour contracts of the fix.
2026-09-09 12:11:13 -07:00
liuhao1024
7416201baf fix(tools): exempt POSIX-only killpg from the Windows footgun scan
The group kill lives behind browser_exec's os.name != "nt" branch, so
os.killpg/SIGKILL are never reached on Windows; mark the line per the
scan's suppression convention.
2026-09-09 12:11:13 -07:00
liuhao1024
60debff28d fix(tools): kill the whole browser-use CLI process group on browser_exec timeout
subprocess.run only kills the direct CLI child on TimeoutExpired; a
browser_harness daemon / Chrome helper grandchild inherits the stdout/
stderr pipes and keeps them open, so the internal communicate() blocks
on pipe EOF forever. The wedged tool call never returns, its activity
heartbeat keeps stamping last_activity_at every 30s, and the session is
pinned at "now" in the desktop sidebar indefinitely (#106244).

On POSIX, run the CLI in its own session (start_new_session=True) and
SIGKILL the whole process group on timeout, then drain the pipes under
a bounded deadline. Windows keeps subprocess.run.
2026-09-09 12:11:13 -07:00
Teknium
d31318745c test(desktop): trim salvaged fixtures to the invariant tests
Drop the two companion tests from #106124 and #106099 that already pass
on main without their fixes (a pinned active transcript is never measured
because prune() skips it before weighing; a rerendered effect already
discards the previous closure). Keep the one red-on-main invariant per
fix: no re-serialisation of unchanged warm transcripts, and change ticks
coalescing behind an in-flight session.active_list request.
2026-09-09 12:09:00 -07:00
Xipong
3d4c537584 fix(desktop): coalesce pending live status refreshes 2026-09-09 12:09:00 -07:00
Xipong
3bfd7b653b fix: cache warm transcript weights 2026-09-09 12:09:00 -07:00
Xipong
5a2ccffd41 fix(desktop): skip distro probe for WSL drive paths 2026-09-09 12:09:00 -07:00
Xipong
ef677265d2 fix(desktop): reuse detected SSH platform 2026-09-09 12:09:00 -07:00
Teknium
14a6a75ebe test(agent): trim Codex patch-budget tests to two invariants
Replace the six cap-mapping unit tests (exact 5536 px value, floor, Anthropic
path, two None cases, worst-case tile math) with one behavioral test: the cap
derived from the real Codex rejection makes the production shrink pass rewrite
a real 5444x6200 PNG Responses ``input_image`` part to within the 30000-patch
budget. That is the user-visible contract; the exact pixel value is an
implementation detail. Keeps the classifier test as the second invariant.
2026-09-09 12:06:53 -07:00
liuhao1024
cabe430a25 fix(agent): derive tile-aware shrink cap from Codex patch-budget 400s
The classifier fix routes Codex patch-budget 400s into the shrink
recovery, but _image_error_max_dimension still returned None for the
Codex wording, so the recovery fell back to the 8000 px default cap
and skipped images between ~5542 and 8000 px that already exceed the
30000-tile budget — burning the single shrink retry without shrinking
anything. Parse the reported patch limit and convert it to a per-side
pixel cap of isqrt(limit)*32 (5536 px for 30000 patches), which keeps
a square image under the budget.
2026-09-09 12:06:53 -07:00
liuhao1024
d3515a9d18 fix(agent): classify Codex patch-budget image 400s as image_too_large
OpenAI Codex Responses rejects an image whose tile-patch budget exceeds
its 30000-patch ceiling with wording ("requires N patches after
processing, exceeding the limit") that contains none of the existing
image-size vocabulary, so the 400 fell through to format_error
(non-retryable). The reactive image-shrink recovery in turn_recovery was
therefore bypassed and the session kept failing — or failover re-sent
the identical oversized image to another model.

Route the patch-budget wording to image_too_large (retryable) so the
existing shrink pass re-encodes the image under the ceiling and retries.

Fixes #106337
2026-09-09 12:06:53 -07:00
teknium1
554eb9c217 chore: map contributor email for ten82e (PR #106290 salvage) 2026-09-09 11:51:00 -07:00
teknium1
53b9615ea6 test(hermes_cli): keep one opencode-go delisting invariant; record the delisting beside the keyed-suffix set
Drop the floor-pin test from #106290 (a snapshot of the static table); the
end-to-end merge test through provider_model_ids with the real curated floor
already fails if the slug is re-added. Note beside
_OPENCODE_FREE_KEYED_SUFFIX_MODELS why ox-alpha-free stays excluded from the
keyless catalog even though it left the curated floor.
2026-09-09 11:51:00 -07:00
ten82e
b2cae54035 fix(hermes_cli): drop delisted ox-alpha-free from opencode-go curated floor
The Go relay (GET /zen/go/v1/models) delisted ox-alpha-free 2026-09-09, but
_PROVIDER_MODELS["opencode-go"] still carried it. _profile_live_catalog merges
the curated floor into the live list (live-first for opencode-go), so the
model picker kept offering a model that now 401s — the same failure class as
#95914 (opencode-free / x-preview-f-free).

Remove it from the floor and add two regression tests: an end-to-end merge
test through provider_model_ids with the real floor (fails if a stale floor
resurrects it) and a floor-pin test asserting the known-delisted model stays
out of the offline fallback.

(cherry picked from commit 091fd85865caa09e828928f93ee985dae7e9580d)
2026-09-09 11:51:00 -07:00
teknium1
b27c6f7f0d docs(line): document LINE_EXPIRED_TEXT beside the other postback reply knobs 2026-09-09 11:47:55 -07:00
Teknium
b21231fef8 test(line): trim #106446 regression suite to the two reporter reproductions
Keep exactly the issue's Repro A (heartbeat must not become the cached answer)
and Repro B (stale READY mapping / expired button must not swallow the next
answer); both fail on origin/main and pass with the fix. The other six tests
from #106470 (config override, PENDING-tap guard, per-layer variants) were
either change-detectors or subsumed by these two.
2026-09-09 11:47:55 -07:00
Tranquil-Flow
77c994bab2 fix(line): keep progress heartbeats and stale mappings out of the postback cache (#106446)
(cherry picked from commit 4b0dc3c58cfe9771745384f20d75ca48d9b25408)
2026-09-09 11:47:55 -07:00
teknium1
3dc3809d07 refactor(fal): render the managed billing message once in fal_common
Image and video callers were each formatting the same four-field dict into
the same sentence. Return the rendered tail from _managed_fal_billing_error
so the wording lives in one place; output is byte-identical.
2026-09-09 11:46:36 -07:00
teknium1
acf9177c70 test(fal): trim the billing-409 salvage to two invariant tests
Keep the two tests that fail on main without the fix:
- test_fal_common: a keyed managed submit makes exactly one POST (plus the
  negative arm: an unkeyed submit still goes through the SDK retry ladder)
- test_image_generation: the 409 BILLING_ERROR body surfaces
  `unsupported_pricing_meter` instead of the generic "not yet enabled" text

Dropped from #106484: the duplicate video-plugin billing test (same helper,
same assertion), the `_fal_client = fake` / `import_fal_client` stub churn
and the `tools.lazy_deps` stub — fal-client is installed in CI (`--extra fal`)
so those fixtures were not needed; the `_load_fal_client` no-op fixture on
TestManagedGatewayErrorTranslation for the same reason.

Also drop the redundant `retry_request is None` re-check in
`_ManagedFalSyncClient.submit` — `__init__` already raises when the helper
is missing.
2026-09-09 11:46:36 -07:00