Skip to content

📴 fix: Persist Cancelled Compactions With Their Identity - #16436

Open
berry-13 wants to merge 24 commits into
devfrom
followup/33-follow-up-a-cancelled-compaction-persists-withou
Open

berry-13 wants to merge 24 commits into
devfrom
followup/33-follow-up-a-cancelled-compaction-persists-withou

Conversation

@berry-13

@berry-13 berry-13 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Pull Request

Summary

Closes berry-13#33, split out of the review of #15883.

Stopping a manual compaction mid-summarization persists the partial turn through the abort path, and that path had no way to know the run was a compaction: the job metadata flattens compact into isRegenerate, and the abort persistence sites build their row from stream-aggregated content alone. The row therefore carried no initiatedBy: 'user' marker, and on a branch ending in a user message the hover row kept Regenerate, which answered the user message behind the compaction instead of redoing it.

This carries compact on the job metadata and stamps the aborted content with the compaction identity at both abort persistence sites, through one helper alongside resolveFailedTurnContent so all three persistence routes share the rule that a compaction turn is always identifiable. A stopped compaction keeps its unfinished shape and any partial summary it streamed; a run stopped before any part streamed records the typed failure so the row is still identifiable.

How it works

createJob                    metadata: compact = true
  Stop                       GenerationJobManager.abortJob     markAbortedCompactionContent
  all subscribers disconnect controller allSubscribersLeft    markAbortedCompactionContent
  run completes              sendCompletion                    markCompactionOutcome      (unchanged)
  run throws                 saveErrorTurn                     resolveFailedTurnContent   (unchanged)

markAbortedCompactionContent stamps initiatedBy: 'user' on every summary and error part of the aborted content, the same marker isUserInitiatedCompaction reads to withhold edit, regenerate and continue, and appends the typed compaction_failed part when the stopped run streamed nothing yet.

Type of change

  • Bug fix

Testing

Tested environments/configuration: mock e2e harness (Playwright, in-process fake model with the fixture summarizer held silent via delayMs, stopped through the composer's stop control); jest for the unit and controller suites; MongoDB and the in-memory job store.

Automated tests:

  • packages/api: npx jest src/agents/compaction.spec.ts src/stream/__tests__/abortCompactionIdentity.spec.ts src/stream/__tests__/RedisJobStore.spec.ts: 97 passed
  • api: npx jest on the touched suites (request partial disconnect, resume metadata, abort route): 218 passed
  • reviewctl precheck (static + jest per touched workspace) on the final head: static pass, @librechat/api 2,855 related tests passed; the api workspace run had two suites crash on Jest worker OOM under shared-machine load with zero test failures (5,588 passed), environment rather than diff
  • New e2e scenarios in e2e/specs/mock/scenarios/compaction-rerun-controls.spec.ts, run through reviewctl verify on desktop light, desktop dark and mobile: cancelled-compaction-on-user-turn-offers-no-rerun-controls and cancelled-compaction-on-user-turn-survives-reload-without-rerun-controls, both passing on all three projects on the final head

Screenshots / recordings

No user-facing change beyond the withheld hover controls on a cancelled compaction row, which the new e2e scenarios assert in the DOM.

Risk / compatibility

No schema migration: the marker is a content-part field that already existed. Redis job records decode the new compact metadata key with absent-means-absent, so mixed-version records read as before. A stopped compaction that streamed nothing now persists a typed-failure row instead of an empty one; ordinary turns keep the early-abort behavior unchanged.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary
  • Required dependency changes have been merged/published
  • Required documentation PR: N/A

Copilot AI lite review requested due to automatic review settings September 28, 2026 07:34
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T19:06:06.085978Z 929997a New commits
🔒 Security Review ✅ Completed 2026-09-28T07:39:46.648260Z 5b8ceb2 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Empty compaction disconnects can still skip persistence, and metadata facade and Redis round-trip coverage gaps remain.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Persists manual compaction identity through cancellation and disconnect paths, preventing incorrect rerun controls.

Changes:

  • Adds compact metadata and Redis persistence.
  • Marks aborted compaction content or records typed failure content.
  • Adds unit, controller, and end-to-end coverage.
File Summary
packages/​api/​src/​types/​stream.ts Adds compaction metadata typing. Moderate issue: the job facade does not expose compact.
packages/​api/​src/​stream/​metadata.ts Sanitizes compaction metadata.
packages/​api/​src/​stream/​interfaces/​IJobStore.ts Extends serializable job metadata.
packages/​api/​src/​stream/​implementations/​RedisJobStore.ts Restores compaction metadata. Nit: add Redis round-trip coverage.
packages/​api/​src/​stream/​GenerationJobManager.ts Marks aborted compaction content.
packages/​api/​src/​stream/​__tests__/​abortCompactionIdentity.spec.ts Tests abort persistence.
packages/​api/​src/​agents/​compaction.ts Implements aborted-content identity marking.
packages/​api/​src/​agents/​compaction.spec.ts Tests helper behavior.
e2e/​specs/​mock/​scenarios/​compaction-rerun-controls.spec.ts Adds cancelled-compaction UI scenarios.
api/​server/​controllers/​agents/​request.js Persists compaction identity on disconnect. Critical issue: empty disconnect snapshots bypass typed failure persistence.
api/​server/​controllers/​agents/​__tests__/​request.resumeMetadata.spec.js Updates metadata mocks.
api/​server/​controllers/​agents/​__tests__/​request.partialDisconnect.spec.js Tests disconnect persistence.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread api/server/controllers/agents/request.js Outdated

@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: 5b8ceb2f3b

ℹ️ 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".

Comment on lines +1860 to +1863
const persistableContent = markAbortedCompactionContent(
filterPersistableAbortContent(aggregatedContent),
isCompaction,
);

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 Do not mark live disconnects as failed compactions

When the last SSE subscriber disconnects after a compaction has emitted a persistable non-summary part (such as thinking, activity, text, or a tool call), this handler runs while generation explicitly continues, but markAbortedCompactionContent finds no summary/error and appends compaction_failed. The partial row therefore reports a false failure on reload until a successful completion overwrites it, and can remain that way if final persistence fails. Use a marker-only path here and reserve synthesized failure content for the actual terminal abort path.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 6562583. The disconnect save now runs the marker-only mode (synthesizeFailure: false): it stamps what streamed and invents no failure, since the run is still live and the completing path overwrites the row. Regression in request.partialDisconnect.spec.js saves a think-only compaction partial with no error part.

Comment thread packages/api/src/agents/compaction.ts Outdated
Comment on lines +190 to +191
if (!markedOutcome) {
contentParts.push(...compactionFailureContent());

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 Treat empty summary placeholders as empty aborts

When Stop lands after summarization has started but before its first delta, the abort snapshot can contain an empty placeholder such as { type: 'summary', content: [], summarizing: true }. The loop sets markedOutcome solely from that type, so this branch skips the promised compaction_failed part even though no summary content was produced; once submission ends, the summary renderer no longer considers it actively streaming and can label the empty interrupted row as context compacted by the user. Only count a summary as an outcome when it contains streamed text, while still stamping the placeholder for identity.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 6562583. Only a summary carrying text counts as an outcome now (isSummaryPartWithText); the placeholder itself is still stamped and the typed failure is appended beside it. Covered in compaction.spec.ts and abortCompactionIdentity.spec.ts, including the manager-level abort snapshot.

@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: 0bf52141f5

ℹ️ 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".

Comment thread packages/api/src/agents/compaction.ts Outdated
Comment on lines +191 to +193
part.initiatedBy = 'user';
if (isSummaryPartWithText(part)) {
hasOutcome = 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 Mark aborted summaries as failed before persisting

When Stop arrives after the summarizer has opened a summary part, this stamps initiatedBy but preserves summarizing: true and leaves failed unset. After the run settles or the page reloads, Summary.tsx computes isActivelyStreaming as false and therefore labels the part “Context compacted by you”; a partial summary with text also sets hasOutcome, so no failure is appended, while an empty placeholder's sibling error does not affect the summary component. Fresh evidence after the prior thread is that the renderer selects its success label solely from the summary part's own failed and initiatedBy fields, so the attempted fix still makes a cancelled compaction claim success.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 6ed3ba8. The terminal path now applies the completed run's outcome rules: a usable summary is marked as the outcome, a text-bearing partial one keeps its text but is marked failed so the label reads Summarization failed, and an empty placeholder is replaced by the typed failure. Covered in compaction.spec.ts; the disconnect snapshot still stamps identity only since its row belongs to the live run.

Comment thread packages/api/src/agents/compaction.ts Outdated
Comment on lines +202 to +203
if (!hasOutcome && synthesizeFailure) {
contentParts.push(...compactionFailureContent());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not overwrite the compaction anchor on an empty abort

When a manual compaction is stopped before streaming any content, synthesizing this error makes shouldPersistAbortedTurn true, so the abort endpoint's beforePublish hook saves jobData.userMessage as a prerequisite. For a compaction that object is the already-persisted leaf projected with text: '', while the hook additionally forces sender: 'User' and isCreatedByUser: true; because saveMessage updates by the same message ID, Stop can erase a user leaf's text or convert an assistant leaf into an empty user message. Skip the prerequisite write when jobData.compact is true—the anchor already exists—while still persisting the new aborted response.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 6ed3ba8. The abort route skips the prerequisite user write when jobData.compact is true (the anchor is the persisted leaf itself) while still persisting the aborted response row. Regression in abort.spec.js asserts Stop saves exactly one row for a compaction. You are right that the overwrite was not only reachable through the synthesized failure: any compaction stop after the created event hit it on dev, so the guard closes the pre-existing window too.

@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: 6ed3ba8ba8

ℹ️ 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".

Comment on lines +1863 to +1866
const persistableContent = markAbortedCompactionContent(
filterPersistableAbortContent(aggregatedContent),
isCompaction,
{ synthesizeFailure: 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 Finalize disconnected compactions when the run errors

When all subscribers leave after only thinking/text or a partial summary, this persists a marker-only snapshot; if the provider subsequently throws, that snapshot is not overwritten. Fresh evidence beyond the prior disconnect thread is that saveErrorTurn returns as soon as it finds the row under liveResponseMessageId (lines 437-443), so a thinking-only row remains unmarked and exposes the wrong rerun controls, while a partial summary keeps failed unset and is labeled as a successful user compaction after reload. The terminal error path needs to finalize or replace an existing disconnected snapshot.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in db2cf6e8bb. saveErrorTurn's early-return branches now finalize a compaction's partial row through resolveFinalizedCompactionTurn (packages/api): a snapshot with no summary or error part gets the typed failure, a partial summary is marked failed beside its text, and an already-terminal row is untouched. Ordinary turns keep the existing partial-preservation behavior. The mock harness cannot drive disconnect-then-provider-fault, so the rule is covered by the resolveFinalizedCompactionTurn unit tests.

Comment thread api/server/routes/agents/index.js Outdated
Comment on lines +767 to +770
/** A compaction's `userMessage` is the already-persisted leaf
* projected for identity only; upserting it would erase a user
* leaf's text or turn an assistant leaf into an empty user row. */
const shouldPersistAnchor = jobData?.compact !== 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.

P1 Badge Move abort-anchor policy out of the CJS route

This adds shouldPersistAnchor and a new conditional persistence branch directly to the CJS abort route. The repository requires /api CJS files to contain wiring rather than behavior and specifically says newly added branches and persistence decisions belong in packages/api; move this compaction prerequisite policy behind a TypeScript helper and leave the route to invoke it.

AGENTS.md reference: AGENTS.md:L64-L68

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in db2cf6e8bb. The decision moved to shouldPersistAbortAnchor in packages/api/src/agents/compaction.ts; the route keeps only the wiring. Covered by unit tests plus abort.spec.js.

@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: eabaf2b613

ℹ️ 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".

Comment on lines +578 to +582
async function finalizeFailedCompactionTurn(
req,
{ userId, conversationId, messageId, partialRow },
) {
const finalized = resolveFinalizedCompactionTurn(partialRow, req.body);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move failed-row finalization out of the CJS controller

Unlike the resolved abort-anchor branch, this newly added finalizeFailedCompactionTurn helper introduces another decision branch and persistence workflow directly in api/server/controllers/agents/request.js. The repository requires new CJS branches, helpers, and service-call behavior under /api to live in packages/api; move the whole finalization operation behind a TypeScript entry point with its persistence dependency supplied by the caller, leaving this controller to invoke it.

AGENTS.md reference: AGENTS.md:L64-L68

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 20856aa30f. The operation moved to persistFinalizedCompactionTurn in packages/api/src/agents/compaction.ts with the write injected; the controller supplies its persistence dependency and the loaded row, and keeps no decision branch.

req?._agentEventBindingRetention?.expiredAt ?? req?.resolvedConversation?.expiredAt,
interfaceConfig: req?.config?.interfaceConfig,
},
{ messageId, conversationId, ...finalized },

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 Settle finalized snapshots as failed rows

When all subscribers disconnect and the provider subsequently throws, the existing snapshot was saved with unfinished: true and error: false, but this update only supplies identity and replacement content. Because saveMessage partially updates the row, those live-snapshot flags remain after terminal finalization, so a restored session persists a terminal failure as an unfinished non-error response and downstream consumers such as search/export continue to classify it as incomplete. Include the terminal envelope fields (unfinished: false and error: true, consistently with the normal saveErrorTurn path) in the finalized update.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 20856aa30f. persistFinalizedCompactionTurn writes the terminal envelope (unfinished false, error true) beside the finalized content, matching the normal saveErrorTurn row; covered by the new unit test.

Comment thread packages/api/src/agents/compaction.ts Outdated
Comment on lines +234 to +237
export function shouldPersistAbortAnchor(
jobData: { compact?: boolean } | null | undefined,
): boolean {
return jobData?.compact !== 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 Verify the compaction anchor before persisting an early abort

When Stop wins after createJob but before BaseClient has loaded the branch, or when the submitted parent ID is stale, jobData.userMessage is only the unverified projection created from the request. The synthesized failure makes the abort turn persistable, while this unconditional skip lets the route write its assistant child without confirming that the anchor exists, leaving an orphaned/phantom-root row on reload. Fresh evidence beyond the resolved anchor-overwrite thread is that this early-abort window precedes branch loading and isUnpersistedPreliminaryParent does not query ordinary UUID parent IDs; check anchor existence rather than rewriting it before saving the response.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 20856aa30f. shouldPersistAbortAnchor became resolveAbortAnchorDecision(jobData, anchorExists): the route now reads the anchor before deciding, writes only the response when the leaf is persisted, and nothing at all when it is not (Stop winning the pre-branch-load race). Both compaction branches have regressions in abort.spec.js.

@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: 20856aa30f

ℹ️ 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".

Comment thread api/server/routes/agents/index.js Outdated
jobData?.responseMessageId &&
shouldPersistAbortedTurn
shouldPersistAbortedTurn &&
anchorDecision !== 'skip-turn'

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 early-abort signaling when the anchor is absent

When Stop wins before a compaction's branch has loaded, the anchor lookup returns skip-turn, and this guard silently omits both database writes. However, abortJob has already synthesized a compaction_failed part, so it classifies the abort as non-early and constructs a normal FINAL containing the conversation and response; because this callback succeeds, that FINAL is published and the client can navigate to a response that was deliberately never persisted. Signal this outcome back to abortJob so it publishes an early-abort/reconciliation event instead.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a3d7c1f558. skip-turn now pushes onto beforePublish's persistenceErrors, the established channel for suppressing the normal FINAL: the manager publishes a reconciliation frame instead, and no row is written. The abort spec asserts both.

Comment on lines +271 to +274
const finalized = resolveFinalizedCompactionTurn(partialRow, requestBody);
if (finalized == null) {
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 Settle snapshots that already contain a terminal outcome

When a disconnected compaction snapshot already contains an error part or a failed summary and the live run subsequently throws, resolveFinalizedCompactionTurn returns null, causing this branch to skip the entire save rather than only skip the content rewrite. saveErrorTurn then returns immediately, leaving the snapshot's unfinished: true and error: false flags permanently intact. Fresh evidence after the prior terminal-envelope fix is that its new wrapper writes those fields only when content also needs finalization; it must still persist the terminal envelope for an already-marked outcome.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a3d7c1f558. resolveFinalizedCompactionTurn returns a discriminated write decision now: content finalization when the parts need it, an envelope-only write when they already carry the failure (error part or failed summary), and no write only for other turns' rows or a completed checkpoint. persistFinalizedCompactionTurn settles the terminal envelope in both write cases; both are unit tested.

@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: a3d7c1f558

ℹ️ 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".

Comment thread api/server/routes/agents/index.js Outdated
Comment on lines +774 to +775
if (jobData?.compact === true && jobData?.userMessage?.messageId && req?.user?.id) {
const anchorRows = await getMessages({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move the abort-anchor lookup out of the CJS route

Fresh evidence after the prior thread is that the current head still adds both the compaction-specific branch and a new getMessages service call directly to this CJS route; resolveAbortAnchorDecision only moved the final boolean decision. Move the existence lookup and policy into a TypeScript operation in packages/api, injecting the database reader so this route only supplies dependencies and invokes it.

AGENTS.md reference: AGENTS.md:L64-L68

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae4a0. The existence lookup and policy are now one async TypeScript operation (resolveAbortedTurnAnchorDecision) with the database reader injected; the route supplies its reader, awaits the decision, and pushes the withhold error. No compaction branch remains in the route.

Comment thread packages/api/src/agents/compaction.ts Outdated
Comment on lines +340 to +341
if (sawCheckpoint) {
return { write: 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 Settle completed disconnect snapshots

When a disconnected compaction has already emitted a usable summary but terminal persistence subsequently fails before overwriting the snapshot, saveErrorTurn finds that row and this branch performs no write. The snapshot was stored with unfinished: true and error: false, so the restored conversation permanently treats an already-terminal job as live/incomplete; use the loaded row's envelope to distinguish an already-finished response from a disconnect snapshot and settle the latter while preserving its completed summary.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae4a0. The resolver reads the row's own envelope: a completed checkpoint inside a snapshot still flagged unfinished settles its envelope while preserving the summary; an already-settled row stays untouched. Unit tested both ways.

Comment thread api/server/routes/agents/index.js Outdated
Comment on lines +775 to +779
const anchorRows = await getMessages({
user: req.user.id,
messageId: jobData.userMessage.messageId,
conversationId: jobData.conversationId,
});

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 Continue checkpoint cleanup when the anchor read fails

If this new anchor lookup throws because Mongo is temporarily unavailable, the exception exits beforePublish before the checkpoint-cleanup block at lines 887-899 runs. At that point abortJob has already won the terminal CAS, so the manager suppresses the normal FINAL and cleans up the job while leaving its checkpoint behind; capture the lookup failure while withholding the response write, then still execute checkpoint cleanup as the callback already does for message-save failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae4a0. The anchor read runs inside the injected operation and a failure returns skip-turn instead of throwing, so beforePublish continues to checkpoint cleanup while the response write is withheld.

Comment thread packages/api/src/agents/compaction.ts Outdated
if (!finalized.write) {
return false;
}
await saveMessage({

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 Reject falsy finalization writes

When the injected saveMessage resolves null or undefined rather than throwing—which the production method's contract explicitly permits—this helper still returns true, and saveErrorTurn consequently lets completeJob finalize the terminal barrier as though the snapshot had been settled. The row remains unfinished: true/error: false while clients receive terminal reconciliation, so require a truthy saved row or throw just as the surrounding failed-turn persistence path does.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae. The injected save must resolve truthy; a falsy result throws like the surrounding failed-turn persistence, and a unit test covers it.

Comment on lines +433 to +437
const existing = await getMessages({
user: userId,
messageId: errorMessageId,
conversationId,
});

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 the lightweight lookup for ordinary failures

For every non-compaction generation error, this changed query now materializes the entire existing response document even though finalizeFailedCompactionTurn immediately no-ops and the caller returns. Previously the _id projection avoided loading potentially large streamed content, tool results, and metadata on this error path; keep the lightweight projection for ordinary turns and request only the content/envelope fields needed when req.body.compact === true.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae4a0. Both existing-row checks keep the id-only projection for ordinary turns; the full document is requested only for a compaction's live response row, which is the one row the finalizer writes.

Comment on lines +439 to +443
await finalizeFailedCompactionTurn(req, {
userId,
conversationId,
messageId: errorMessageId,
partialRow: existing[0],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not finalize the compaction anchor as its response

When a compaction is anchored to a persisted message whose ID ends in _—a supported preliminary-response shape—getPreliminaryResponseMessageId at line 392 normalizes that anchor ID back to itself. This lookup therefore returns the anchor, and the newly added call marks that existing branch message error: true and can append compaction_failed before the code checks the distinct liveResponseMessageId; finalize only the exact live response row so a failed compaction cannot corrupt its parent.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2cc88ae4a0. The anchor-derived branch no longer finalizes anything; a compaction is finalized only under its distinct live response id, so an anchor-shaped id (underscore preliminary shape included) is never treated as the failed run's row.

@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: 2cc88ae4a0

ℹ️ 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".

Comment on lines 436 to 440
/** No compaction finalization here: this id can normalize back to the
* anchor itself when the anchor ends in `_`, and the anchor is never
* the failed run's row. The run's own snapshot, if any, is checked
* under its distinct live response id below. */
return;

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 Continue past anchor collisions to finalize the live row

When a failed compaction is anchored to an existing message whose ID ends in _, getPreliminaryResponseMessageId normalizes errorMessageId to that anchor, so this lookup succeeds and the return executes before the distinct liveResponseMessageId lookup below. If a disconnect snapshot was saved and the provider subsequently errors, that live row therefore remains unfinished: true/error: false, and a marked partial summary can be presented as a successful user compaction after reload. Fresh evidence after the resolved anchor-finalization thread is that the current early return still makes its promised “checked ... below” path unreachable in this supported ID shape; skip writing the anchor but continue to finalize the distinct live row.

AGENTS.md reference: AGENTS.md:L42-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 16da5d3. The existing-row settlement now lives in settleExistingRowsBeforeErrorTurn (packages/api): an anchor-shaped match settles the distinct live response row and never receives the error row, and the same settlement runs when nothing matched, so the collision shape no longer strands the snapshot. Covered by the anchor-shaped collision regression.

@berry-13
berry-13 force-pushed the followup/33-follow-up-a-cancelled-compaction-persists-withou branch from 16da5d3 to 84b3896 Compare September 28, 2026 14:20

@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: 84b38966ca

ℹ️ 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".

Comment thread packages/api/src/agents/compaction.ts
Comment thread api/server/controllers/agents/request.js Outdated
Comment on lines +775 to +778
messageExists: (messageId, conversationId) =>
getMessages({ user: req?.user?.id, messageId, conversationId }).then(
(rows) => rows.length > 0,
),

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 Project only identity when checking the compaction anchor

For every stopped compaction, this existence callback calls getMessages without a projection, so Mongo materializes the anchor's complete message—including potentially large content, tool results, attachments, and metadata—while the decision only reads rows.length. Fresh evidence beyond the resolved ordinary-failure projection thread is that this newly added abort-route lookup remains unprojected; pass the _id projection so Stop does not incur avoidable latency and heap use on large conversation leaves.

AGENTS.md reference: AGENTS.md:L186-L188

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in d54e8e1. The existence reader now requests the id-only projection, so Stop never materializes the anchor's content or attachments for a length check.

@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: f3e160ffc1

ℹ️ 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".

Comment thread api/server/controllers/agents/request.js Outdated
Comment thread packages/api/src/types/stream.ts
@berry-13
berry-13 force-pushed the followup/33-follow-up-a-cancelled-compaction-persists-withou branch from d54e8e1 to 8d53e95 Compare September 28, 2026 15:15

@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: b724f5eabe

ℹ️ 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".

Comment on lines +244 to +247
return (
jobRecord.status === 'complete' ||
jobRecord.status === 'error' ||
jobRecord.status === 'aborted'

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 snapshots while terminal persistence is pending

When the last subscriber leaves after claimBeforeResponsePersistence has changed the job to complete, error, or aborted with terminalPersistencePending: true, but before the Mongo write finishes, this helper reports the job as settled and the guard in request.js drops the disconnect snapshot. If that final write then fails, the terminal path only publishes reconciliation and the streamed compaction has no fallback row, losing its summary/content. This is distinct from the previously discussed check-to-save race: the fresh evidence is that the terminal status transition explicitly precedes BaseClient response persistence, so a terminal status does not yet prove the row is durable. Await the pending persistence outcome or retain a fallback until the pending marker clears.

AGENTS.md reference: AGENTS.md:L45-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in fb85ea6. isSettledJobRecord now treats terminalPersistencePending as unsettled, so a disconnect in the claim-to-write window keeps the snapshot as the fallback; covered by a unit test.

@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: 2702225ef0

ℹ️ 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".

* the guard reads the same record, so the window is the settling
* path's own commit span. Ordinary turns keep the pre-change behavior
* exactly: their snapshot is the fallback row, settled or not. */
if (isCompaction && isSettledJobRecord(jobRecord, jobCreatedAt)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move the disconnect-save guard into TypeScript

When a compaction's subscribers disconnect, this newly added branch decides in the legacy CJS controller whether persistence is skipped. The repository requires new /api branches and service behavior to live in packages/api, so move the settled-job guard behind a TypeScript operation that receives the job state and persistence dependencies, leaving this listener to invoke it.

AGENTS.md reference: AGENTS.md:L73-L81

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2485ce8. The guard became allowsDisconnectSnapshot in packages/api, combining the compaction flag with the settled-record check; the controller keeps only the invocation and the log line. Unit tests cover both turn kinds, and the disconnect specs assert a settled ordinary turn still persists while a settled compaction does not.

@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: 2485ce87e8

ℹ️ 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".

Comment on lines +259 to +263
return (
jobRecord.status === 'complete' ||
jobRecord.status === 'error' ||
jobRecord.status === 'aborted'
);

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 fallback snapshots after persistence reconciliation

When the last-subscriber callback is delayed until after the terminal Mongo write fails, publishTerminalClaim(..., null) can durably store a reconciliation event and clear terminalPersistencePending; this predicate then classifies the job as settled and allowsDisconnectSnapshot discards the only streamed fallback even though no message row was persisted. Fresh evidence after the prior pending-marker fix is that finalizeTerminalPersistence clears the marker for reconciliation events as well as successful finals, so the final event or another persistence-success signal must distinguish these cases.

AGENTS.md reference: AGENTS.md:L45-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 86e701b. isSettledJobRecord now also treats a record whose durable final event is a reconciliation frame as unsettled: a failed terminal write cleared the pending marker while publishing the conservative frame, and no row was persisted, so the streamed snapshot remains the turn's only fallback. Unit test distinguishes the reconcile frame from a settled final.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Extended in 418342c: the guard is now resolveDisconnectSnapshotMode, and a reconciled record promotes the snapshot to the terminal row, persisted with the terminal marking (the typed failure when no summary streamed) and the terminal envelope. Unit tests cover the mode decision and the persisted row; live runs keep the marker-only shape.

@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: 86e701b3db

ℹ️ 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".

Comment on lines +276 to +280
if (hasDurableReconcileFrame(jobRecord.finalEvent)) {
/** The terminal write failed and a reconciliation frame was published in
* its place: nothing was persisted for the turn, so the streamed
* snapshot remains its only row. */
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 Finalize reconciliation fallback before saving it

When the last-subscriber callback runs after a compaction’s terminal Mongo write has failed and the reconciliation frame has been committed, this exception makes allowsDisconnectSnapshot accept the write, but request.js still saves the marker-only content with unfinished: true and error: false. Consequently an errored run can restore as a successful partial summary, a completed run remains unfinished, and an aborted non-summary snapshot lacks its terminal failure outcome. The new reconciliation-aware path should persist a status-appropriate terminal envelope/outcome rather than the ordinary live snapshot.

AGENTS.md reference: AGENTS.md:L45-L51

Useful? React with 👍 / 👎.

@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: 418342cace

ℹ️ 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".

Comment on lines +1917 to +1918
unfinished: snapshotMode !== 'terminal',
error: snapshotMode === 'terminal',

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 terminal status when promoting reconciled snapshots

When the disconnect callback runs after a reconciled complete or aborted claim, resolveDisconnectSnapshotMode collapses both into the same terminal mode used for errors, so these lines persist every fallback as unfinished: false, error: true. A completed compaction with a usable summary is therefore restored as an error, while a stopped compaction loses the normal aborted envelope (unfinished: true, error: false). Fresh evidence after the prior thread is that the promotion now writes a terminal row but still ignores the available jobRecord.status; return a status-specific mode/envelope rather than treating every reconciliation as a failed generation.

AGENTS.md reference: AGENTS.md:L45-L51

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 929997a. resolveReconciledSnapshotEnvelope maps the claim's status to its row shape; unit and controller regressions cover aborted, complete, and error.

Comment on lines +1888 to +1891
const persistableContent = markAbortedCompactionContent(
filterPersistableAbortContent(aggregatedContent),
isCompaction,
{ synthesizeFailure: snapshotMode === 'terminal' },

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 Let empty reconciled compactions reach terminal synthesis

When Stop or the error path reconciles after its primary write fails before any content was emitted, closing the remaining subscriber invokes this handler with aggregatedContent: [], but the initial length guard returns before this terminal-mode synthesis can append compaction_failed. The reconciliation frame is then durable while no response row records the cancelled compaction at all. Load the terminal mode before rejecting empty content so reconciled compactions can synthesize their terminal outcome, while still skipping empty snapshots for live runs.

AGENTS.md reference: AGENTS.md:L49-L52

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 929997a. Empty content is rejected only for live snapshots; a promoted terminal snapshot synthesizes the typed failure from nothing, with a controller regression.

* runs keep the marker-only shape, a failed terminal write (settled
* for a reconciliation frame) promotes the snapshot to the turn's
* terminal row, and a durably settled compaction withholds it. */
const snapshotMode = resolveDisconnectSnapshotMode(isCompaction, jobRecord, jobCreatedAt);

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 Recheck the anchor before promoting a reconciled snapshot

When an early Stop finds that the submitted compaction anchor is absent, the abort route deliberately withholds both rows and publishes reconciliation, but a non-empty allSubscribersLeft callback then enters this new terminal mode and saves the snapshot using resumeState.userMessage, which is only the unverified identity projection from job metadata. That recreates the orphaned child the anchor decision was intended to suppress. Fresh evidence after the resolved anchor thread is that reconciliation promotion bypasses its existence check; verify the anchor again, or carry the route's skip-turn outcome, before permitting this fallback write.

AGENTS.md reference: AGENTS.md:L186-L189

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 929997a. resolveDisconnectSnapshotMode now takes an injected anchorExists reader and returns skip when the anchor is not persisted; both unit and controller regressions cover the withheld promotion.

@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: 929997a8b4

ℹ️ 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".

Comment on lines +1868 to 1869
if (partialResponseSaved || !aggregatedContent) {
return;

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 Finalize snapshots saved before reconciliation

When the first disconnect occurs while a compaction is live, this handler saves an unfinished: true snapshot and sets partialResponseSaved; if the completed response write later fails and the terminal claim settles to a reconciliation frame, there may be no further disconnect callback, and any callback that does occur returns here before resolveDisconnectSnapshotMode can promote the row. Unlike the previously reviewed delayed-first-callback ordering, the fresh evidence is this successful-earlier-callback ordering: a completed job can remain permanently stored as an unfinished live response after reload. Settle the already-saved snapshot from the terminal persistence-failure path, or allow reconciliation to reprocess it.

AGENTS.md reference: AGENTS.md:L45-L51

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
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.

2 participants