mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Sync generated provider output
This commit is contained in:
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -175,6 +175,14 @@
|
||||
let pickedAnchorViewportTop = null;
|
||||
let pendingVariantAnchorRetryObserver = null;
|
||||
let pendingAcceptedSession = null;
|
||||
// Survives cleanupAcceptedSession on purpose: the id of an accept whose
|
||||
// POST was acknowledged (intent durable, epoch fenced) but whose actual
|
||||
// source promotion hasn't reported back yet. Accept is optimistic, so the
|
||||
// teardown nulls pendingAcceptedSession long before live-accept.mjs runs;
|
||||
// this marker is what lets the SSE 'error' branch still recognize a late
|
||||
// accept failure and say the variant was not saved (issue #384). Released
|
||||
// when the real accept result arrives or a new session starts.
|
||||
let awaitingAcceptResult = null;
|
||||
let variantObserver = null;
|
||||
let variantSelectionInFlight = false;
|
||||
let variantSelectionPromise = null;
|
||||
@@ -6474,12 +6482,20 @@
|
||||
break;
|
||||
case 'complete':
|
||||
case 'accept':
|
||||
// The real accept result arrived: the awaited failure window closed.
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'agent_done':
|
||||
// The deterministic accept has already committed the reviewed DOM
|
||||
// and fenced generation. Carbonize may continue in the background;
|
||||
// it must not hold the foreground picker hostage.
|
||||
// Only a carbonize agent_done is provably accept-side: accept
|
||||
// unlocks at the first variant, so a late generation agent_done
|
||||
// for the same session id can still arrive after Accept and must
|
||||
// not close the awaited failure window early (the SSE broadcast
|
||||
// carries no sourceEventType to tell the two apart).
|
||||
if (msg.data?.carbonize === true && awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) awaitingAcceptResult = null;
|
||||
if (msg.data?.carbonize === true && maybeCompleteAcceptedSession(msg)) break;
|
||||
break;
|
||||
case 'discarded':
|
||||
@@ -6491,11 +6507,27 @@
|
||||
case 'error':
|
||||
if (pendingAcceptedSession?.id && msg.id === pendingAcceptedSession.id) {
|
||||
pendingAcceptedSession = null;
|
||||
awaitingAcceptResult = null;
|
||||
setLiveState('CYCLING');
|
||||
updateBarContent('cycling');
|
||||
showToast('Could not complete accept cleanup. Try Accept again.', 5000);
|
||||
break;
|
||||
}
|
||||
// The optimistic teardown already released the session, so the
|
||||
// CYCLING recovery above can no longer match; without this branch
|
||||
// the failure fell through to the generic toast and the user had
|
||||
// no hint their variant was never written (issue #384).
|
||||
if (awaitingAcceptResult?.id && msg.id === awaitingAcceptResult.id) {
|
||||
awaitingAcceptResult = null;
|
||||
console.error('[impeccable] Accept failed after teardown:', msg.message);
|
||||
// Hedged on purpose: a carbonize-phase failure raises this same
|
||||
// error after the source WAS promoted, so "was not saved" would
|
||||
// overclaim. Normalize the server message's terminal punctuation
|
||||
// so the two sentences don't run together.
|
||||
const acceptFailDetail = String(msg.message || 'unknown error').trim().replace(/[.!?]?$/, '.');
|
||||
showToast('Accept failed: ' + acceptFailDetail + ' The variant may not have been saved. If the change is missing, pick the element and generate again.', 8000);
|
||||
break;
|
||||
}
|
||||
if (maybeCompleteSteer(msg)) break;
|
||||
console.error('[impeccable] Error:', msg.message);
|
||||
showToast('Error: ' + msg.message, 5000);
|
||||
@@ -6958,6 +6990,9 @@
|
||||
stripManualEditRuntimeState(selectedElement);
|
||||
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7037,6 +7072,9 @@
|
||||
|
||||
stopVoice({ suppressSubmit: true });
|
||||
pendingAcceptedSession = null;
|
||||
// A new session supersedes any accept still awaiting its result; a late
|
||||
// failure toast for the previous session would only mislead here.
|
||||
awaitingAcceptResult = null;
|
||||
currentSessionId = id8();
|
||||
expectedVariants = selectedCount;
|
||||
arrivedVariants = 0;
|
||||
@@ -7868,6 +7906,7 @@ void main() {
|
||||
markSessionHandled();
|
||||
setLiveState('CONFIRMED');
|
||||
document.documentElement.dataset.impeccableAcceptToPickingMs = String(Date.now() - acceptPayload.clientSentAt);
|
||||
awaitingAcceptResult = { id: acceptedSessionId };
|
||||
scheduleAcceptCleanup(pending);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
Reference in New Issue
Block a user