Skip to content

feat(messages): make append delivery traceable - #1312

Draft
mindfn wants to merge 5 commits into
mainfrom
feat/1308-message-attempts
Draft

feat(messages): make append delivery traceable#1312
mindfn wants to merge 5 commits into
mainfrom
feat/1308-message-attempts

Conversation

@mindfn

@mindfn mindfn commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What

Closes #1308.

  • projects durable append and per-target attempt custody into the message UI
  • preserves prior attempt history while retry creates a new target attempt
  • makes retry idempotent under double-click and concurrent target updates, including cross-thread carriers

Verification

  • cross-family review: Fable approved the final two commits
  • targeted API + web tests: 93 passing

Known non-blocking

  • P3 review notes: terminal-reason mapping has two default paths; a small retry race may return 409 after a carrier is consumed; stale-snapshot/file-size follow-ups remain. Durable target custody and the tested retry fence are unchanged.

mindfn added 2 commits August 7, 2026 15:24
Why: expose durable append evidence and make failed target delivery safely retryable without resending user messages.
Why: repeated body exposure and rejected stale retries must not corrupt delivery history or leave dead retry controls.
@mindfn
mindfn requested a review from zts212653 as a code owner August 7, 2026 08:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c639fc123

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (!current.pendingTargetCats.includes(targetCatId as CatId)) return { next: current };
const attempts = ensureTargetAttempts(current);
const previous = latestTargetAttempt(attempts, targetCatId);
if (!previous || previous.id !== expectedAttemptId || previous.state !== 'failed') return { next: current };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow retrying invocation-cancelled attempts

When an invocation is canceled, QueueProcessor leaves the target in failedByCatIds but records its latest attempt as cancelled with terminalReason: 'invocation_cancelled'. The receipt therefore presents a failed target, yet this fence accepts only previous.state === 'failed' (and the UI similarly hides Retry for cancelled), so a stopped reply can never be retried even though its queue carrier remains pending. Accept invocation-cancelled attempts here while continuing to exclude source_withdrawn cancellations.

Useful? React with 👍 / 👎.

mindfn added 2 commits August 7, 2026 16:51
Why: queue/append intent and its durable receipt projection form one user journey; Stop remains independently reviewable.
Why: a stale client preference must not override persisted delivery intent, and a stopped invocation should remain recoverable without reopening author withdrawals.
@mindfn

mindfn commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head; prior inline findings were addressed in subsequent commits.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bab571401d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

this.deps.log.warn({ err, threadId, entryId, catId }, '[QueueProcessor] retry auto-dispatch failed');
});
} else {
void this.processNext(threadId, userId).catch((err) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict retry execution to the selected target

When a user-authored queue entry has multiple failed targets, InvocationQueue.retryFailedTarget clears failure state only for the clicked catId, but this call to processNext executes the entry with its entire unchanged targetCats array. The unselected failed targets are therefore run again without a corresponding retry attempt being appended for them, which can trigger unwanted work and leave their attempt history inconsistent with the execution. Dispatch a target-scoped snapshot or otherwise prevent sibling targets from running on a single-target retry.

Useful? React with 👍 / 👎.

Why: retrying one failed recipient must not re-run sibling targets or rewrite their attempt history; receipt assertions now preserve the durable attempt contract.
@mindfn

mindfn commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head; this addresses single-target retry dispatch and preserves the receipt attempt contract.

@mindfn
mindfn marked this pull request as draft August 7, 2026 09:34

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 526c9f0e1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (!this.deps.queue.markProcessingById(threadId, entryId)) return false;

const processing = this.deps.queue.getEntrySnapshot(threadId, userId, entryId);
return this.startReservedEntry(processing ?? current, slotKey, catId, [catId], true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable user batching for scoped retries

When the retried carrier is user-authored and another queued user entry has the same intent, owner, and full target set, this target-scoped call still enters executeEntry's user-batching block around lines 3485-3503. That block appends the other entry's content and marks it processing, but the invocation is routed only to [catId]; the unrelated entry is therefore executed without a retry attempt being appended for it, corrupting its receipt-attempt history and potentially settling work the user did not retry. Scoped retry executions should bypass normal user batching.

Useful? React with 👍 / 👎.

Comment on lines +2557 to +2559
const slotKey = QueueProcessor.slotKey(threadId, catId);
if (this.processingSlots.has(slotKey) || this.deps.invocationTracker.has(threadId, catId)) return false;
if (!this.deps.queue.markProcessingById(threadId, entryId)) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve retry scope while the target slot is busy

If another invocation or queue execution owns this target slot when Retry is clicked, the durable retry attempt has already been appended and the failed marker cleared, but this method simply returns false and its fire-and-forget caller ignores the result. The entry remains in the ordinary queue with no target-scoped dispatch owner, so a later generic dequeue or manual continue executes its full targetCats array and can rerun failed sibling targets without corresponding retry attempts. Keep the scoped retry pending until this slot is available, or avoid committing the attempt until its scoped reservation is secured.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UX:让追加消息与失败目标可见、可追溯、可重试

1 participant