Skip to content

Fix localized Inbox pause recovery / 修复收件箱暂停恢复与本地化 - #8270

Merged
SivanCola merged 10 commits into
esengine:main-v2from
SivanCola:fix/inbox-error-localization
Aug 11, 2026
Merged

Fix localized Inbox pause recovery / 修复收件箱暂停恢复与本地化#8270
SivanCola merged 10 commits into
esengine:main-v2from
SivanCola:fix/inbox-error-localization

Conversation

@SivanCola

@SivanCola SivanCola commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • localize paused and sibling Inbox product-state errors in English, Simplified Chinese, and Traditional Chinese
  • expose an actionable recovery banner for ordinary paused queues, not only restart-recovered queues
  • recover same-process orphaned in-flight guidance into paused, reviewable work instead of leaving immutable zombie items
  • make guidance actions reflect durable state: active items are protected, while uncertain and blocked items have an explicit retry path

Fixes #8276.

User-visible behavior

Chinese desktop sessions now show 收件箱已暂停 instead of the raw backend text inbox is paused. Capacity, invalid-state, read-only, and workspace-startup errors on the same Inbox path are localized as well.

If an accepted/running guidance item loses its live Controller owner, the next snapshot safely pauses the Inbox and changes the item to uncertain. The user can review it, continue, then retry or delete it. Guidance that is still actively owned cannot be sent or deleted again, and the UI explains that it is already being applied.

Root cause

Known Inbox states crossed the Wails boundary as English error.Error() text, and Composer toast paths rendered that text directly. The frontend also propagated pause state only for recovered snapshots, so an ordinary paused queue could still expose the guidance action without a resume surface.

For #8276, the normal rejected-steer path already rolls steer_accepted back to queued; the durable failure window was instead same-process ownership loss. Persisted running / steer_accepted / steer_consumed records were only recovered across process restarts. A live process could therefore retain an in-flight record after its Controller owner disappeared, while the shelf continued presenting queued-item actions. Claim-to-owner and owner-to-ack boundaries also needed serialization against snapshot recovery.

Changes

  • encode known Inbox errors as stable reasonix_error:<code> values while preserving Go error unwrapping
  • translate stable codes at display time and accept legacy English strings for compatibility
  • preserve unknown diagnostic errors verbatim
  • propagate receipt and snapshot pause state into the guidance shelf
  • disable guidance admission while paused and offer review/resume/keep-paused actions
  • atomically recover unowned in-flight records to uncertain, with a fast no-candidate path that keeps ordinary snapshots lightweight
  • serialize durable claim, live ownership, and durable acknowledgement against orphan recovery
  • disable send/delete for live in-flight items and expose localized retry actions for uncertain / blocked items
  • localize the queued-after-turn-ended guidance notice
  • localize cancellation context and nested Inbox errors in the active locale

Verification

  • go test ./...
  • go test -race ./internal/control ./internal/sessioninbox
  • deterministic claim-to-owner and owner-to-ack interleaving tests
  • cd desktop && go test . -run 'Inbox|Steer' -count=1
  • cd desktop/frontend && pnpm test:typecheck && pnpm test
  • Composer Inbox recovery regression test: 20 passed
  • cd desktop/frontend && pnpm build (typecheck, hooks lint, CSS/WAAPI checks, Vite build, bundle budgets)
  • make lint
  • real Chromium DOM/click verification in zh-CN: retry is disabled while paused, enabled after 继续执行, carries the accessible name 重试这条引导, and clicks successfully

Two unrelated desktop full-suite tests each failed once due existing temporary/global-state flakiness (TestApplyLinuxVersionedActivatesWithoutPersistingGuard and TestRemoveWorkspaceClearsActivePointerWhenRemovingCurrentWorkspace); each passed 10 consecutive focused reruns. The desktop Inbox/Steer integration set passed.

Compatibility and risk

  • persisted schema, state enum, Wails method signatures, and JSON fields are unchanged
  • recovery reuses the existing uncertain, paused, retry, and delete contracts
  • new frontend code accepts both stable codes and legacy English errors
  • old frontends can review/resume and directly retry recovered uncertain items; new frontends use the already-existing RetryInboxItem bridge method
  • unknown diagnostics remain unchanged
  • no prompt, provider serialization, tool schema, cache prefix, dependency, permission, or network behavior changes

Documentation-impact: none - Existing Inbox documentation remains correct; this change localizes runtime errors and makes existing recovery semantics reachable for same-process orphaned items.

Problem:
- Paused Inbox actions surfaced raw English backend errors in localized desktop sessions.
- Non-recovered paused queues had no actionable recovery surface and could still attempt guidance admission.

Root cause:
- Wails transported product-state text instead of stable identifiers, and the Composer rendered Error.message directly.
- Pause state was only propagated for recovered Inbox snapshots.

Fix:
- Encode known Inbox states as stable error codes while preserving Go error unwrapping and unknown diagnostics.
- Localize current and legacy error forms in English, Simplified Chinese, and Traditional Chinese.
- Propagate pause state, disable guidance admission, and expose resume controls for ordinary paused queues.

Verification:
- cd desktop && go test ./... -count=1
- Inbox localization tests: 18 passed
- Inbox recovery interaction tests: 13 passed
- cd desktop/frontend && pnpm build
- Verified the zh-CN pause and resume flow in a real browser
@SivanCola
SivanCola requested a review from esengine as a code owner August 11, 2026 01:04
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) and removed v2 Go rewrite (1.x) — main-v2 branch, active development labels Aug 11, 2026
Problem:
- Inbox cancellation failures exposed English context or stable transport codes in localized sessions.
- PR checks were blocked by errorlint and ratcheted source-file budgets.

Root cause:
- The controller cancellation path bypassed the shared Inbox error formatter.
- An intentional error identity assertion lacked a scoped errorlint explanation, while added lines exceeded existing budgets.

Fix:
- Localize cancellation context and nested Inbox errors while preserving unknown diagnostics.
- Add formatter and controller regression coverage.
- Keep source budgets unchanged and document the intentional identity assertion.

Verification:
- go run ./tools/repolint
- cd desktop && go test ./... -count=1
- cd desktop && golangci-lint run --timeout=5m ./...
- cd desktop/frontend && pnpm build
- Inbox localization, cancellation reconciliation, and recovery regression tests
Problem:
Durable in-flight guidance could outlive Controller ownership and become an immutable zombie item while the UI kept offering invalid operations.

Root cause:
Same-process snapshots had no ownership reconciliation, and durable claim/active/ack transitions were not serialized against recovery. The guidance shelf also treated every persisted state like a queued item.

Fix:
Recover unowned in-flight records to paused, reviewable uncertain state; serialize the full ownership lifecycle; and expose state-aware disabled or retry actions with localized labels.

Verification:
- go test ./...
- go test -race ./internal/control ./internal/sessioninbox
- cd desktop && go test . -run Inbox\|Steer
- cd desktop/frontend && pnpm test:typecheck && pnpm test && pnpm build
- make lint
- real Chromium recovery/retry interaction in zh-CN

Fixes esengine#8276
@github-actions github-actions Bot added the agent Core agent loop (internal/agent, internal/control) label Aug 11, 2026
Problem:
A recovered item could start as a new turn on retry and then reject a stale frontend steer. Completion also held the inbox admission lock across transcript snapshot I/O.

Root cause:
The steer idempotency path omitted the running state, and completion removed active ownership before persistence, requiring broad serialization.

Fix:
Treat Controller-owned running items as already admitted. Keep active IDs published while snapshotting and hold admission exclusion only for durable acknowledgement and ownership removal.

Verification:
- go test ./...
- go test -race ./internal/control ./internal/sessioninbox
- cd desktop && go test ./...
- frontend typecheck, focused Inbox tests, and production build
- make lint
Problem:
Inbox snapshots and turn completion held admissionMu while waiting on session inbox disk transactions, allowing bounded file-lock waits and per-item acknowledgement loops to block admission.

Root cause:
Recovery consumed a stale active-ID snapshot, so a broad Controller lock bridged the durable claim-to-ownership and ownership-to-ack windows.

Fix:
Track admitting and active ownership in lock-free registries that Store recovery consults after refreshing its transaction. Keep ownership published through durable acknowledgement, and remove admission locking from snapshot and completion I/O.

Verification:
- go test -race -count=1 ./internal/control ./internal/sessioninbox
- go test ./...
- cd desktop && go test ./...
- go vet ./...
- make lint
- frontend typecheck, Inbox focused tests, and production build
Problem:
Queued guidance could remain idle after rotation, a rejected steer, a transient dispatch failure, or the previous durable turn completing. The desktop fallback also disabled the queued action while idle.

Root cause:
Inbox dispatch used a one-shot edge guard, so kicks that arrived during a scan or a closed admission gate could be lost. Several gate-release paths did not publish a new kick, and the frontend exposed no FIFO-safe recovery action.

Fix:
Use a level-triggered drain with bounded retry, re-kick after rotation and rejected steering, and expose an idle head-item Send action that reuses the idempotent resume/drain path. Keep later items disabled to preserve FIFO ordering and localize the new states.

Verification:
- go test -race ./...
- go test -race ./internal/control -count=1
- focused inbox race tests with -count=10
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- composer-inbox-recovery.test.tsx
Problem:
The Linux and Windows desktop jobs failed the raw initial asset budget after the inbox recovery UI landed, even though every compressed startup and chunk budget remained green.

Root cause:
The new localized recovery controls add 2.7 KiB to the current main-v2 raw initial bundle beyond its 2246 KiB allowance.

Fix:
Raise only the raw aggregate allowance to 2249 KiB and document the durable inbox recovery contract. This leaves roughly 0.3 KiB of headroom while preserving all gzip, CSS, locale, and largest-chunk limits.

Verification:
- wails generate module
- pnpm --dir frontend build
- pnpm --dir frontend check:bundle
- pnpm --dir frontend test
- go test -race ./... (root and desktop modules)
Problem:
Repository lint rejected the queue liveness patch because it expanded two baselined legacy files and pushed TrySteerInboxItem five lines over the function limit.

Root cause:
The drain kick and idle recovery details lived inline in already oversized controller and Composer functions.

Fix:
Move the idle UI kick into composerGuidance, colocate rotation release with inbox dispatch, and extract steer-admission unlock handling. This restores every existing file/function budget without adding lint baselines.

Verification:
- go run ./tools/repolint
- focused inbox race tests with -count=10
- pnpm lint:hooks
- pnpm test:typecheck
- composer-inbox-recovery.test.tsx
Problem: PR esengine#8270 no longer merged after main-v2 added startup config-warning bundle growth.

Root cause: both changes updated the same raw startup bundle ratchet from an older shared baseline.

Fix: merge the current main-v2, preserve both feature ownership notes, and set the tight 2251 KiB limit measured from the 2250.4 KiB merged artifact.

Verification: go test ./...; go test -race ./internal/control ./internal/sessioninbox; cd desktop && go test -race ./...; frontend pnpm test:all; frontend pnpm build; go vet for both modules; repolint.
@SivanCola
SivanCola merged commit cc28d83 into esengine:main-v2 Aug 11, 2026
36 of 37 checks passed
SivanCola added a commit to SivanCola/DeepSeek-Reasonix that referenced this pull request Aug 11, 2026
Problem:
- main-v2 advanced with esengine#8270 and esengine#8340 while the catalog integration was under review.
- Both branches changed the controller frontend and startup bundle budgets.

Root cause:
- The inbox recovery localization and opt-in task-spend defaults overlapped the catalog hydration guards and measured frontend budget ratchets.

Fix:
- Preserve the history hydration identity/error fences alongside locale-aware inbox recovery.
- Keep the latest task-spend defaults and use measured startup bundle budgets without widening the repolint baseline.

Verification:
- env REASONIX_RELEASE_CACHE_GUARD=1 go test -count=1 ./...
- env REASONIX_RELEASE_CACHE_GUARD=1 go test -race -count=1 ./...
- cd desktop && go test -count=1 ./...
- cd desktop && go test -race -count=1 ./...
- cd desktop/frontend && pnpm test:all && pnpm build && pnpm test:motion
- go vet ./... and pinned golangci-lint for native and cross-platform build tags
- CGO_ENABLED=0 root/Desktop builds for darwin amd64/arm64, windows amd64/arm64, and linux amd64
- Wails binding generation, repolint, release workflow contracts, SDK ordinary/race tests, and go mod tidy -diff
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) desktop Wails desktop app (desktop/**) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 发送引导后反复出现 "inbox item state does not allow this operation",待处理引导永久卡死(无法删除/重试/发送)

1 participant