mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-19 01:26:29 +03:00
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:
committed by
Abdul Wahab
co-authored by
Claude Fable 5
parent
a3bb21cbde
commit
335945525d
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user