Skip to content

fix(accounts): align pending comment failure state with plebbit-js#48

Merged
tomcasaburi merged 3 commits intomasterfrom
fix/comment-publish-failure-review
Mar 26, 2026
Merged

fix(accounts): align pending comment failure state with plebbit-js#48
tomcasaburi merged 3 commits intomasterfrom
fix/comment-publish-failure-review

Conversation

@tomcasaburi
Copy link
Member

@tomcasaburi tomcasaburi commented Mar 26, 2026

Align pending account comment failure handling with the terminal publication semantics used by plebbit-js.

Closes #47


Note

Medium Risk
Changes affect comment publishing/session lifecycle and how failures are surfaced, which can impact user-visible state and error callbacks. Risk is moderated by substantial new/updated tests covering edge cases like retries and terminal state handling.

Overview
Pending account comments now transition to failed immediately when plebbit-js reports a terminal failure (publishingState === "failed" + state === "stopped") or when publish errors are recorded, instead of waiting solely on the 20-minute fallback; docs were updated to clarify this behavior.

accountsActions.publishComment now records errors onto the pending account comment, dedupes/normalizes error reporting, and ignores stale events from replaced retry comments (session/comment identity checks + deferred cleanup on terminal failure). Tests were updated/added to cover the new state derivation, error persistence, stale error suppression, and callback expectations.

Written by Cursor Bugbot for commit c6043fe. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Account comments now fail immediately when a publish error occurs, rather than waiting for a fallback timeout period.
  • Tests

    • Added test coverage for comment error state handling and terminal publication state transitions.

@coderabbitai
Copy link

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

This pull request fixes terminal publish failure handling for pending account comments. Instead of relying on a 20-minute timeout fallback, the system now immediately classifies comments as failed when the terminal state pair (publishingState === "failed" and state === "stopped") is detected or when publish errors occur. Error handling and session-comment tracking were refactored throughout the publish flow.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added note clarifying that pending comments transition to failed immediately upon terminal publish failure or error, rather than waiting for the 20-minute fallback.
Hook Tests & Implementation
src/hooks/accounts/accounts.test.ts, src/hooks/accounts/accounts.ts
Updated useAccountComment to detect failed state from terminal state pairs and error presence. Added tests verifying state transitions when publishingState is "failed" with various state values, and when error/errors fields are populated.
Store Action Tests
src/stores/accounts/accounts-actions.test.ts
Added assertions that publish errors populate error.message and errors[] in stored comments. Added test verifying publishingState: "failed" and state: "stopped" terminal pair updates are recorded correctly.
Store Action Implementation
src/stores/accounts/accounts-actions.ts
Refactored publishComment to: add error normalization and recording utilities; capture activeComment for consistent event wiring; clear comment associations on challenge retry; route error, statechange, and publishingstatechange events through session-aware handlers; synchronize client and publishing state via activeComment.
Skill Assignments
.codex/skills/make-closed-issue/SKILL.md, .cursor/skills/make-closed-issue/SKILL.md
Updated GitHub issue assignment from plebe1us to tomcasaburi in both skill files.

Sequence Diagram

sequenceDiagram
    participant Comment
    participant PublishSession
    participant AccountsStore
    participant Hook

    Comment->>PublishSession: publish()
    PublishSession->>PublishSession: ⚠️ Error occurs
    PublishSession->>AccountsStore: error event
    AccountsStore->>AccountsStore: recordPublishCommentError()
    AccountsStore->>AccountsStore: Update error & errors[]
    PublishSession->>PublishSession: emit statechange: "stopped"
    PublishSession->>PublishSession: emit publishingstatechange: "failed"
    PublishSession->>AccountsStore: statechange & publishingstatechange events
    AccountsStore->>AccountsStore: Update comment.state = "stopped"
    AccountsStore->>AccountsStore: Update comment.publishingState = "failed"
    Hook->>AccountsStore: getAccountCommentsStates()
    AccountsStore->>Hook: Detect terminal pair (failed + stopped)
    Hook->>Hook: Set state = "failed"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A comment once wandered in limbo so long,
Till errors were caught and states grew strong,
Now failures are instant, no waiting in pain—
The publish-state dance finds its destined chain! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes to .codex/skills/make-closed-issue/SKILL.md and .cursor/skills/make-closed-issue/SKILL.md updating assignee values appear unrelated to the accounts comment failure state fix. Remove the assignee changes in the skill files or provide justification for why they belong in this PR focused on comment failure state alignment.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(accounts): align pending comment failure state with plebbit-js' directly describes the main change: aligning pending comment failure handling with plebbit-js terminal semantics.
Linked Issues check ✅ Passed All coding requirements from issue #47 are met: publish errors are persisted, terminal state pairs are detected, live event tracking is implemented, and the hook correctly derives failure state.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/comment-publish-failure-review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tomcasaburi tomcasaburi force-pushed the fix/comment-publish-failure-review branch from 4fb355d to 222af7e Compare March 26, 2026 10:42
@tomcasaburi tomcasaburi self-assigned this Mar 26, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/stores/accounts/accounts-actions.ts (1)

1124-1270: ⚠️ Potential issue | 🟠 Major

Scope failure callbacks to the active session and close failed sessions.

Line 1124 intentionally detaches the old publication before a retry, but Lines 1210-1215 and 1267-1270 still call onError even when that emitter is no longer the active session. A late error from a replaced or abandoned comment can still surface as a user-visible failure. Also, the new state === "stopped" + publishingState === "failed" pair never calls cleanupPublishSessionOnTerminal(), so terminal failures remain active and can be overwritten by later events.

🔧 Suggested session-lifecycle fix
+    const finishFailedSession = () =>
+      queueMicrotask(() => cleanupPublishSessionOnTerminal(publishSessionId));
+
     activeComment.on("error", (error: Error) => {
-      publishCommentOptions.onError?.(
-        recordPublishCommentError(error, activeComment),
-        activeComment,
-      );
+      if (!getActiveSessionForComment(activeComment)) return;
+      const normalizedError = recordPublishCommentError(error, activeComment);
+      finishFailedSession();
+      publishCommentOptions.onError?.(normalizedError, activeComment);
     });
     activeComment.on("statechange", (state: string) => {
       const session = getActiveSessionForComment(activeComment);
       if (!session) return;
       const currentIndex = session.currentIndex;
       accountsStore.setState(({ accountsComments }) =>
         maybeUpdateAccountComment(accountsComments, account.id, currentIndex, (ac, acc) => {
-          ac[currentIndex] = { ...acc, state };
+          const nextAccountComment = { ...acc, state };
+          ac[currentIndex] = nextAccountComment;
+          if (
+            nextAccountComment.state === "stopped" &&
+            nextAccountComment.publishingState === "failed"
+          ) {
+            finishFailedSession();
+          }
         }),
       );
     });
     activeComment.on("publishingstatechange", async (publishingState: string) => {
       const session = getActiveSessionForComment(activeComment);
       if (!session) return;
       const currentIndex = session.currentIndex;
       accountsStore.setState(({ accountsComments }) =>
         maybeUpdateAccountComment(accountsComments, account.id, currentIndex, (ac, acc) => {
-          ac[currentIndex] = { ...acc, publishingState };
+          const nextAccountComment = { ...acc, publishingState };
+          ac[currentIndex] = nextAccountComment;
+          if (
+            nextAccountComment.state === "stopped" &&
+            nextAccountComment.publishingState === "failed"
+          ) {
+            finishFailedSession();
+          }
         }),
       );
       publishCommentOptions.onPublishingStateChange?.(publishingState);
     });
@@
     } catch (error) {
-      publishCommentOptions.onError?.(
-        recordPublishCommentError(error, activeComment),
-        activeComment,
-      );
+      if (!getActiveSessionForComment(activeComment)) return;
+      const normalizedError = recordPublishCommentError(error, activeComment);
+      finishFailedSession();
+      publishCommentOptions.onError?.(normalizedError, activeComment);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stores/accounts/accounts-actions.ts` around lines 1124 - 1270, The
error/state/publishing callbacks need to be scoped to the current publish
session: in the activeComment "error", "statechange" and "publishingstatechange"
handlers only call publishCommentOptions.onError or mutate state if
getActiveSessionForComment(activeComment) returns the same session that created
this handler (and the session is not abandoned via isPublishSessionAbandoned);
additionally when you detect the terminal failed pair (state === "stopped" &&
publishingState === "failed") call
cleanupPublishSessionOnTerminal(publishSessionId) so the session is closed and
cannot be overwritten by later events; update the try/catch publish() path
similarly to verify the session is still active before invoking
publishCommentOptions.onError(recordPublishCommentError(...), activeComment).
🧹 Nitpick comments (1)
src/hooks/accounts/accounts.ts (1)

325-330: Create a shared type for publish metadata properties instead of ad-hoc casting.

The AccountComment & { error?, errors?, publishingState?, state? } pattern appears at lines 325–330 and 546–547 in this file. Extract this shape into a dedicated interface (e.g., AccountCommentWithPublishMetadata) in src/types.ts and use it consistently. This reduces duplication and makes TypeScript upgrades safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/accounts/accounts.ts` around lines 325 - 330, Replace the ad-hoc
intersection type used where accountComment is cast (AccountComment & { error?:
Error; errors?: Error[]; publishingState?: string; state?: string }) by
declaring a shared exported interface named AccountCommentWithPublishMetadata
that extends AccountComment and defines those four optional properties, add that
interface to your central types module and export it, then import and use
AccountCommentWithPublishMetadata wherever the inline cast is used (e.g., the
accountComment variable casts) to remove duplication and ensure consistent
typing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/stores/accounts/accounts-actions.test.ts`:
- Around line 2176-2200: The test flakes because it emits events on commentRef
before the mocked createComment assigns it; change the test to wait
deterministically for commentRef to be set after calling
accountsActions.publishComment instead of using a fixed timeout: create a
Promise that resolves when your vi.spyOn(account.plebbit,
"createComment").mockImplementation assigns commentRef (or otherwise hook into
the mock to signal completion), await that promise after the act that calls
accountsActions.publishComment, and only then emit the "statechange" and
"publishingstatechange" events on commentRef; this ensures publishComment and
the createComment mock have completed before emitting events.

---

Outside diff comments:
In `@src/stores/accounts/accounts-actions.ts`:
- Around line 1124-1270: The error/state/publishing callbacks need to be scoped
to the current publish session: in the activeComment "error", "statechange" and
"publishingstatechange" handlers only call publishCommentOptions.onError or
mutate state if getActiveSessionForComment(activeComment) returns the same
session that created this handler (and the session is not abandoned via
isPublishSessionAbandoned); additionally when you detect the terminal failed
pair (state === "stopped" && publishingState === "failed") call
cleanupPublishSessionOnTerminal(publishSessionId) so the session is closed and
cannot be overwritten by later events; update the try/catch publish() path
similarly to verify the session is still active before invoking
publishCommentOptions.onError(recordPublishCommentError(...), activeComment).

---

Nitpick comments:
In `@src/hooks/accounts/accounts.ts`:
- Around line 325-330: Replace the ad-hoc intersection type used where
accountComment is cast (AccountComment & { error?: Error; errors?: Error[];
publishingState?: string; state?: string }) by declaring a shared exported
interface named AccountCommentWithPublishMetadata that extends AccountComment
and defines those four optional properties, add that interface to your central
types module and export it, then import and use
AccountCommentWithPublishMetadata wherever the inline cast is used (e.g., the
accountComment variable casts) to remove duplication and ensure consistent
typing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1511482-1225-488c-8ae2-a8922762bce5

📥 Commits

Reviewing files that changed from the base of the PR and between 1935f4d and 4fb355d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • README.md
  • package.json
  • src/hooks/accounts/accounts.test.ts
  • src/hooks/accounts/accounts.ts
  • src/stores/accounts/accounts-actions.test.ts
  • src/stores/accounts/accounts-actions.ts

@tomcasaburi
Copy link
Member Author

Addressed the remaining valid review point in the latest commit: stale publish-session errors are now ignored once a session is replaced or reaches the terminal failed pair, and regression tests cover both the retry and terminal-failure paths. Local verification: yarn build, yarn test, and yarn test:coverage:hooks-stores (still non-zero only because of the repo’s pre-existing sub-100% hooks/stores baseline).

@tomcasaburi
Copy link
Member Author

The remaining non-correctness review items are being declined. The / assignee updates were included intentionally because the username change to was requested during this PR and those files are repo-managed mirrors; the docstring coverage warning is not a repository gate and adding unrelated docstrings here would be noise; the shared-type suggestion in is a non-blocking cleanup and not required for this bug fix.

@tomcasaburi tomcasaburi merged commit a9f7111 into master Mar 26, 2026
8 checks passed
@tomcasaburi tomcasaburi deleted the fix/comment-publish-failure-review branch March 26, 2026 11:06
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.

Pending account comments misclassify terminal publish failures and retries

1 participant