mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 10:36:27 +03:00
Roll call: a page that cannot resolve the target declines instead of claiming
Field-testing with two pages open showed the first-wins claim letting the wrong page answer: a tab whose page lacks the element won the claim, resolved the selector locally, and replied no_match while another page had the element. The overlay now resolves the selector before any claim and, when its page cannot resolve it, declines with reason no_match and the resolution verdict; the same check runs on the busy-to-idle re-claim. The server records that verdict on the report and, once every connected overlay has declined, prefers a report that could serve later (a tab mid-session or with an apply in flight, which answers busy so the agent retries) over no_match, and returns the resolution verdict only when no page can serve; the timeout uses the same precedence. Also from the same field tests: a tab on another page of the app was resuming this page's session from the per-origin localStorage cache after a dev-server reload re-initialised it, then sat in GENERATING for a wrapper it never renders and declined every later target. restoreSessionWithoutWrapper now resumes a cached session only on the page that saved it, the check the server-adoption branch beside it already applied. Covered by two Rust integration cases, two protocol cases, and contract assertions for the resolve-before-claim path and the page gate; the cross-page scenario of the field harness passes on a two-page site. AI-assisted: found by field tests and fixed with Claude Code under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Abdul Wahab
co-authored by
Claude Fable 5
parent
8fb7f7fec5
commit
25263adc51
@@ -7263,6 +7263,7 @@
|
||||
if (agentTargetOverlayGone()) return;
|
||||
const busy = agentTargetBusyReason();
|
||||
if (busy) { declineAgentTargetBusy(msg, busy); return; }
|
||||
if (declineAgentTargetUnresolvable(msg)) return;
|
||||
claimAgentTarget(msg.targetId, { eligible: true }).then((claim) => {
|
||||
if (claim.granted) { actOnAgentTarget(msg); return; }
|
||||
if (!claim.pending) return;
|
||||
@@ -7284,6 +7285,19 @@
|
||||
// target, so a replay must not start a second claim or a second Go.
|
||||
const agentTargetsSeen = [];
|
||||
|
||||
// Only a page that can resolve the target claims it. A tab whose page
|
||||
// lacks the element declines with its resolution verdict instead, so a
|
||||
// first-wins claim never lets the wrong page answer for a target that
|
||||
// another page has. The server prefers a busy report (a tab that could
|
||||
// serve later) over these, and returns the resolution verdict only when
|
||||
// no connected page can serve.
|
||||
function declineAgentTargetUnresolvable(msg) {
|
||||
const probe = resolveAgentTargetElement(msg);
|
||||
if (!probe.error) return false;
|
||||
claimAgentTarget(msg.targetId, { eligible: false, state, reason: 'no_match', result: probe.error });
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleAgentTarget(msg) {
|
||||
if (!msg || typeof msg.targetId !== 'string') return;
|
||||
if (agentTargetsSeen.includes(msg.targetId)) return;
|
||||
@@ -7297,6 +7311,7 @@
|
||||
declineAgentTargetBusy(msg, busy);
|
||||
return;
|
||||
}
|
||||
if (declineAgentTargetUnresolvable(msg)) return;
|
||||
// Eligible tabs race for the server's lease and only the holder acts. A
|
||||
// hidden tab yields a short head start so a visible one wins when both
|
||||
// exist, and still serves the request on its own: the user finds the
|
||||
@@ -9376,7 +9391,12 @@ void main() {
|
||||
}
|
||||
|
||||
function restoreSessionWithoutWrapper(reason, activeSessions) {
|
||||
const cached = loadSession();
|
||||
// The session cache is per origin, so a tab on another page of the same
|
||||
// app sees this page's session too. Only the page that saved it may
|
||||
// resume it: the server-adoption branch below already applies the same
|
||||
// check, and a tab on another page has nothing to render for it.
|
||||
const cachedRaw = loadSession();
|
||||
const cached = cachedRaw?.id && !pageMatchesCurrent(cachedRaw.pageUrl) ? null : cachedRaw;
|
||||
// localStorage is a cache, not a gate. A cleared tab, a second browser
|
||||
// profile, or a teardown that dropped local state all leave the durable
|
||||
// server session as the only record of work in progress; adopt it instead
|
||||
|
||||
Reference in New Issue
Block a user