Skip to content

chore(agent-workflow): track AI tooling and PR-first branch flow#26

Merged
tomcasaburi merged 2 commits intomasterfrom
chore/agent-workflow-tracking
Mar 11, 2026
Merged

chore(agent-workflow): track AI tooling and PR-first branch flow#26
tomcasaburi merged 2 commits intomasterfrom
chore/agent-workflow-tracking

Conversation

@tomcasaburi
Copy link
Member

@tomcasaburi tomcasaburi commented Mar 11, 2026

Ports the 5chan branch-and-PR workflow into bitsocial-react-hooks, including tracked .codex and .cursor files, mirrored hook/skill updates, and the new review-and-merge-pr flow. Updates .gitignore to allowlist the AI workflow subtrees instead of ignoring them. Fixes stale repo-name references so the committed tooling points at bitsocialnet/bitsocial-react-hooks and project 6.

Closes #25


Note

Medium Risk
Primarily docs/tooling, but it changes .gitignore behavior and introduces hook scripts (including git branch cleanup) that could affect developer workflows if enabled or misconfigured.

Overview
Adds repo-managed AI contributor tooling under .codex/ and .cursor/, including mirrored agent definitions (quality check runner, plan implementer, React patterns enforcer), reusable “skills” playbooks (commit/issue formatting, merge conflict resolution, plan execution, PR review+merge), and hook scripts for auto-formatting, running yarn install on package.json edits, syncing/pruning git branches, and running yarn build/yarn test on stop.

Updates .gitignore to allowlist these .codex/.cursor subtrees (instead of ignoring them entirely) and extends AGENTS.md with explicit PR-first git workflow rules plus guidance to keep Codex/Cursor workflow files aligned and tracked.

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

Summary by CodeRabbit

  • Documentation
    • Added many new guides and skills: commit/issue/release/PR workflows, merge-conflict, refactor, implement-plan, code-quality, React patterns, and various helper skills.
  • Chores
    • Added automated hooks for formatting, dependency install, branch sync, and build/test verification.
    • Updated ignore rules and expanded developer workflow guidance in AGENTS.md.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a8fab2da-2236-45f9-95a7-85357c2d7f29

📥 Commits

Reviewing files that changed from the base of the PR and between aa5cb91 and 9a7a16d.

📒 Files selected for processing (46)
  • .codex/agents/code-quality.md
  • .codex/agents/plan-implementer.md
  • .codex/agents/react-patterns-enforcer.md
  • .codex/hooks.json
  • .codex/hooks/format.sh
  • .codex/hooks/sync-git-branches.sh
  • .codex/hooks/verify.sh
  • .codex/hooks/yarn-install.sh
  • .codex/skills/commit-format/SKILL.md
  • .codex/skills/commit/SKILL.md
  • .codex/skills/context7/SKILL.md
  • .codex/skills/deslop/SKILL.md
  • .codex/skills/find-skills/SKILL.md
  • .codex/skills/fix-merge-conflicts/SKILL.md
  • .codex/skills/implement-plan/SKILL.md
  • .codex/skills/issue-format/SKILL.md
  • .codex/skills/make-closed-issue/SKILL.md
  • .codex/skills/readme/SKILL.md
  • .codex/skills/refactor-pass/SKILL.md
  • .codex/skills/release-description/SKILL.md
  • .codex/skills/review-and-merge-pr/SKILL.md
  • .codex/skills/you-might-not-need-an-effect/SKILL.md
  • .cursor/agents/code-quality.md
  • .cursor/agents/plan-implementer.md
  • .cursor/agents/react-patterns-enforcer.md
  • .cursor/hooks.json
  • .cursor/hooks/format.sh
  • .cursor/hooks/sync-git-branches.sh
  • .cursor/hooks/verify.sh
  • .cursor/hooks/yarn-install.sh
  • .cursor/skills/commit-format/SKILL.md
  • .cursor/skills/commit/SKILL.md
  • .cursor/skills/context7/SKILL.md
  • .cursor/skills/deslop/SKILL.md
  • .cursor/skills/find-skills/SKILL.md
  • .cursor/skills/fix-merge-conflicts/SKILL.md
  • .cursor/skills/implement-plan/SKILL.md
  • .cursor/skills/issue-format/SKILL.md
  • .cursor/skills/make-closed-issue/SKILL.md
  • .cursor/skills/readme/SKILL.md
  • .cursor/skills/refactor-pass/SKILL.md
  • .cursor/skills/release-description/SKILL.md
  • .cursor/skills/review-and-merge-pr/SKILL.md
  • .cursor/skills/you-might-not-need-an-effect/SKILL.md
  • .gitignore
  • AGENTS.md

📝 Walkthrough

Walkthrough

Adds tracked AI tooling and a set of repo-managed agent specs, hook configurations/scripts, and skill documentation; updates AGENTS.md and .gitignore so the new .codex/.cursor files are tracked and executed by hook runners.

Changes

Cohort / File(s) Summary
Agent Specifications
.codex/agents/code-quality.md, .codex/agents/plan-implementer.md, .codex/agents/react-patterns-enforcer.md, .cursor/agents/code-quality.md, .cursor/agents/plan-implementer.md, .cursor/agents/react-patterns-enforcer.md
New agent role documents describing workflows for code-quality verification, plan-based implementers, and React-pattern enforcement; include inputs, verification (yarn build/test), minimal-fix rules, and reporting templates.
Hook Configuration
.codex/hooks.json, .cursor/hooks.json
Defines hook groups and timeouts for afterFileEdit (format + yarn-install) and stop (sync branches + verify).
Hook Scripts
.codex/hooks/format.sh, .codex/hooks/yarn-install.sh, .codex/hooks/verify.sh, .codex/hooks/sync-git-branches.sh, .cursor/hooks/format.sh, .cursor/hooks/yarn-install.sh, .cursor/hooks/verify.sh, .cursor/hooks/sync-git-branches.sh
Adds shell scripts: formatting edited files with Prettier, running yarn install when package.json changes, running build/tests and restoring dist, and pruning/deleting integrated temporary branches (uses gh when available).
Skill Documentation
.codex/skills/*/SKILL.md, .cursor/skills/*/SKILL.md (multiple files: commit, commit-format, context7, deslop, find-skills, fix-merge-conflicts, implement-plan, issue-format, make-closed-issue, readme, refactor-pass, release-description, review-and-merge-pr, you-might-not-need-an-effect)
Adds 14+ SKILL.md specs covering commit conventions, conflict resolution, plan orchestration, refactor guidance, issue/PR workflows, release notes, React effect guidance, and tooling discovery.
Repo config & guidance
AGENTS.md, .gitignore
AGENTS.md extended with Git and AI tooling rules; .gitignore adjusted to allow tracking of .codex and .cursor subpaths (agents/hooks/skills) while keeping other platform excludes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
In burrows of code I hop and write,
Hooks lined up tidy through the night,
Agents whisper tests that run,
Branches pruned when work is done,
A carrot-nibbled README bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: adding tracked AI tooling files (.codex/ and .cursor/) and implementing a PR-first branch workflow, which aligns directly with the core objectives.
Linked Issues check ✅ Passed The PR addresses all primary objectives from issue #25: (1) stops ignoring .codex/ and .cursor/ directories; (2) replaces make-closed-issue with PR-first workflow; (3) updates tooling/documentation to reflect the new flow.
Out of Scope Changes check ✅ Passed All changes are scoped to AI tooling setup (agent/skill/hook definitions), .gitignore adjustments, and documentation updates (AGENTS.md). No runtime library code or unrelated features are modified.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/agent-workflow-tracking

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

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: 3

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (5)
src/lib/debug-utils.ts (1)

4-11: ⚠️ Potential issue | 🟡 Minor

Orphaned data under old store names won't be cleared.

After this rename, existing users with data stored under the old names (plebbitReactHooks-subplebbits, plebbitReactHooks-subplebbitsPages) will retain orphaned data that deleteDatabases no longer targets. If the intent is a clean migration, consider also clearing the legacy stores—either here or via a one-time migration utility.

🛡️ Optional: Clear both old and new store names during transition
 const deleteDatabases = () =>
   Promise.all([
     localForage.createInstance({ name: "plebbitReactHooks-accountsMetadata" }).clear(),
     localForage.createInstance({ name: "plebbitReactHooks-accounts" }).clear(),
     localForageLru.createInstance({ name: "plebbitReactHooks-communities" }).clear(),
+    localForageLru.createInstance({ name: "plebbitReactHooks-subplebbits" }).clear(), // legacy cleanup
     localForageLru.createInstance({ name: "plebbitReactHooks-comments" }).clear(),
     localForageLru.createInstance({ name: "plebbitReactHooks-communitiesPages" }).clear(),
+    localForageLru.createInstance({ name: "plebbitReactHooks-subplebbitsPages" }).clear(), // legacy cleanup
   ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/debug-utils.ts` around lines 4 - 11, The deleteDatabases function no
longer clears legacy localForage stores, leaving orphaned data; update
deleteDatabases to also clear the old store names (plebbitReactHooks-subplebbits
and plebbitReactHooks-subplebbitsPages) alongside the new names so both legacy
and current databases are removed during migration or provide a one-time
migration routine that deletes those legacy instances using the same
localForage/localForageLru createInstance(...).clear() pattern used for the
other stores.
src/stores/accounts/accounts-actions.test.ts (1)

393-422: ⚠️ Potential issue | 🟡 Minor

These accountName cases still pass if the option is ignored.

publishCommunityEdit, createCommunity, and deleteCommunity only assert “no throw” or that an address exists. They would still go green if the implementation silently used the active account instead of the named one. Please assert against the named account’s store entry or spy on that account’s plebbit instance.

Also applies to: 454-467

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

In `@src/stores/accounts/accounts-actions.test.ts` around lines 393 - 422, The
tests for publishCommunityEdit, createCommunity, and deleteCommunity currently
only check for no-throw or that an address exists, so update those tests to
verify the named account was actually used: after calling
accountsActions.createAccount("Name") and invoking
accountsActions.publishCommunityEdit/createCommunity/deleteCommunity with the
accountName argument, assert that the operation used that account's store entry
(e.g., read the account record for "SubEditAccount"/"CreateSubAccount" from the
accounts store and assert its expected plebbit/request was used) or spy on the
named account's plebbit instance to confirm the API method was called for that
named account rather than the active account; modify the tests referencing
publishCommunityEdit, createCommunity, and deleteCommunity to include one of
these explicit assertions.
src/hooks/feeds/feeds.ts (2)

199-223: ⚠️ Potential issue | 🟠 Major

Forward the rest of the feed options when buffering.

useBufferedFeeds derives postsPerPages, filters, and newerThans, but the addFeedToStore() call drops them. The buffered feed stored under that feedName can therefore be built with the wrong page size or unfiltered posts.

Suggested fix
-        addFeedToStore(feedName, uniqueCommunityAddresses, sortType, account, isBufferedFeed).catch(
-          (error: unknown) =>
-            log.error("useBufferedFeeds addFeedToStore error", { feedName, error }),
-        );
+        addFeedToStore(
+          feedName,
+          uniqueCommunityAddresses,
+          sortType,
+          account,
+          isBufferedFeed,
+          postsPerPages[i],
+          filters[i],
+          newerThans[i],
+        ).catch((error: unknown) =>
+          log.error("useBufferedFeeds addFeedToStore error", { feedName, error }),
+        );

Also applies to: 233-249

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

In `@src/hooks/feeds/feeds.ts` around lines 199 - 223, The buffering logic in
useBufferedFeeds drops postsPerPage, filter, and newerThan when calling
addFeedToStore, causing buffered feeds (identified by feedName computed from
feedNames/useFeedNames and sortTypes/uniqueCommunityAddressesArrays) to be
recreated with wrong page size or filters; update the addFeedToStore call inside
useBufferedFeeds to include and forward the remaining feed options
(postsPerPage, filter, newerThan and any other feedOptions fields you derived
into postsPerPages/filters/newerThans) so the stored buffered feed preserves the
original feedOptions used to compute feedName and pagination.

291-308: ⚠️ Potential issue | 🟠 Major

Fix mutation of incoming arrays in normalization helpers.

Both useUniqueSortedArrays and useUniqueSorted call .sort() directly on incoming arrays, which mutates them in place. This reorders parent state arrays (communityAddresses / feedsOptions[*].communityAddresses) and can unintentionally change feed keys as a side effect.

Suggested fix
-      uniqueSorted.push([...new Set(stringsArray.sort())]);
+      uniqueSorted.push([...new Set([...stringsArray].sort())]);
...
-    return [...new Set(stringsArray.sort())];
+    return [...new Set([...stringsArray].sort())];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/feeds/feeds.ts` around lines 291 - 308, The helpers mutate incoming
arrays by calling .sort() directly; update useUniqueSortedArrays and
useUniqueSorted to sort copies instead of the originals: for
useUniqueSortedArrays, iterate arrs and for each stringsArray create a shallow
copy first (e.g., [...stringsArray]) then sort and dedupe with new Set before
pushing to uniqueSorted; for useUniqueSorted, return a deduped sorted array
built from a shallow copy of stringsArray (e.g., [...stringsArray].sort()) so
the parent arrays (communityAddresses / feedsOptions[*].communityAddresses) are
not mutated by useUniqueSortedArrays or useUniqueSorted.
src/hooks/accounts/accounts.test.ts (1)

2347-2392: ⚠️ Potential issue | 🟠 Major

Restore the prototype mock in a finally block.

If any waitFor or assertion fails before Line 2392, Community.prototype.rolesToGet stays patched and contaminates later tests. Wrap the override and the test body in try/finally so cleanup is guaranteed.

Proposed fix
       const moderatorAuthorAddress = "author address";
       const moderatingCommunityAddress = "moderating community address";
       const rolesToGet = Community.prototype.rolesToGet;
       Community.prototype.rolesToGet = () => ({
         [moderatorAuthorAddress]: { role: "moderator" },
       });

-      const rendered = renderHook<any, any>((communityAddress) => {
-        const { accountCommunities } = useAccountCommunities();
-        const account = useAccount();
-        const { setAccount } = accountsActions;
-        const community = useCommunity({ communityAddress });
-        return { accountCommunities, setAccount, account };
-      });
-      const waitFor = testUtils.createWaitFor(rendered);
-      await waitFor(() => rendered.result.current.account);
+      try {
+        const rendered = renderHook<any, any>((communityAddress) => {
+          const { accountCommunities } = useAccountCommunities();
+          const account = useAccount();
+          const { setAccount } = accountsActions;
+          const community = useCommunity({ communityAddress });
+          return { accountCommunities, setAccount, account };
+        });
+        const waitFor = testUtils.createWaitFor(rendered);
+        await waitFor(() => rendered.result.current.account);

-      // change author address
-      await act(async () => {
-        await rendered.result.current.setAccount({
-          ...rendered.result.current.account,
-          author: { address: moderatorAuthorAddress },
-        });
-      });
-      await waitFor(
-        () => rendered.result.current.account.author.address === moderatorAuthorAddress,
-      );
-      // account communities are not yet added, will be added after we fetch the sub
-      expect(rendered.result.current.accountCommunities[moderatingCommunityAddress]).toBe(
-        undefined,
-      );
-      expect(rendered.result.current.account.communities[moderatingCommunityAddress]).toBe(
-        undefined,
-      );
+        // change author address
+        await act(async () => {
+          await rendered.result.current.setAccount({
+            ...rendered.result.current.account,
+            author: { address: moderatorAuthorAddress },
+          });
+        });
+        await waitFor(
+          () => rendered.result.current.account.author.address === moderatorAuthorAddress,
+        );
+        expect(rendered.result.current.accountCommunities[moderatingCommunityAddress]).toBe(
+          undefined,
+        );
+        expect(rendered.result.current.account.communities[moderatingCommunityAddress]).toBe(
+          undefined,
+        );

-      // fetch the moderating community from the moderator account
-      rendered.rerender(moderatingCommunityAddress);
-      await waitFor(() => rendered.result.current.accountCommunities[moderatingCommunityAddress]);
-      expect(rendered.result.current.accountCommunities[moderatingCommunityAddress].role.role).toBe(
-        "moderator",
-      );
-      await waitFor(() => rendered.result.current.account.communities[moderatingCommunityAddress]);
-      expect(
-        rendered.result.current.account.communities[moderatingCommunityAddress].role.role,
-      ).toBe("moderator");
-
-      // unmock the roles
-      Community.prototype.rolesToGet = rolesToGet;
+        // fetch the moderating community from the moderator account
+        rendered.rerender(moderatingCommunityAddress);
+        await waitFor(() => rendered.result.current.accountCommunities[moderatingCommunityAddress]);
+        expect(
+          rendered.result.current.accountCommunities[moderatingCommunityAddress].role.role,
+        ).toBe("moderator");
+        await waitFor(() => rendered.result.current.account.communities[moderatingCommunityAddress]);
+        expect(
+          rendered.result.current.account.communities[moderatingCommunityAddress].role.role,
+        ).toBe("moderator");
+      } finally {
+        Community.prototype.rolesToGet = rolesToGet;
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/accounts/accounts.test.ts` around lines 2347 - 2392, You currently
replace Community.prototype.rolesToGet and restore it at the end of the test,
but if an assertion or waitFor throws the mock remains patched; wrap the
override and all following test logic that depends on it in a try/finally: store
the original value in rolesToGet, set Community.prototype.rolesToGet = () =>
{...} before the try, run the test body (renders, waits, assertions) inside try,
and in finally restore Community.prototype.rolesToGet = rolesToGet so the
prototype is always reset even on failures.
♻️ Duplicate comments (1)
.cursor/skills/release-description/SKILL.md (1)

14-16: ⚠️ Potential issue | 🟡 Minor

Git tag sorting may not return the semantically latest release.

Same issue as in .codex/skills/release-description/SKILL.md - the command uses --sort=-creatordate which could return an incorrect tag if tags are created out of chronological order.

Refer to the comment on .codex/skills/release-description/SKILL.md lines 14-16 for details and suggested fix.

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

In @.cursor/skills/release-description/SKILL.md around lines 14 - 16, The git
tag command `git tag --sort=-creatordate | head -1` can return a non-semantic
"latest" tag if tags were created out of chronological order; replace that
pipeline with a semver-aware sort such as using `--sort=-v:refname` so the
command becomes `git tag --sort=-v:refname | head -1`, updating the occurrence
of `git tag --sort=-creatordate | head -1` in the file to use
`--sort=-v:refname` instead.
🟠 Major comments (25)
.codex/skills/review-and-merge-pr/SKILL.md-17-20 (1)

17-20: ⚠️ Potential issue | 🟠 Major

Don't send the no-PR path back through make-closed-issue.

Line 19 points the agent back to the commit-first flow this PR is replacing. If no PR exists yet, the next step should be to create/open one for the current branch, not invoke the old close-on-commit workflow.

Suggested fix
-If there is no open PR yet, stop and use `make-closed-issue` first.
+If there is no open PR yet, stop and create/open a PR for the current branch before proceeding.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/skills/review-and-merge-pr/SKILL.md around lines 17 - 20, Update the
decision flow in the SKILL.md logic so that when the current branch is not
`master` and no open PR exists, the agent creates/opens a PR for the current
branch instead of routing to the `make-closed-issue` (commit-first) path;
specifically, remove or replace the sentence that instructs sending the no-PR
case back through `make-closed-issue` and add a line instructing the agent to
open a PR for the current branch (keeping the existing master-specific behavior
of inspecting open PRs and choosing one matching the user request).
.codex/agents/react-patterns-enforcer.md-13-17 (1)

13-17: ⚠️ Potential issue | 🟠 Major

Use a broader strategy to detect changed files instead of only HEAD~1.

The git diff --name-only HEAD~1 -- '*.ts' approach only inspects the diff between the current HEAD and its parent commit. This misses:

  • Uncommitted/unstaged changes in the working tree
  • Staged changes awaiting commit
  • Changes from multiple commits if the agent runs after several commits

To catch all recently modified files, check:

Alternative detection approach
# Uncommitted working tree changes:
git diff --name-only -- '*.ts'

# Staged changes:
git diff --cached --name-only -- '*.ts'

# Last committed delta (if relevant):
git diff --name-only HEAD~1 -- '*.ts'

Then union these sets to get the full picture of recent changes.

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

In @.codex/agents/react-patterns-enforcer.md around lines 13 - 17, The current
detection uses the single command `git diff --name-only HEAD~1 -- '*.ts'`, which
misses staged and working-tree changes; replace that logic with a union of three
checks: `git diff --name-only -- '*.ts'` (working-tree unstaged), `git diff
--cached --name-only -- '*.ts'` (staged), and `git diff --name-only HEAD~1 --
'*.ts'` (last commit), combine their outputs (deduplicate) to produce the final
file list, and update the code where `git diff --name-only HEAD~1 -- '*.ts'` is
invoked so it runs these three commands and merges results before proceeding.
.cursor/agents/react-patterns-enforcer.md-13-17 (1)

13-17: ⚠️ Potential issue | 🟠 Major

Use git diff --name-only instead of HEAD~1 to detect all recent changes.

The command git diff --name-only HEAD~1 only inspects the last committed delta. This misses files modified but not yet committed (staged or unstaged). Since the agent description says "Use after writing or modifying" without requiring a commit first, the current command will silently skip changed files when run on uncommitted changes.

Replace with git diff --name-only to detect working-tree changes, or git diff --cached --name-only for staged changes, depending on intended workflow. If comparing to a specific baseline, document it explicitly.

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

In @.cursor/agents/react-patterns-enforcer.md around lines 13 - 17, The current
guidance runs git diff --name-only HEAD~1 which only shows the last commit;
change the command text in the agent instructions to use git diff --name-only
(or offer git diff --cached --name-only for staged-only) so uncommitted
working-tree changes are detected as described, and update any adjacent
documentation/comments to explain that git diff --name-only checks working-tree
changes while --cached limits to staged files.
.codex/skills/fix-merge-conflicts/SKILL.md-58-60 (1)

58-60: ⚠️ Potential issue | 🟠 Major

Broaden conflict marker detection to all file types and hidden directories.

The current command is limited to TypeScript and JSON files, which will miss unresolved merge conflicts in Markdown, YAML, shell scripts, and other file types. Additionally, it excludes hidden directories like .codex/ and .cursor/ by default, which this PR is starting to track. For a skill teaching merge conflict resolution, the detection command should be comprehensive.

Suggested fix
-rg '<<<<<<<|=======|>>>>>>>' --type ts --type json
+rg -n --hidden --glob '!.git' '<<<<<<<|=======|>>>>>>>' .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/skills/fix-merge-conflicts/SKILL.md around lines 58 - 60, The ripgrep
invocation that currently restricts search to TypeScript/JSON (the command using
--type ts --type json and the pattern '<<<<<<<|=======|>>>>>>>') should be
changed to scan all files and include hidden/ignored directories; remove the
--type filters and add ripgrep flags to include hidden files and respect
no-ignore (e.g., use --hidden and --no-ignore or -uu) so the search will catch
conflict markers in Markdown, YAML, shell scripts, and tracked hidden dirs like
.codex/.cursor; update the example command in SKILL.md to reflect these flags
and keep the same conflict marker regex.
src/lib/plebbit-js/plebbit-js-mock.test.ts-85-88 (1)

85-88: ⚠️ Potential issue | 🟠 Major

Use the renamed community field consistently.

This test now passes communityAddresses as a string, while the rest of the rename lands on singular communityAddress. If the plural form is intentional, the value should at least be an array; as written, the mock can accept an input shape the real publish path rejects.

Suggested fix
     const comment = await plebbit.createComment({
       content: "content",
-      communityAddresses: "community address",
+      communityAddress: "community address",
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/plebbit-js/plebbit-js-mock.test.ts` around lines 85 - 88, The test is
passing the old/incorrect field communityAddresses as a string; update the call
to match the renamed API by using the singular communityAddress and provide the
correct type (an array of addresses if the real publish path expects multiple
addresses) — e.g., change the createComment call in plebbit-js-mock.test.ts to
pass communityAddress: ["community address"] (or a single-element array) so the
mock's input shape matches the real publish path and the createComment function
signature.
.cursor/skills/review-and-merge-pr/SKILL.md-17-20 (1)

17-20: ⚠️ Potential issue | 🟠 Major

Don't send no-PR cases back to make-closed-issue.

That reintroduces the close-on-commit flow this repo is replacing. If no PR exists yet, the skill should hand off to the PR-creation flow instead of the old issue-closing workflow.

Suggested fix
-If there is no open PR yet, stop and use `make-closed-issue` first.
+If there is no open PR yet, stop and use the PR-creation workflow first.
+Do not use `make-closed-issue`, because this repo now requires reviewed PRs before closing work.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/skills/review-and-merge-pr/SKILL.md around lines 17 - 20, The logic
in the skill should not route "no PR exists" cases back to make-closed-issue;
update the decision flow so when the current branch is not `master` it prefers
the PR for that branch, and when the current branch is `master` it inspects open
PRs to match the user request, but if no open PR is found it should hand off to
the PR-creation flow (do not call or route to `make-closed-issue`); modify the
control path that currently invokes `make-closed-issue` to instead invoke or
queue the PR creation skill/workflow.
.codex/skills/deslop/SKILL.md-9-22 (1)

9-22: ⚠️ Potential issue | 🟠 Major

Diff against master here, not main.

This repo's default branch is master, so these commands point at the wrong base. Either switch to master or resolve the default branch dynamically before diffing.

Suggested fix
-Scan the diff against main and remove AI-generated slop introduced in this branch.
+Scan the diff against `master` and remove AI-generated slop introduced in this branch.
...
-   git diff main...HEAD
+   git diff master...HEAD
...
-   git diff main
+   git diff master
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/skills/deslop/SKILL.md around lines 9 - 22, The workflow examples in
SKILL.md use "git diff main...HEAD" and "git diff main" which are wrong for this
repo; update those examples to use the repo's default branch (replace "main"
with "master") or show a dynamic approach (e.g., resolve the default branch) and
add a short note explaining to use "master" when this repo's default branch is
master so readers run the correct git diff command.
.cursor/skills/fix-merge-conflicts/SKILL.md-56-60 (1)

56-60: ⚠️ Potential issue | 🟠 Major

Search all files for leftover conflict markers.

The current rg only scans TS/JSON, so markers can survive in Markdown, YAML, lockfiles, or shell scripts while the skill reports success. Drop the type filters or derive the exact file list from git before declaring the repo clean.

Suggested fix
-rg '<<<<<<<|=======|>>>>>>>' --type ts --type json
+rg -n '^(<<<<<<<|=======|>>>>>>>)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/skills/fix-merge-conflicts/SKILL.md around lines 56 - 60, Update the
"Verify no remaining markers" step so the ripgrep invocation checks all tracked
files instead of only TypeScript/JSON: locate the line containing the rg pattern
'<<<<<<<|=======|>>>>>>>' in the "### 4. Verify no remaining markers" section
and remove the --type ts --type json filters (or alternatively replace the
command with one that enumerates files from git e.g., using git ls-files) so
conflict markers cannot remain in Markdown, YAML, lockfiles, or scripts.
.codex/skills/you-might-not-need-an-effect/SKILL.md-27-31 (1)

27-31: ⚠️ Potential issue | 🟠 Major

Run tests after fix=true refactors.

Build-only verification is too weak for an automated effect-removal skill. These edits can preserve types while still breaking runtime behavior, so the workflow should run yarn test whenever it changes code.

Suggested fix
-  - `fix=true`: apply the refactors, then verify with `yarn build`
+  - `fix=true`: apply the refactors, then verify with `yarn build`
+    and `yarn test`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/skills/you-might-not-need-an-effect/SKILL.md around lines 27 - 31,
Update the "3. **Fix or propose**" step so that when `fix=true` the workflow
runs tests after building: replace the single `yarn build` verification with
`yarn build` followed by `yarn test` and ensure the step fails (returns non-zero
/ aborts) if tests fail; refer to the "3. **Fix or propose**" block and the
`fix=true` branch, and mention `yarn build` and `yarn test` explicitly so
maintainers know to run both commands and treat test failures as errors.
src/lib/plebbit-js/plebbit-js-mock-content.ts-1002-1004 (1)

1002-1004: ⚠️ Potential issue | 🟠 Major

Preserve the edit payload when creating CommunityEdit.

createCommunityEdit() now ignores the incoming options, and CommunityEdit no longer copies any fields onto the publication. The mock loses address/title/description edits, so the community-edit flow diverges from the other publication mocks.

🛠️ Proposed fix
-  async createCommunityEdit() {
-    return new CommunityEdit();
+  async createCommunityEdit(createCommunityEditOptions: any) {
+    return new CommunityEdit(createCommunityEditOptions);
   }
-class CommunityEdit extends Publication {}
+class CommunityEdit extends Publication {
+  constructor(publishCommunityEditOptions: any) {
+    super();
+    for (const prop in publishCommunityEditOptions) {
+      // `@ts-ignore`
+      this[prop] = publishCommunityEditOptions[prop];
+    }
+  }
+}

Also applies to: 1429-1429

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

In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1002 - 1004,
createCommunityEdit() currently ignores the incoming options and returns a new
empty CommunityEdit, losing edits (address/title/description); update
createCommunityEdit(options) to pass the incoming options (payload) into the
CommunityEdit constructor or call a setter so the instance copies/preserves all
fields from the provided options, and ensure CommunityEdit's constructor or a
copyFrom method assigns address, title, description and any other publication
edit fields; apply the same change to the other occurrence of
createCommunityEdit to keep mock behavior consistent with other publication
mocks.
src/lib/plebbit-js/plebbit-js-mock-content.ts-1197-1211 (1)

1197-1211: ⚠️ Potential issue | 🟠 Major

Don't replace this.posts with a plain object on first update.

JSON.parse(JSON.stringify(community)) strips the non-enumerable community back-reference from Pages. After this.posts is overwritten, the reassigned getPage() runs without that context, so loading another page after the first update can fail.

🛠️ Proposed fix
   async simulateGetCommunityOnFirstUpdateEvent() {
     this._getCommunityOnFirstUpdate = false;

     // `@ts-ignore`
     const community = await new Plebbit().getCommunity({ address: this.address });
     const props = JSON.parse(JSON.stringify(community));
+    const posts = props.posts;
+    delete props.posts;
     for (const prop in props) {
       if (prop.startsWith("_")) {
         continue;
       }
       // `@ts-ignore`
       this[prop] = props[prop];
     }
-    this.posts.getPage = community.posts.getPage;
+    this.posts.pages = posts.pages;
+    this.posts.pageCids = posts.pageCids;
     this.updatingState = "succeeded";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1197 - 1211, The
code currently deep-clones `community` and copies its properties onto `this`,
which strips the non-enumerable Pages back-reference and ends up replacing
`this.posts` with a plain object; instead, when implementing
simulateGetCommunityOnFirstUpdateEvent() skip copying the `posts` property (do
not overwrite `this.posts`) and then assign the updater function from the real
Pages object while preserving context, e.g., set `this.posts.getPage =
community.posts.getPage.bind(this.posts)` (or otherwise reuse the original Pages
instance) so the Pages back-reference remains intact and subsequent getPage
calls work correctly.
src/lib/plebbit-js/plebbit-js-mock-content.ts-1153-1163 (1)

1153-1163: ⚠️ Potential issue | 🟠 Major

Keep createdCommunities in sync when edit() renames the address.

After this method updates this.address, the backing map still stays under the old key. plebbit.communities and delete() will keep using stale data, and the truthy guard also blocks valid edits like false or "".

🛠️ Proposed fix
   async edit(editCommunityOptions: any) {
     assert(
       editCommunityOptions && typeof editCommunityOptions === "object",
       `invalid editCommunityOptions '${editCommunityOptions}'`,
     );
+    const previousAddress = this.address;
     for (const prop in editCommunityOptions) {
-      if (editCommunityOptions[prop]) {
+      if (editCommunityOptions[prop] !== undefined) {
         // `@ts-ignore`
         this[prop] = editCommunityOptions[prop];
       }
     }
+    if (previousAddress && this.address && previousAddress !== this.address) {
+      createdCommunities[this.address] = this;
+      delete createdCommunities[previousAddress];
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1153 - 1163, The
edit() method currently skips falsy values and doesn't update the
createdCommunities backing map when this.address is changed; modify
edit(editCommunityOptions) to iterate only over own keys (e.g.,
Object.keys/editCommunityOptions) so empty string or false are allowed, capture
the previousAddress before assigning this.address, apply property updates to
this, and if previousAddress !== this.address update the
plebbit.createdCommunities (or createdCommunities map) by removing the old key
and inserting the new key pointing to this instance so plebbit.communities and
delete() see the renamed entry.
.codex/hooks/yarn-install.sh-19-25 (1)

19-25: ⚠️ Potential issue | 🟠 Major

Propagate yarn install failures instead of always succeeding.

The hook returns exit code 0 regardless of whether cd or yarn install fails. Line 19 uses || exit 0 to swallow directory change failures, and line 22 has no error handling for yarn install. This allows dependency resolution to break silently while the workflow reports success.

🐛 Proposed fix
 if [ "$file_path" = "package.json" ]; then
   # Change to project directory
-  cd "$(dirname "$0")/../.." || exit 0
+  cd "$(dirname "$0")/../.." || exit 1
   
   echo "package.json changed - running yarn install to update yarn.lock..."
-  yarn install
+  yarn install || exit 1
 fi
 
 exit 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/hooks/yarn-install.sh around lines 19 - 25, The hook currently
swallows failures: the cd command uses "|| exit 0" and the script always ends
with "exit 0", and "yarn install" has no error handling; modify
.codex/hooks/yarn-install.sh to propagate errors by removing the "|| exit 0" on
the cd invocation, checking the exit status of "yarn install" (or use "set -e" /
"set -o errexit" at the top) and exit non‑zero on failure so that failures in cd
or yarn install cause the hook to fail rather than always returning 0.
.codex/hooks/verify.sh-33-45 (1)

33-45: ⚠️ Potential issue | 🟠 Major

Don't mask build/test failures in the verify hook.

This script always reports success because both commands are wrapped with || true and the file ends with exit 0. That means callers cannot distinguish a clean verification run from a broken one, which defeats the purpose of a verify hook.

Proposed fix
 echo "Running build and tests..."
 echo ""
 
+status=0
+
 # Run build (catches compilation errors)
 echo "=== yarn build ==="
-yarn build 2>&1 || true
+yarn build 2>&1 || status=1
 echo ""
 
 # Run tests
 echo "=== yarn test ==="
-yarn test 2>&1 || true
+yarn test 2>&1 || status=1
 echo ""
 
 restore_dist_worktree
 
-echo "Verification complete."
-exit 0
+if [ "$status" -ne 0 ]; then
+  echo "Verification failed."
+else
+  echo "Verification complete."
+fi
+exit "$status"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/hooks/verify.sh around lines 33 - 45, The verify hook masks failures
by appending "|| true" to both "yarn build" and "yarn test" and always ending
with "exit 0"; remove the "|| true" from those commands and instead capture
their exit statuses (e.g., store $? after "yarn build" and "yarn test") so you
can run restore_dist_worktree unconditionally and then exit with a non-zero code
if either step failed; specifically update the commands in verify.sh (the "yarn
build" and "yarn test" invocations and the final "exit 0") so failures propagate
to the script caller while still ensuring restore_dist_worktree always runs.
src/hooks/communities.ts-304-323 (1)

304-323: ⚠️ Potential issue | 🟠 Major

Ignore stale address-resolution completions.

This lookup has no request/version guard. If communityAddress changes while an earlier resolveCommunityAddress() call is still in flight, the older promise can overwrite resolvedAddress and state for the new input.

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

In `@src/hooks/communities.ts` around lines 304 - 323, The async resolver closure
started for resolveCommunityAddress lacks a freshness guard and can overwrite
state when communityAddress changes; fix by capturing a local token (e.g. const
requested = communityAddress or a generated requestId) before awaiting
resolveCommunityAddress(communityAddress, chainProviders) and, after the await
or in the catch, only call setState, setResolvedAddress, and setErrors if the
current communityAddress (or requestId) still matches the captured token; also
avoid pushing stale errors by checking the same guard before setErrors and
before log.error so only the latest request updates state or logs.
src/hooks/communities.ts-101-117 (1)

101-117: ⚠️ Potential issue | 🟠 Major

Don't swallow stats fetch failures.

If fetchCid or JSON.parse fails, this hook only logs and keeps returning state: "fetching-ipfs" with empty error/errors. Callers can't stop loading or render the failure.

Also applies to: 129-137

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

In `@src/hooks/communities.ts` around lines 101 - 117, The hook currently swallows
failures from account.plebbit.fetchCid and JSON.parse and only logs them,
leaving callers stuck in "fetching-ipfs"; update the catch block inside the IIFE
to call setCommunityStats with an error state/value (include the
communityAddress and the caught error) so state reflects failure (e.g.
setCommunityStats(communityAddress, { state: "error", error })) and ensure any
partial fetchedCid is included in the error payload; do the same for the other
similar block at lines ~129-137 so callers can stop loading and render an error.
src/hooks/accounts/accounts.ts-145-145 (1)

145-145: ⚠️ Potential issue | 🟠 Major

Don't merge owner communities from the active account here.

useAccountCommunities({ accountName }) still calls useListCommunities() with no account override, so this merge uses whichever account is active instead of the requested account.

Also applies to: 226-234

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

In `@src/hooks/accounts/accounts.ts` at line 145, The code is calling
useListCommunities() without forwarding the requested account, causing
ownerCommunityAddresses to be populated from the active account instead of the
accountName parameter; update the calls inside useAccountCommunities (the
ownerCommunityAddresses assignment and the other occurrences around the merge
logic) to call useListCommunities({ accountName }) (or the hook's account
override option) so the list is fetched for the requested account rather than
the active account.
src/hooks/communities.ts-86-89 (1)

86-89: ⚠️ Potential issue | 🟠 Major

Pass accountName through to useCommunity.

useCommunityStats reads accountName, but it loads the community through the active account. With a non-active account, statsCid can come from the wrong context or never load at all.

Suggested fix
-  const community = useCommunity({ communityAddress, onlyIfCached });
+  const community = useCommunity({ communityAddress, accountName, onlyIfCached });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/communities.ts` around lines 86 - 89, useCommunity is currently
called without the accountName so community may be loaded from the active
account instead of the requested account; update the call to useCommunity in
this file to pass accountName from options (e.g., call useCommunity with {
communityAddress, onlyIfCached, accountName }) so communityStatsCid comes from
the correct account context used by useCommunityStats.
src/lib/utils/utils.ts-381-385 (1)

381-385: ⚠️ Potential issue | 🟠 Major

Check the commentIsValid() results here.

commentIsValid() resolves false for invalid replies; it doesn't reject. Promise.all() will still fulfill, so repliesAreValid() currently returns true even when a nested reply failed validation.

Suggested fix
-    const promises = replies.map((reply) =>
-      commentIsValid(reply, { validateReplies: false, blockCommunity: true }, plebbit),
-    );
-    await Promise.all(promises);
+    const results = await Promise.all(
+      replies.map((reply) =>
+        commentIsValid(reply, { validateReplies: false, blockCommunity: true }, plebbit),
+      ),
+    );
+    if (results.some((isValid) => !isValid)) {
+      if (blockCommunity) {
+        communitiesWithInvalidComments[comment.communityAddress] = true;
+      }
+      return false;
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/utils/utils.ts` around lines 381 - 385, The repliesAreValid flow
currently calls commentIsValid for each reply and awaits Promise.all(promises)
but ignores returned booleans, so invalid replies (false) don't mark the check
as failed; change the logic in repliesAreValid to await Promise.all(promises)
into a results array, then check results.includes(false) (or use
results.every(Boolean)) and return/throw accordingly so that any false from
commentIsValid causes repliesAreValid to fail; reference the call to
commentIsValid and the Promise.all(promises) usage to locate where to collect
and evaluate results.
src/stores/communities-pages/communities-pages-store.ts-98-102 (1)

98-102: ⚠️ Potential issue | 🟠 Major

Don't log the full account object here.

These payloads include account, and this codebase already treats signer/author fields as sensitive elsewhere because they can expose private key material. Sending the raw account into debug logs will leak secrets whenever tracing is enabled.

Also applies to: 118-122, 153-159

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

In `@src/stores/communities-pages/communities-pages-store.ts` around lines 98 -
102, The trace logs in communitiesPagesStore.addNextCommunityPageToStore (and
the other similar trace calls at the indicated locations) are currently
including the full account object which may contain sensitive signer/private-key
data; update those log.trace calls to stop passing the entire account object and
instead pass only non-sensitive identifiers (e.g., account.address or a masked
ID) or remove the account field entirely so no raw account object is logged.
Ensure you change each occurrence referenced (the call shown and the ones around
lines 118-122 and 153-159) to use the safe identifier symbol rather than the
full account payload.
src/stores/communities-pages/communities-pages-store.ts-286-321 (1)

286-321: ⚠️ Potential issue | 🟠 Major

Scope the cached community instances by account, not just address.

fetchPageCommunities[communityAddress] reuses the first account.plebbit.createCommunity() result for every later account. That mixes per-account client config/permissions for the same community, and the stale instance even survives resetCommunitiesPagesStore(). Key this cache by account identity (or plebbit instance) and clear it during reset.

Also applies to: 386-399

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

In `@src/stores/communities-pages/communities-pages-store.ts` around lines 286 -
321, fetchPageCommunities is currently keyed only by communityAddress so the
first account’s plebbit.createCommunity() instance is reused across different
accounts; change the cache to be scoped by account (for example use a composite
key like `${account.id}:${communityAddress}` or key by the account.plebbit
instance) and update fetchPage to look up/create using that account-scoped key
(affecting fetchPageCommunities and any uses in fetchPage). Also ensure the
cache is cleared in resetCommunitiesPagesStore (and the analogous cache usage at
the other location noted) so per-account instances are removed on reset.
src/stores/communities-pages/communities-pages-store.ts-264-309 (1)

264-309: ⚠️ Potential issue | 🟠 Major

Don't write mod-queue client state into community.posts.

fetchPage() is called for both "posts" and "modQueue", but this callback always mutates community.posts.clients. Any mod-queue fetch will overwrite post-feed client state and the moderation path never gets its own client tree.

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

In `@src/stores/communities-pages/communities-pages-store.ts` around lines 264 -
309, The callback onCommunityPostsClientsStateChange always writes into
community.posts.clients, so when fetchPage handles pageType "modQueue" it
accidentally overwrites posts client state; create a separate handler (e.g.,
onCommunityModQueueClientsStateChange) that mirrors
onCommunityPostsClientsStateChange but updates community.modQueue.clients (and
community.modQueue[...] trees) and, inside fetchPage where
utils.pageClientsOnStateChange is called, pass the appropriate handler based on
pageType (use onCommunityPostsClientsStateChange for "posts" and the new
onCommunityModQueueClientsStateChange for "modQueue") so each pageType keeps its
own client tree.
src/stores/accounts/accounts-actions.ts-428-433 (1)

428-433: ⚠️ Potential issue | 🟠 Major

Serialize the account write behind subscribe/unsubscribe.

Both actions update state immediately and fire accountsDatabase.addAccount(updatedAccount) without awaiting it. Two quick toggles can finish out of order and leave the persisted subscriptions list wrong after reload.

Also applies to: 463-468

🤖 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 428 - 433, The
subscribe/unsubscribe flows update state immediately but call
accountsDatabase.addAccount(updatedAccount) without awaiting, allowing
concurrent toggles to persist out-of-order; serialize writes for a given account
by chaining or awaiting previous write promises before calling
accountsDatabase.addAccount. Implement a per-account write-queue (e.g., a map
keyed by updatedAccount.id that stores the last write Promise) and when
subscribing/unsubscribing (the code that constructs updatedAccount and calls
accountsDatabase.addAccount), replace the fire-and-forget with: chain the new
addAccount call onto the existing promise for that account (prevPromise.then(()
=> accountsDatabase.addAccount(updatedAccount))).catch(log) and then replace the
map entry with the new promise; keep immediate UI state update via
accountsStore.setState but ensure persistence calls for the same account are
executed in order (apply same fix to both the block around
updatedAccount/accountsDatabase.addAccount and the similar block at lines
463-468).
src/stores/accounts/accounts-actions.ts-1187-1189 (1)

1187-1189: ⚠️ Potential issue | 🟠 Major

Guard account.plebbit.communities before calling .includes().

useListCommunities() in src/hooks/communities.ts already treats plebbit.communities as optional, so this direct .includes() can throw when the local communities list has not been initialized yet.

🩹 Minimal guard
-const localCommunityAddresses = account.plebbit.communities;
+const localCommunityAddresses = Array.isArray(account.plebbit.communities)
+  ? account.plebbit.communities
+  : [];
🤖 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 1187 - 1189, Guard
against a missing or non-array plebbit.communities before calling .includes():
ensure the code that sets localCommunityAddresses (or the subsequent check)
verifies account.plebbit?.communities is an array (e.g. Array.isArray) and only
calls .includes(communityAddress) when that check passes; otherwise treat it as
an empty list so the "account is owner" branch is skipped safely. Target the
variables/members localCommunityAddresses, account.plebbit.communities and the
conditional that uses .includes(communityAddress) in accounts-actions.ts.
src/hooks/accounts/accounts.test.ts-2501-2506 (1)

2501-2506: ⚠️ Potential issue | 🟠 Major

Remove the unstable title === undefined assertion from this delete test.

This is checking the same transient state that the previous test already calls out as unreliable in CI. Since Community.update() can populate title shortly after creation, this makes the delete-path test flaky even when deletion is correct.

Proposed fix
       // can useCommunity
       rendered.rerender(createdCommunityAddress);
       await waitFor(() => rendered.result.current.community);
       expect(rendered.result.current.community.address).toBe(createdCommunityAddress);
-      expect(rendered.result.current.community.title).toBe(undefined);

       // delete it
       await act(async () => {
         await rendered.result.current.deleteCommunity(createdCommunityAddress);
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/accounts/accounts.test.ts` around lines 2501 - 2506, The test is
making a flaky assertion on transient community.title; remove the unstable
check. In the test that calls rendered.rerender(createdCommunityAddress) and
waits for rendered.result.current.community, delete the line
expect(rendered.result.current.community.title).toBe(undefined); (or replace it
with a stable assertion such as asserting the property exists or leaving it
unasserted). Keep the address assertion
(expect(...community.address).toBe(createdCommunityAddress)) and do not add
timing-dependent checks tied to Community.update().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 41b01c07-25b4-4ad2-b910-4bbdae6dc31c

📥 Commits

Reviewing files that changed from the base of the PR and between 0f8c906 and 3d43300.

📒 Files selected for processing (148)
  • .codex/agents/code-quality.md
  • .codex/agents/plan-implementer.md
  • .codex/agents/react-patterns-enforcer.md
  • .codex/hooks.json
  • .codex/hooks/format.sh
  • .codex/hooks/sync-git-branches.sh
  • .codex/hooks/verify.sh
  • .codex/hooks/yarn-install.sh
  • .codex/skills/commit-format/SKILL.md
  • .codex/skills/commit/SKILL.md
  • .codex/skills/context7/SKILL.md
  • .codex/skills/deslop/SKILL.md
  • .codex/skills/find-skills/SKILL.md
  • .codex/skills/fix-merge-conflicts/SKILL.md
  • .codex/skills/implement-plan/SKILL.md
  • .codex/skills/issue-format/SKILL.md
  • .codex/skills/make-closed-issue/SKILL.md
  • .codex/skills/readme/SKILL.md
  • .codex/skills/refactor-pass/SKILL.md
  • .codex/skills/release-description/SKILL.md
  • .codex/skills/review-and-merge-pr/SKILL.md
  • .codex/skills/you-might-not-need-an-effect/SKILL.md
  • .cursor/agents/code-quality.md
  • .cursor/agents/plan-implementer.md
  • .cursor/agents/react-patterns-enforcer.md
  • .cursor/hooks.json
  • .cursor/hooks/format.sh
  • .cursor/hooks/sync-git-branches.sh
  • .cursor/hooks/verify.sh
  • .cursor/hooks/yarn-install.sh
  • .cursor/skills/commit-format/SKILL.md
  • .cursor/skills/commit/SKILL.md
  • .cursor/skills/context7/SKILL.md
  • .cursor/skills/deslop/SKILL.md
  • .cursor/skills/find-skills/SKILL.md
  • .cursor/skills/fix-merge-conflicts/SKILL.md
  • .cursor/skills/implement-plan/SKILL.md
  • .cursor/skills/issue-format/SKILL.md
  • .cursor/skills/make-closed-issue/SKILL.md
  • .cursor/skills/readme/SKILL.md
  • .cursor/skills/refactor-pass/SKILL.md
  • .cursor/skills/release-description/SKILL.md
  • .cursor/skills/review-and-merge-pr/SKILL.md
  • .cursor/skills/you-might-not-need-an-effect/SKILL.md
  • .gitignore
  • AGENTS.md
  • README.md
  • docs/TODO.md
  • docs/algorithms.md
  • docs/clients.md
  • docs/mock-content.md
  • docs/schema.md
  • docs/testing.md
  • scripts/coverage-gaps.mjs
  • src/hooks/accounts/accounts.test.ts
  • src/hooks/accounts/accounts.ts
  • src/hooks/accounts/index.ts
  • src/hooks/accounts/utils.test.ts
  • src/hooks/accounts/utils.ts
  • src/hooks/actions/actions.test.ts
  • src/hooks/actions/actions.ts
  • src/hooks/actions/index.ts
  • src/hooks/authors/author-avatars.ts
  • src/hooks/authors/authors.test.ts
  • src/hooks/authors/authors.ts
  • src/hooks/authors/index.ts
  • src/hooks/authors/utils.ts
  • src/hooks/comments.test.ts
  • src/hooks/comments.ts
  • src/hooks/communities.test.ts
  • src/hooks/communities.ts
  • src/hooks/feeds/feeds.test.ts
  • src/hooks/feeds/feeds.ts
  • src/hooks/feeds/index.ts
  • src/hooks/replies.test.ts
  • src/hooks/states.test.ts
  • src/hooks/states.ts
  • src/hooks/subplebbits.ts
  • src/index.ts
  • src/lib/chain/chain.test.ts
  • src/lib/chain/index.ts
  • src/lib/community-address.test.ts
  • src/lib/community-address.ts
  • src/lib/debug-utils.ts
  • src/lib/localforage-lru/localforage-lru.test.ts
  • src/lib/localforage-lru/localforage-lru.ts
  • src/lib/plebbit-js/fixtures/markdown-example.ts
  • src/lib/plebbit-js/plebbit-js-mock-content.donttest.ts
  • src/lib/plebbit-js/plebbit-js-mock-content.ts
  • src/lib/plebbit-js/plebbit-js-mock.test.ts
  • src/lib/plebbit-js/plebbit-js-mock.ts
  • src/lib/subplebbit-address.test.ts
  • src/lib/test-utils.ts
  • src/lib/utils/index.ts
  • src/lib/utils/utils.test.ts
  • src/lib/utils/utils.ts
  • src/lib/validator.ts
  • src/stores/accounts/account-generator.ts
  • src/stores/accounts/accounts-actions-internal.test.ts
  • src/stores/accounts/accounts-actions-internal.ts
  • src/stores/accounts/accounts-actions.test.ts
  • src/stores/accounts/accounts-actions.ts
  • src/stores/accounts/accounts-database.test.ts
  • src/stores/accounts/accounts-database.ts
  • src/stores/accounts/accounts-store.test.ts
  • src/stores/accounts/index.ts
  • src/stores/accounts/utils.test.ts
  • src/stores/accounts/utils.ts
  • src/stores/authors-comments/authors-comments-store.test.ts
  • src/stores/authors-comments/authors-comments-store.ts
  • src/stores/authors-comments/index.ts
  • src/stores/comments/index.ts
  • src/stores/communities-pages/communities-pages-store.test.ts
  • src/stores/communities-pages/communities-pages-store.ts
  • src/stores/communities-pages/index.ts
  • src/stores/communities/communities-store.test.ts
  • src/stores/communities/communities-store.ts
  • src/stores/communities/index.ts
  • src/stores/feeds/feed-sorter.test.ts
  • src/stores/feeds/feeds-store.test.ts
  • src/stores/feeds/feeds-store.ts
  • src/stores/feeds/index.ts
  • src/stores/feeds/utils.test.ts
  • src/stores/feeds/utils.ts
  • src/stores/replies-pages/index.ts
  • src/stores/replies-pages/replies-pages-store.test.ts
  • src/stores/replies-pages/replies-pages-store.ts
  • src/stores/replies-pages/utils.test.ts
  • src/stores/replies/index.ts
  • src/stores/replies/replies-store.test.ts
  • src/stores/replies/replies-store.ts
  • src/stores/replies/utils.test.ts
  • src/stores/replies/utils.ts
  • src/stores/subplebbits-pages/index.ts
  • src/stores/subplebbits-pages/subplebbits-pages-store.test.ts
  • src/stores/subplebbits-pages/subplebbits-pages-store.ts
  • src/stores/subplebbits/index.ts
  • src/stores/subplebbits/subplebbits-store.test.ts
  • src/stores/subplebbits/subplebbits-store.ts
  • src/types.ts
  • test/browser-e2e/accounts.test.js
  • test/browser-e2e/communities.test.js
  • test/browser-e2e/feeds.test.js
  • test/browser-plebbit-js-mock-content/plebbit-js-mock-content.test.js
  • test/browser-plebbit-js-mock/accounts.test.js
  • test/browser-plebbit-js-mock/communities.test.js
  • test/browser-plebbit-js-mock/feeds.test.js
  • test/test-server/index.js
💤 Files with no reviewable changes (2)
  • src/lib/subplebbit-address.test.ts
  • src/hooks/subplebbits.ts

Comment on lines +1 to +67
---
name: context7
description: Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 API. This skill should be used when looking up documentation for any programming library or framework, finding code examples for specific APIs or features, verifying correct usage of library functions, or obtaining current information about library APIs that may have changed since training.
---

# Context7

## Overview

This skill enables retrieval of current documentation for software libraries and components by querying the Context7 API via curl. Use it instead of relying on potentially outdated training data.

## Workflow

### Step 1: Search for the Library

To find the Context7 library ID, query the search endpoint:

```bash
curl -s "https://context7.com/api/v2/libs/search?libraryName=LIBRARY_NAME&query=TOPIC" | jq '.results[0]'
```

**Parameters:**
- `libraryName` (required): The library name to search for (e.g., "react", "zustand", "vitest", "ethers")
- `query` (required): A description of the topic for relevance ranking

**Response fields:**
- `id`: Library identifier for the context endpoint
- `title`: Human-readable library name
- `description`: Brief description of the library
- `totalSnippets`: Number of documentation snippets available

### Step 2: Fetch Documentation

To retrieve documentation, use the library ID from step 1:

```bash
curl -s "https://context7.com/api/v2/context?libraryId=LIBRARY_ID&query=TOPIC&type=txt"
```

**Parameters:**
- `libraryId` (required): The library ID from search results
- `query` (required): The specific topic to retrieve documentation for
- `type` (optional): Response format - `json` (default) or `txt` (plain text, more readable)

## Examples

### Zustand store documentation

```bash
curl -s "https://context7.com/api/v2/libs/search?libraryName=zustand&query=store" | jq '.results[0].id'
curl -s "https://context7.com/api/v2/context?libraryId=/pmndrs/zustand&query=create+store&type=txt"
```

### Vitest testing

```bash
curl -s "https://context7.com/api/v2/libs/search?libraryName=vitest&query=testing" | jq '.results[0].id'
curl -s "https://context7.com/api/v2/context?libraryId=/vitest-dev/vitest&query=mock+functions&type=txt"
```

## Tips

- Use `type=txt` for more readable output
- Use `jq` to filter and format JSON responses
- Be specific with the `query` parameter to improve relevance ranking
- URL-encode query parameters containing spaces (use `+` or `%20`)
- No API key is required for basic usage (rate-limited)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

What are the current Context7 API endpoints and how do they work?

💡 Result:

Context7 REST API (documentation lookup)

Base URL: https://context7.com (endpoints below are under /api/v2) [1]

1) Search libraries

Endpoint: GET /api/v2/libs/search [1]
Auth: Authorization: Bearer <CONTEXT7_API_KEY> [1]
Query params:

  • libraryName (required) — e.g. react, nextjs [1]
  • query (required) — what you’re trying to do; used for relevance ranking [1]

What it returns: an array of matching libraries with an id like /facebook/react plus metadata (scores, versions, snippet counts). [1]

2) Fetch documentation “context” (snippets) for a library

Endpoint: GET /api/v2/context [1]
Auth: Authorization: Bearer <CONTEXT7_API_KEY> [1]
Query params:

  • libraryId (required) — from search results (e.g. /facebook/react) [1]
  • query (required) — what you want to know/do [1]
  • type (optional) — json (default) or txt (plain text ready to paste into an LLM prompt) [1]

What it returns:

  • json: array of snippets with title, content, source [1]
  • txt: a single plain-text blob suitable for prompting [1]

Version pinning: you can request version-specific context by using a versioned libraryId (example shown in docs: /vercel/next.js/v15.1.8). [1]

Typical workflow: call Search → pick best libraryId → call Context with your question. [1]


Context7 hosted MCP server (for AI tools that speak MCP)

MCP endpoint (API key header approach): https://mcp.context7.com/mcp (used by MCP clients like Cursor/Claude Code). [2]
Clients commonly pass the key as a header named CONTEXT7_API_KEY. [2]

OAuth-enabled MCP endpoint: https://mcp.context7.com/mcp/oauth [3]
How it works at a high level: connect to the OAuth endpoint → browser sign-in → redirect back → client handles token refresh. [3]


Sources

[1] Context7 “API Guide” (REST endpoints, params, formats, errors) (context7.com)
[2] Context7 “Installation” (hosted MCP server URL + headers) (context7.com)
[3] Context7 “OAuth” (OAuth MCP endpoint and flow) (context7.com)

Citations:


🌐 Web query:

Context7 API library ID format and examples

💡 Result:

Context7 “library ID” format

  • Base format: /<owner>/<repo> (a leading /, then GitHub-style owner/repo). Example: /facebook/react. [1]
  • Pinned version format (optional): /<owner>/<repo>/<version> where version is typically a tag like v15.1.8. Example: /vercel/next.js/v15.1.8. [1]

How to find the correct library ID

  • Call Search Library: GET /api/v2/libs/search and read the id field from the results (that id is the library ID you pass to the context endpoint). Example result shows id: "/facebook/react". [1]

How it’s used (examples)

  • Fetch docs context (default version):
    • GET /api/v2/context?libraryId=/facebook/react&query=hooks [1]
  • Fetch docs context (pinned version):
    • GET /api/v2/context?libraryId=/vercel/next.js/v15.1.8&query=app%20router [1]

Fix API authentication requirement in the Tips section.

The documentation incorrectly states "No API key is required for basic usage (rate-limited)" on line 65. According to the official Context7 API documentation, both the search and context endpoints require authentication via Authorization: Bearer <CONTEXT7_API_KEY>. Update the Tips section to clarify that an API key is required for all API calls, and optionally note any current rate limits for authenticated requests.

The API endpoints (/api/v2/libs/search and /api/v2/context), parameter names, and example library ID formats are all accurate.

🧰 Tools
🪛 LanguageTool

[grammar] ~6-~6: Ensure spelling is correct
Context: ...may have changed since training. --- # Context7 ## Overview This skill enables retrieval o...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

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

In @.cursor/skills/context7/SKILL.md around lines 1 - 67, Update the Tips
section to remove the incorrect statement "No API key is required for basic
usage (rate-limited)" and instead state that both Context7 endpoints
(/api/v2/libs/search and /api/v2/context) require authentication via the
Authorization: Bearer <CONTEXT7_API_KEY> header; optionally add a brief note
about rate limits for authenticated requests and keep the examples, parameter
names, and library ID formats unchanged (edit the Tips paragraph that currently
mentions API key requirements).

Comment on lines 13 to 18
const deleteCaches = () =>
Promise.all([
localForageLru.createInstance({ name: "plebbitReactHooks-subplebbits" }).clear(),
localForageLru.createInstance({ name: "plebbitReactHooks-communities" }).clear(),
localForageLru.createInstance({ name: "plebbitReactHooks-comments" }).clear(),
localForageLru.createInstance({ name: "plebbitReactHooks-subplebbitsPages" }).clear(),
localForageLru.createInstance({ name: "plebbitReactHooks-communitiesPages" }).clear(),
]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if dist files exist and whether they contain stale store names.
# Expected: If dist is tracked, it should contain "communities" not "subplebbits" after rebuild.

echo "=== Checking if dist/ is tracked in git ==="
git ls-files --error-unmatch dist/lib/debug-utils.js 2>/dev/null && echo "dist/lib/debug-utils.js IS tracked" || echo "dist/lib/debug-utils.js is NOT tracked"

echo ""
echo "=== Checking .gitignore for dist/ ==="
rg -n "^dist" .gitignore || echo "No dist entry in .gitignore"

echo ""
echo "=== Current content of dist/lib/debug-utils.js (if exists) ==="
if [ -f dist/lib/debug-utils.js ]; then
  cat dist/lib/debug-utils.js
else
  echo "File does not exist"
fi

Repository: bitsocialnet/bitsocial-react-hooks

Length of output: 1305


Rebuild dist files before merging—dist file contains stale store names.

The compiled dist/lib/debug-utils.js references the old store names (plebbitReactHooks-subplebbits, plebbitReactHooks-subplebbitsPages), while the source has been updated to plebbitReactHooks-communities and plebbitReactHooks-communitiesPages. This mismatch will cause runtime errors in the published package when it attempts to clear non-existent stores.

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

In `@src/lib/debug-utils.ts` around lines 13 - 18, The compiled dist output is
stale: update the build so dist/lib/debug-utils.js matches the source change in
deleteCaches (which now uses localForageLru.createInstance with names
"plebbitReactHooks-communities" and "plebbitReactHooks-communitiesPages");
rebuild the project (run the repository's build script) to regenerate dist
artifacts, verify dist/lib/debug-utils.js contains the new store names instead
of the old "plebbitReactHooks-subplebbits" and
"plebbitReactHooks-subplebbitsPages", and include the regenerated dist files in
the PR before merging.

@tomcasaburi
Copy link
Member Author

Addressed the valid CI/review findings in commit 682c0fe. Fixed the stop-hook cleanup logic so it only restores/cleans dist/ when verification started from a clean baseline, and restored external test-server compatibility with the still-legacy upstream plebbit API. Local verification after the patch: yarn build ✅, yarn test ✅, yarn test:e2e:mock-content ✅. Remaining blocker: the repo-mandated coverage gate still fails on this branch (yarn test:coverage:hooks-stores). I did not apply the Context7 auth suggestion because the live Context7 endpoints currently return 200 without an API key, and I did not apply the stale-dist suggestion because this repo explicitly avoids committing rebuilt dist/ output.

@tomcasaburi tomcasaburi force-pushed the chore/agent-workflow-tracking branch from 682c0fe to aa5cb91 Compare March 11, 2026 11:45
@tomcasaburi
Copy link
Member Author

Restacked this PR on top of master in aa5cb91 so it only contains the AI workflow and contributor-tooling changes that are actually unique to PR 26.

Kept in this PR:

  • the .codex and .cursor agents, hooks, and skills
  • the AGENTS.md and .gitignore updates
  • the verify.sh dist-preservation fix in both hook directories

Dropped from this PR:

  • the temporary test/test-server compatibility change, because that only existed to compensate for the stacked community-refactor branch and is handled separately

Local verification:

  • sh -n .codex/hooks/verify.sh
  • sh -n .cursor/hooks/verify.sh
  • diff -u .codex/hooks/verify.sh .cursor/hooks/verify.sh

# Only format JS/TS files
case "$file_path" in
*.js|*.ts|*.tsx|*.mjs|*.cjs)
npx prettier --config config/prettier.config.js --write "$file_path" 2>/dev/null || true
Copy link

Choose a reason for hiding this comment

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

Format hook invokes wrong formatter tool

Medium Severity

The afterFileEdit hook calls npx prettier --config config/prettier.config.js but the project's actual formatter is oxfmt (the yarn prettier script in package.json maps to oxfmt, not prettier). There's no evidence prettier is a project dependency or that config/prettier.config.js exists. This hook either silently no-ops (due to || true) or applies formatting rules inconsistent with the rest of the codebase, defeating its purpose.

Additional Locations (1)
Fix in Cursor Fix in Web

;;
esac

exit 0
Copy link

Choose a reason for hiding this comment

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

Massive file duplication across .codex and .cursor

Low Severity

Every file under .codex/ (agents, hooks, skills, hooks.json) is duplicated byte-for-byte under .cursor/. This is 14+ skill files, 3 agent files, 4 shell scripts, and 2 JSON configs — all maintained in two places. The AGENTS.md rule to "keep equivalent workflow files aligned" will be very hard to enforce manually and increases the risk of the copies silently drifting apart. Symlinks or a shared directory with toolchain-specific entry points would eliminate the duplication.

Additional Locations (2)
Fix in Cursor Fix in Web

@tomcasaburi tomcasaburi force-pushed the chore/agent-workflow-tracking branch from aa5cb91 to 9a7a16d Compare March 11, 2026 11:48
@tomcasaburi tomcasaburi merged commit 614b909 into master Mar 11, 2026
1 check passed
@tomcasaburi tomcasaburi deleted the chore/agent-workflow-tracking branch March 11, 2026 11:48
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

fi

return 0
}
Copy link

Choose a reason for hiding this comment

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

git cherry failure silently marks branch as integrated

Low Severity

branch_is_integrated falls through to return 0 (meaning "integrated") when git cherry fails and produces empty output via || true. An empty cherry_output causes the grep '^+' check to find no match, so the function reports the branch as fully integrated. Combined with the deletion logic downstream, this could cause a temporary branch to be incorrectly deleted when the default branch ref is missing or git cherry otherwise errors out.

Additional Locations (1)
Fix in Cursor Fix in Web

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.

AI workflow files are local-only and skip PR review

1 participant