Polish lease UX: self-heal metadata damage, sanitize errors, retire lock sidecars / 打磨租约体验:元数据损坏自愈、错误脱敏、锁侧车清理#6030
Merged
SivanCola merged 2 commits intoJul 5, 2026
Conversation
The lease registry keys sessions through agent's canonical form (clean, absolute, lowercased on Windows), while desktop sessionRuntimeKey kept the raw path case. On Windows every tab path contains uppercase segments (C:\Users\...), so the tab's own lease never matched its session key: ensureSessionLease re-acquired against the process's own registry entry, reclaim then hit the tab's live OS lock handle, and every model / effort / token-mode / provider-key rebuild failed with the "already open in another Reasonix window" error on a single window — including fresh installs. - Export agent.CanonicalSessionPath and layer it into sessionRuntimeKey so desktop identity checks agree with the lease registry byte-for-byte. - Normalize the canonicalTabSessionPath fallback (project-scope sessions) with clean+abs so shape variants cannot split one file into two keys. - Keep case-folded keys out of SessionPath: detach/attach paths now carry the display path separately from the runtime key. - Release only the build's own lease on startup-build failure, and skip claiming a lease the build merely reused (a concurrent switch owns it). - Supersede in-flight startup builds when a model/effort/token switch installs its controller, so a slow blank-session build can no longer overwrite the new runtime or strip its lease. - Regression tests: lease/runtime key agreement, idempotence, Windows case folding, project-scope fallback normalization, fast-path lease reuse. Fixes esengine#5999. Fixes esengine#6006. Fixes esengine#5989. Fixes esengine#5990. Refs esengine#5996.
…rs, retire lock sidecars Follow-up polish on the lease/CAS stack after the esengine#6023/esengine#6025 hotfixes, closing the remaining ways a healthy single-window session could still present as busy, leak holder details, or litter the session directory. - Reclaim now arbitrates on the OS lock alone: a missing or unreadable lease.json (user cleanup, AV quarantine, crash tear) with a free lock is a leftover, not a holder. Previously an orphaned in-process entry whose info file disappeared could never be reclaimed - the fallback acquire re-hit the orphaned entry forever and every rebuild stayed busy. The desktop gate mirrors this: nil lease info allows the reclaim attempt, foreign holders are still refused. - Raw lease errors (session path + host-pid-writer id) can no longer reach the UI: startup bind failures (StartupErr feeds the topbar banner and tab metas), ClearSession's bridge returns, and the recovery-lease callback (chat notices via snapshot errors) all render the user-facing busy message; raw details stay in slog. The busy message drops its dangling 'before changing this setting' clause when no setting applies. - SessionLease.Release retires the .lock/.lease.lock sidecars through the existing atomic take-then-remove helpers (non-blocking, no-op against any live holder or in-flight save), so ordinary use stops accumulating lock files that previously waited for the next boot reconcile (esengine#6014). - Autosave failures warn once per burst and at most once per 5 minutes per tab: retries are slog-only, the user notice fires when the burst gives up. Explicit action saves keep immediate feedback. Every failure is still logged. - SaveRecoveryBranch tolerates event-index write failures like the other save paths: the recovery transcript and meta are already durable, so a failed listing accelerator no longer discards a successful recovery. - Lease test suite now feeds user-shape (mixed-case) paths to the APIs under test instead of pre-canonicalized ones - the pattern that let the Windows case-fold mismatch (esengine#5999) escape the suite - plus regressions for orphan-without-info, corrupt-info, foreign-info-refused, sidecar retirement on release, StartupErr sanitization, and the autosave warning debounce. Refs esengine#6014 esengine#5999 esengine#6027.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up polish on the lease/CAS stack after the #6023 / #6025 hotfixes. Those two PRs fixed the deterministic root causes; this one closes the remaining ways a healthy single-window session could still present as busy, leak holder details into the UI, or litter the session directory — the secondary irritants reported across the same incident issues.
Stacked on #6023 (branched from it; merge #6023 first and this PR reduces to one commit).
Changes
1. Lease reclaim self-heals metadata damage
TryReclaimCurrentProcessSessionLeasenow arbitrates on the OS lock alone. A missing or unreadablelease.json(deleted by the user, quarantined by AV, torn by a crash) with a free lock is a leftover, not a holder. Previously an orphaned in-process registry entry whose info file disappeared could never be reclaimed — the fallback acquire re-hit the orphaned entry forever, wedging every rebuild as busy. The desktop gate (canReclaimCurrentProcessSessionLease) mirrors this: nil lease info now allows the reclaim attempt (the OS lock arbitrates), while a readable info naming a foreign runtime is still refused.2. Raw lease errors can no longer reach the UI
The raw
SessionLeaseErrortext carries the session path and the holder'shost-pid-writerid (seen verbatim in #5990). Audited every surface end-to-end and sanitized the three that still leaked:tab.StartupErrfeeds the topbar error banner and the polled tab metas; all three assignment sites now render the user-facing busy message.ClearSessionbridge returns (both the direct path andclearActiveSessionRuntime).ctrl.Snapshot()into chat notices and bridge returns; the surfaced message is now sanitized while the raw details stay inslog.The busy message also drops its dangling "before changing this setting" clause when no setting applies. Trash/delete paths, deferred rebuilds, and serve/ACP were audited and confirmed clean (serve/ACP never touch leases).
3. Lock sidecars retire on release instead of accumulating
SessionLease.Releasenow retires.lock/.lease.lockthrough the existing atomic take-then-remove helpers — non-blocking, and a no-op against any live holder or in-flight save, preserving the delete-atomic-with-release invariant. Previously the sidecars were only swept on the next boot reconcile, so ordinary use accumulated lock files (#6014 reported 5 of them alongside the recovery pile-up).4. Autosave failure warnings stop streaming
A persistently failing disk (AV hold, full volume) used to emit a chat warning for every attempt of every turn (up to 3 per burst, unbounded across turns). Now: retries are slog-only, the user notice fires once when a burst gives up, and at most once per 5 minutes per tab. Explicit action saves keep immediate feedback; every failure is still logged.
5. Recovery branches tolerate index write failures
SaveRecoveryBranchno longer fails after the recovery transcript and meta are already durable just because the event-index (a listing accelerator) could not be written — matching the two save() call sites hardened in #6025.6. Lease test suite feeds user-shape paths
The suite previously passed pre-canonicalized paths to the APIs under test — the exact pattern that let the Windows case-fold mismatch (#5999) escape it. A
leaseTestPathhelper now feeds mixed-case user-shape paths to every API call (identical off-Windows, exercises the fold on Windows), with internal-state setup keeping canonical keys. New regressions: orphan-without-info reclaim, corrupt-info reclaim, foreign-info refusal, sidecar retirement on release (including the racing-successor guard), StartupErr sanitization, and the autosave warning debounce.Tests
go test ./internal/agentfull (+ lease/canonical subset with-race) — pass.cd desktop && go test .full suite — pass (one pre-existing assertion updated: it required the raw lease text inStartupErr, i.e. it had codified the leak; it now asserts the sanitized message and the absence of raw details).GOOS=windows GOARCH=amd64builds clean in both modules;gofmtclean.Cache impact
Cache-impact: none. Desktop runtime ownership, error presentation, sidecar lifecycle, and tests only; no provider-visible prompts, tool schemas, request serialization, or compaction changes.
Refs #6014 #5999 #6027 (Phase-1 groundwork for the write-protection convergence proposal).