Address review: a done target stays off-limits for a replay

Marking a target done on reply opened a window: an EventSource reconnect
replays the still-pending target while the result is on the wire, the
tab is GENERATING by then, so it declined busy, the server handed the
lease back mid-resolution, and another tab could claim and fire a second
Go. `agentTargetTaken` now covers both acting and done, so a replay of a
target this page took a lease on is ignored, and the late-mount watch
stops on either. Contract pins for the guard and the watch.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Abdul Wahab
2026-09-15 05:45:49 +05:00
committed by Abdul Wahab
co-authored by Claude Fable 5
parent a3bb21cbde
commit 335945525d
2 changed files with 31 additions and 10 deletions
+19 -8
View File
@@ -7289,18 +7289,29 @@
}
// This page's participation in each target it heard: 'acting' once a
// claim was granted (so a replay never starts a second Go), else the word
// it last gave. The server replays pending targets to every connection
// that opens. After a reconnect that overlapped the old connection the
// server still holds this page's word; after one that did not, it dropped
// the word on the close, so a replayed target is handled again: a busy or
// unresolvable page re-declines (idempotent), an idle page claims.
// claim was granted, 'done' once it replied (or stood down from a lapsed
// lease), else the word it last gave. The server replays pending targets
// to every connection that opens. After a reconnect that overlapped the
// old connection the server still holds this page's word; after one that
// did not, it dropped the word on the close, so a replayed target is
// handled again: a busy or unresolvable page re-declines (idempotent), an
// idle page claims.
const agentTargetsSeen = new Map();
function noteAgentTarget(targetId, status) {
agentTargetsSeen.set(targetId, status);
if (agentTargetsSeen.size > 100) agentTargetsSeen.delete(agentTargetsSeen.keys().next().value);
}
// A target this page took a lease on is off-limits for a replay: while
// acting (a second claim or Go), and once done, because its result may
// still be on the wire and this tab is GENERATING by then, so handling
// the replay would decline busy, hand the lease back mid-resolution, and
// let another tab fire a second Go.
function agentTargetTaken(targetId) {
const status = agentTargetsSeen.get(targetId);
return status === 'acting' || status === 'done';
}
// 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
@@ -7338,7 +7349,7 @@
}
function watchAgentTargetResolution(msg, lastError) {
if (agentTargetOverlayGone() || agentTargetsSeen.get(msg.targetId) === 'acting') return;
if (agentTargetOverlayGone() || agentTargetTaken(msg.targetId)) return;
const busy = agentTargetBusyReason(msg.targetId);
if (busy) { declineAgentTargetBusy(msg, busy); return; }
const probe = resolveAgentTargetElement(msg);
@@ -7350,7 +7361,7 @@
function handleAgentTarget(msg) {
if (!msg || typeof msg.targetId !== 'string') return;
if (agentTargetsSeen.get(msg.targetId) === 'acting') return;
if (agentTargetTaken(msg.targetId)) return;
noteAgentTarget(msg.targetId, 'heard');
const busy = agentTargetBusyReason(msg.targetId);
if (busy) {
+12 -2
View File
@@ -852,8 +852,18 @@ describe('live-browser source contracts', () => {
);
assert.match(
SOURCE,
/function handleAgentTarget\(msg\) \{[\s\S]{0,120}?if \(agentTargetsSeen\.get\(msg\.targetId\) === 'acting'\) return;/,
'a replayed target this page is acting on must not start a second claim or Go; any other replay is handled again',
/function handleAgentTarget\(msg\) \{[\s\S]{0,120}?if \(agentTargetTaken\(msg\.targetId\)\) return;/,
'a replayed target this page took a lease on must not start a second claim, Go, or decline; any other replay is handled again',
);
assert.match(
SOURCE,
/function agentTargetTaken\(targetId\) \{[\s\S]{0,200}?status === 'acting' \|\| status === 'done';/,
'a done target is still taken: a replay while its result is on the wire must not decline busy and hand the lease to a second Go',
);
assert.match(
SOURCE,
/function watchAgentTargetResolution\(msg, lastError\) \{\s*if \(agentTargetOverlayGone\(\) \|\| agentTargetTaken\(msg\.targetId\)\) return;/,
'the late-mount watch stops once this page took the lease',
);
assert.match(
SOURCE,