Skip to content

docs: add AI-agentic monorepo starter kit#2233

Open
webguru-hypha wants to merge 2 commits into
mainfrom
docs/agentic-monorepo-starter
Open

docs: add AI-agentic monorepo starter kit#2233
webguru-hypha wants to merge 2 commits into
mainfrom
docs/agentic-monorepo-starter

Conversation

@webguru-hypha

@webguru-hypha webguru-hypha commented May 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a self-contained docs/agentic-monorepo-starter template for teams to bootstrap a Vercel + GitHub AI-agentic monorepo
  • include starter app/package scaffolding, CI/deploy workflows, and governance templates
  • include direct Hypha role/router reference links so external teams can adopt the same role system patterns

Test plan

  • Verify only docs/agentic-monorepo-starter/** files are included in this PR
  • Confirm branch is based on main (isolated from unrelated work)
  • Copy starter into a separate repo and run pnpm install && pnpm dev
  • Configure Vercel + GitHub secrets in target repo and validate CI/deploy workflows

Made with Cursor

Summary by CodeRabbit

Release Notes

  • New Features

    • Added chat interface with AI-powered capabilities
    • Introduced agentic monorepo starter template with example web application
    • Expanded trusted form submission endpoints in security policies
  • Bug Fixes

    • Improved notification initialization to prevent duplicate attempts
  • Documentation

    • Added development guidelines, pull request template, and governance standards

Provide a copy-ready Vercel/GitHub AI-agentic monorepo starter with workflows, scaffold files, and Hypha role reference links so teams can bootstrap similar environments quickly.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR adds a security policy expansion for form submissions in the web middleware, creates a comprehensive agentic monorepo starter template with CI/CD workflows and example AI-powered chat application, and improves OneSignal initialization logic in the notifications subscriber to prevent concurrent initialization attempts.

Changes

Web App CSP Policy

Layer / File(s) Summary
Security Policy
apps/web/src/middleware.ts
Content-Security-Policy form-action expanded from 'self' only to include external auth endpoints: https://auth.privy.io, https://privy.hypha.earth, https://accounts.google.com, and https://*.google.com.

Agentic Monorepo Starter Template

Layer / File(s) Summary
Workspace Configuration
docs/agentic-monorepo-starter/pnpm-workspace.yaml, docs/agentic-monorepo-starter/turbo.json, docs/agentic-monorepo-starter/package.json
Root-level monorepo config with pnpm workspace and Turbo task definitions for dev, build, lint, typecheck, and test pipelines.
Build & Lint Tooling
docs/agentic-monorepo-starter/packages/tsconfig/..., docs/agentic-monorepo-starter/packages/eslint-config/..., docs/agentic-monorepo-starter/packages/config/...
Shared TypeScript, ESLint, and config packages providing base compiler options and Next.js linting rules for workspace apps.
AI Agent Tooling
docs/agentic-monorepo-starter/packages/agents/src/index.ts, docs/agentic-monorepo-starter/packages/agents/package.json
New agents package exporting a tools object with a getTime tool configured via the ai SDK for use in agent workflows.
Web Application
docs/agentic-monorepo-starter/apps/web/...
Next.js app with chat UI, API route handler integrated with AI SDK, global styling, and TypeScript/ESLint configuration. Uses useChat hook to stream agent responses.
CI/CD Workflows
docs/agentic-monorepo-starter/.github/workflows/ci.yml, docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml
GitHub Actions workflows for linting, type-checking, testing, and building on pull requests; separate deployment pipeline for Vercel with preview/production environments.
Documentation & Templates
docs/agentic-monorepo-starter/README.md, docs/agentic-monorepo-starter/AGENTS.md, docs/agentic-monorepo-starter/.github/pull_request_template.md, docs/agentic-monorepo-starter/.gitignore
Setup guide with quick-start instructions, agent governance guidelines, PR template for consistent contributions, and standard ignore patterns.

Notifications Subscriber Initialization Guard

Layer / File(s) Summary
State Management
packages/notifications/src/components/notifications-subscriber.tsx
New ref flag hasInitAttemptRef added to track in-flight initialization and prevent concurrent OneSignal init attempts.
Initialization Logic
packages/notifications/src/components/notifications-subscriber.tsx
Guard condition updated to skip init if already initialized or an attempt is in progress; requires valid person.slug and appId; resets flag on error to allow retry.
Effect Dependencies
packages/notifications/src/components/notifications-subscriber.tsx
Effect dependency array expanded to include appId, safariWebId, serviceWorkerPath, router, and person?.slug for proper re-run behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • hypha-dao/hypha-web#2097: Both PRs expand Content-Security-Policy in apps/web/src/middleware.ts to add trusted origins for Privy and related authentication domains.
  • hypha-dao/hypha-web#1230: Both PRs modify CSP policy in web middleware; the related PR refactors CSP source lists to environment variables while this PR broadens the form-action sources.
  • hypha-dao/hypha-web#1829: Both PRs update Content-Security-Policy by expanding allowed CSP sources in the web middleware.

Suggested labels

enhancement, documentation, devops

Suggested reviewers

  • plitzenberger
  • alexprate
  • sergey3bv
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title follows conventional commits format with 'docs' type and scope; it accurately describes the main change of adding an AI-agentic monorepo starter kit to the documentation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 docs/agentic-monorepo-starter

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.

Prevent anonymous sessions from initializing OneSignal until a user is available and relax CSP form-action for Privy/Google auth submissions. This keeps login usable while avoiding unauth startup errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

🔗 Custom preview URL: https://pr-2233.preview-app.hypha.earth

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 20

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/middleware.ts`:
- Line 77: Remove the overly permissive wildcard from the CSP form-action entry:
locate the header string "form-action 'self' https://auth.privy.io
https://privy.hypha.earth https://accounts.google.com https://*.google.com" in
apps/web/src/middleware.ts and delete the "https://*.google.com" token so only
the explicitly required origins (e.g., https://accounts.google.com) remain; if
any additional specific Google subdomain is truly needed for the auth flow, add
that exact origin instead of a wildcard.

In `@docs/agentic-monorepo-starter/.github/pull_request_template.md`:
- Around line 1-14: Add a top-level heading and ensure blank lines around each
section heading to satisfy MD041/MD022; specifically insert a single H1 title
line above the existing content (e.g., "# Pull Request") and add an empty line
before and after each "## Change", "## Reason", "## Validation", and "## Risk /
Rollback" heading so the template has proper top-level heading and spacing for
the Markdown linter.

In `@docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml`:
- Line 23: Replace the unpinned, npm-based install step that runs "npm i -g
vercel@latest" with a pinned pnpm install to avoid mixing package managers and
accidental breaking upgrades; specifically change the workflow step that
contains the run command "npm i -g vercel@latest" to use pnpm global install
with a fixed version (e.g., "pnpm add -g vercel@<major.minor.patch>") or use a
pinned CLI invocation pattern that matches the repo's pnpm usage.
- Around line 25-39: The workflow is missing VERCEL_ORG_ID and VERCEL_PROJECT_ID
and is pulling at repo root while building in apps/web, so update the GitHub
Actions steps to export/use the VERCEL_ORG_ID and VERCEL_PROJECT_ID secrets
alongside VERCEL_TOKEN and run vercel pull from the same cwd as the build;
specifically ensure the "Pull Vercel env" step passes VERCEL_ORG_ID and
VERCEL_PROJECT_ID (from secrets) into the environment and invoke vercel pull
with the same --cwd apps/web flag (or run both vercel pull and vercel build from
apps/web) so .vercel/project.json is created where vercel build --cwd apps/web
can find it.
- Around line 9-47: The deploy job currently runs independently and without a
timeout; update the deploy job (job id: deploy) to depend on the CI job by
adding a needs: ["ci"] (or the exact job id used in your CI workflow) so it only
runs after CI succeeds, and add timeout-minutes: 30 (or your desired limit) to
the deploy job to prevent runaway runs; ensure the change is applied at the job
level (next to runs-on) that contains the steps like "Pull Vercel env", "Build
web", and "Deploy web".

In `@docs/agentic-monorepo-starter/apps/web/.eslintrc.json`:
- Around line 1-3: The repo currently uses legacy .eslintrc.json which can
surprise users on eslint@^9.17.0; replace the legacy config by adding an
eslint.config.mjs that imports/exports the Next.js flat config (via
eslint-config-next's flat export) and any project rules, or alternatively
document/keep .eslintrc.json but set ES LINT_USE_FLAT_CONFIG=false; update
references to use eslint.config.mjs and ensure the flat-config export includes
the same extends/rules as the previous .eslintrc.json and any overrides from
"eslint-config-next".

In `@docs/agentic-monorepo-starter/apps/web/app/api/chat/route.ts`:
- Around line 4-16: The POST handler is currently unauthenticated and calls
streamText directly, exposing paid API usage; add an auth/session guard at the
top of the POST function (validate an API key or session token from req.headers
or use your session helper) and return a 401/403 before calling streamText if
the check fails. Specifically, in the POST function that currently calls
streamText (and uses convertToModelMessages and toUIMessageStreamResponse),
verify the caller (e.g., check Authorization header or getServerSession) and
optionally enforce rate-limiting/quota, then only call streamText when
authentication succeeds.
- Line 5: Wrap the await req.json() call and the destructuring const { messages
}: { messages: UIMessage[] } = await req.json() in a try/catch inside the POST
handler (or whichever request handler contains that line), catch SyntaxError and
other parsing errors, and return a structured 400 response (e.g.,
NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })); also validate
that messages exists and is an array of UIMessage (return 400 with a clear
message if not) before proceeding.
- Around line 7-13: The code uses model identifier "openai/gpt-5.4" in
streamText(...) which requires AI SDK v5.0.36+ but package.json pins "ai":
"^4.0.0"; fix by updating the "ai" dependency in package.json to at least
"^5.0.36" (or "^6.0.0"/latest stable) so the creator/model-name syntax is
supported, or alternatively change the streamText call to use a provider
instance compatible with v4 (e.g., use the `@ai-sdk/openai` provider like
openai('gpt-4') passed to streamText and avoid the "openai/gpt-5.4" string) and
ensure any related imports/configs are adjusted accordingly.

In `@docs/agentic-monorepo-starter/apps/web/app/globals.css`:
- Line 9: The font-family declaration uses an unquoted Segoe UI which triggers
Stylelint's font-family-name-quotes rule; update the font-family line (the
font-family declaration) to wrap Segoe UI in quotes (e.g., "Segoe UI") so the
rule passes, and run the linter to confirm no further font-family-name-quotes
violations remain.

In `@docs/agentic-monorepo-starter/apps/web/app/page.tsx`:
- Around line 35-40: The input currently uses only a placeholder which is not
accessible; add a programmatic label for the chat input by assigning an id to
the input (e.g., id="chat-input") and either render an associated <label
htmlFor="chat-input">Chat message</label> (visually hide it with your existing
"sr-only" class if you don't want it visible) or add an aria-label="Chat
message" to the same input; update the JSX where value={text} and
onChange={(e)=>setText(e.target.value)} to include that id/aria-label so screen
readers can announce the field.
- Line 22: The map over m.parts uses the unstable array index (m.parts.map((p,
i) => ... key={i})), which causes remounts during streaming; replace the index
key with a stable identifier such as p.id or a composite like `${m.id}-${p.id}`
(or, if parts currently lack an id, attach a stable id when parts are created)
so the JSX inside m.parts.map uses key={p.id} or key={`${m.id}-${p.id}`} instead
of key={i} to ensure proper React reconciliation.
- Around line 9-11: The DefaultChatTransport instance is being recreated every
render causing useChat to reinitialize; update the component to memoize the
transport passed to useChat (e.g., create a single DefaultChatTransport and
reuse it) so the transport object identity is stable; locate the useChat call
and the inline new DefaultChatTransport({...}) and replace that inline
construction with a memoized value (useMemo or module-level singleton) so
messages, sendMessage, and status aren't reset by transport re-instantiation.
- Around line 4-5: The project imports and uses APIs (DefaultChatTransport,
sendMessage, the parts-based message shape and status values
"submitted"/"streaming", plus route.ts symbols convertToModelMessages,
stepCountIs, UIMessage) that exist only in AI SDK 6.x and `@ai-sdk/react` 3.x, but
package.json pins "ai": "^4.0.0" and "@ai-sdk/react": "^1.0.0"; update
package.json to use the compatible versions (upgrade "ai" to a 6.x release and
"@ai-sdk/react" to a 3.x release) so those symbols resolve and
runtime/installation succeed, then run install and smoke-test
useChat/DefaultChatTransport flows to confirm compatibility.

In `@docs/agentic-monorepo-starter/apps/web/next.config.ts`:
- Line 3: The Next config currently defines nextConfig without telling Next to
transpile workspace packages, so imports from the workspace package `@repo/agents`
(used by app/api/chat/route.ts) will fail; update the nextConfig object to
include a transpilePackages array that contains '@repo/agents' (i.e., add
transpilePackages: ['@repo/agents']) and keep exporting nextConfig so Next.js
will transpile that package's TypeScript source during build.

In `@docs/agentic-monorepo-starter/apps/web/package.json`:
- Around line 14-16: The project depends on "ai": "^4.0.0" which does not
provide the v5+ exports used (stopWhen, inputSchema, UIMessage,
convertToModelMessages) and causes build failures; update the package.json
dependency entry for "ai": "^4.0.0" to "ai": "^6.0.0" (or at minimum "^5.0.0")
so those APIs are available, then reinstall/update the lockfile (npm/yarn/pnpm
install) to ensure the new version is installed.

In `@docs/agentic-monorepo-starter/packages/agents/package.json`:
- Line 9: Package.json currently pins the "ai" dependency to "^4.0.0" which is
incompatible with the code in src/index.ts that uses v5/v6 APIs (e.g.,
inputSchema: z.object(...)); update the version constraint for the "ai"
dependency in packages/agents/package.json from "^4.0.0" to "^6.0.0" so the
installed package matches the inputSchema API used by the tool definitions and
restore runtime compatibility.

In `@docs/agentic-monorepo-starter/packages/agents/src/index.ts`:
- Around line 10-12: The execute handler currently calls new
Date().toLocaleString("en-US", { timeZone: timezone }) which will throw a
RangeError for invalid IANA zone names; update the execute function to validate
or safely handle invalid timezones by either wrapping the date conversion in a
try/catch and returning a sensible fallback (e.g., UTC or an error field) or by
checking timezone against an allowed IANA list before calling toLocaleString;
refer to the execute function and the timezone/now variables to implement the
validation or try/catch and ensure the returned object always contains a stable
now value and clear indication when the provided timezone was invalid.

In `@docs/agentic-monorepo-starter/README.md`:
- Around line 13-22: In docs/agentic-monorepo-starter/README.md the fenced code
block after list item "2." breaks the numbered list (MD029); fix by indenting
the entire code fence and its contents three spaces so the opening ```bash, its
lines ("pnpm install", "pnpm dev") and the closing ``` are aligned under list
item 2, keeping items "3. Open <http://localhost:3000>." and "4. Ask: ..." as
the continued numbered list.

In `@packages/notifications/src/components/notifications-subscriber.tsx`:
- Around line 159-166: The initialization effect's early-exit guard
(initialize() checking initialized and hasInitAttemptRef.current) plus having
initialized in its dependency array causes listeners registered during
initialize() to be removed on re-render and never re-registered; fix by moving
all OneSignal event listener registration/removal out of initialize() into a
separate effect that watches only initialized (e.g., create a new useEffect that
subscribes to OneSignal.on('notificationDisplay', ...),
OneSignal.on('notificationClick', ...), onPermissionChange,
onSubscriptionChange, etc., and returns a cleanup to remove them), remove
initialized from the dependency array of the existing initialization effect that
calls initialize() and keep the hasInitAttemptRef/slug/appId guards there so
initialize() only handles OneSignal.init() and setInitialized(true).
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5af35268-35db-45e0-89b1-46e217a6cd94

📥 Commits

Reviewing files that changed from the base of the PR and between b166203 and cc5b901.

📒 Files selected for processing (27)
  • apps/web/src/middleware.ts
  • docs/agentic-monorepo-starter/.github/pull_request_template.md
  • docs/agentic-monorepo-starter/.github/workflows/ci.yml
  • docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml
  • docs/agentic-monorepo-starter/.gitignore
  • docs/agentic-monorepo-starter/AGENTS.md
  • docs/agentic-monorepo-starter/README.md
  • docs/agentic-monorepo-starter/apps/web/.eslintrc.json
  • docs/agentic-monorepo-starter/apps/web/app/api/chat/route.ts
  • docs/agentic-monorepo-starter/apps/web/app/globals.css
  • docs/agentic-monorepo-starter/apps/web/app/layout.tsx
  • docs/agentic-monorepo-starter/apps/web/app/page.tsx
  • docs/agentic-monorepo-starter/apps/web/next-env.d.ts
  • docs/agentic-monorepo-starter/apps/web/next.config.ts
  • docs/agentic-monorepo-starter/apps/web/package.json
  • docs/agentic-monorepo-starter/apps/web/tsconfig.json
  • docs/agentic-monorepo-starter/package.json
  • docs/agentic-monorepo-starter/packages/agents/package.json
  • docs/agentic-monorepo-starter/packages/agents/src/index.ts
  • docs/agentic-monorepo-starter/packages/config/README.md
  • docs/agentic-monorepo-starter/packages/eslint-config/next.js
  • docs/agentic-monorepo-starter/packages/eslint-config/package.json
  • docs/agentic-monorepo-starter/packages/tsconfig/base.json
  • docs/agentic-monorepo-starter/packages/tsconfig/package.json
  • docs/agentic-monorepo-starter/pnpm-workspace.yaml
  • docs/agentic-monorepo-starter/turbo.json
  • packages/notifications/src/components/notifications-subscriber.tsx

"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"form-action 'self' https://auth.privy.io https://privy.hypha.earth https://accounts.google.com https://*.google.com",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

https://*.google.com wildcard in form-action is overly permissive

accounts.google.com is already enumerated explicitly in the same directive, so the wildcard is redundant for its stated purpose and unnecessarily broadens the CSP attack surface to every *.google.com subdomain (e.g., docs.google.com, mail.google.com, and any future or obscure Google subdomain). If any of those has an open redirect or subdomain takeover, an injected form could exfiltrate data there while still satisfying this CSP directive.

Remove the wildcard and enumerate only the specific origins that are actually needed:

🔧 Proposed fix
-      "form-action 'self' https://auth.privy.io https://privy.hypha.earth https://accounts.google.com https://*.google.com",
+      "form-action 'self' https://auth.privy.io https://privy.hypha.earth https://accounts.google.com",

If another specific *.google.com subdomain is genuinely required for the auth flow, add it explicitly rather than using the wildcard.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"form-action 'self' https://auth.privy.io https://privy.hypha.earth https://accounts.google.com https://*.google.com",
"form-action 'self' https://auth.privy.io https://privy.hypha.earth https://accounts.google.com",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/middleware.ts` at line 77, Remove the overly permissive wildcard
from the CSP form-action entry: locate the header string "form-action 'self'
https://auth.privy.io https://privy.hypha.earth https://accounts.google.com
https://*.google.com" in apps/web/src/middleware.ts and delete the
"https://*.google.com" token so only the explicitly required origins (e.g.,
https://accounts.google.com) remain; if any additional specific Google subdomain
is truly needed for the auth flow, add that exact origin instead of a wildcard.

Comment on lines +1 to +14
## Change
-

## Reason
-

## Validation
- [ ] lint
- [ ] typecheck
- [ ] tests
- [ ] manual check

## Risk / Rollback
-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize heading structure to pass Markdown lint checks

Current heading layout triggers MD041/MD022. Add a top-level heading and blank lines around section headings.

Suggested fix
+# Pull Request
+
 ## Change
+
 -
 
 ## Reason
+
 -
 
 ## Validation
+
 - [ ] lint
 - [ ] typecheck
 - [ ] tests
 - [ ] manual check
 
 ## Risk / Rollback
+
 -
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Change
-
## Reason
-
## Validation
- [ ] lint
- [ ] typecheck
- [ ] tests
- [ ] manual check
## Risk / Rollback
-
# Pull Request
## Change
-
## Reason
-
## Validation
- [ ] lint
- [ ] typecheck
- [ ] tests
- [ ] manual check
## Risk / Rollback
-
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


[warning] 4-4: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 7-7: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 13-13: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/.github/pull_request_template.md` around lines
1 - 14, Add a top-level heading and ensure blank lines around each section
heading to satisfy MD041/MD022; specifically insert a single H1 title line above
the existing content (e.g., "# Pull Request") and add an empty line before and
after each "## Change", "## Reason", "## Validation", and "## Risk / Rollback"
heading so the template has proper top-level heading and spacing for the
Markdown linter.

Comment on lines +9 to +47
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- run: pnpm install --frozen-lockfile
- run: npm i -g vercel@latest

- name: Pull Vercel env
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
else
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
fi

- name: Build web
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel build --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel build --prod --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi

- name: Deploy web
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel deploy --prebuilt --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel deploy --prebuilt --prod --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Deploy job runs in parallel with CI — broken code can ship to production — and has no timeout.

Both ci.yml and deploy-web.yml trigger on the same events with no ordering constraint. If CI fails (lint, typecheck, build) the deploy job still proceeds concurrently. Add a needs reference to the CI job, or merge the deploy step into ci.yml after all validation steps. Also add a timeout-minutes to prevent runaway deployments from hanging the runner indefinitely.

🔧 Proposed fix (separate workflow approach)
 jobs:
   deploy:
     runs-on: ubuntu-latest
+    timeout-minutes: 20
+    needs: ci   # reference the CI job name from ci.yml if using reusable workflows,
+                # or move this job into ci.yml after the build step
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml` around lines
9 - 47, The deploy job currently runs independently and without a timeout;
update the deploy job (job id: deploy) to depend on the CI job by adding a
needs: ["ci"] (or the exact job id used in your CI workflow) so it only runs
after CI succeeds, and add timeout-minutes: 30 (or your desired limit) to the
deploy job to prevent runaway runs; ensure the change is applied at the job
level (next to runs-on) that contains the steps like "Pull Vercel env", "Build
web", and "Deploy web".

cache: pnpm

- run: pnpm install --frozen-lockfile
- run: npm i -g vercel@latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

npm i -g vercel@latest is unpinned and mixes package managers.

The project uses pnpm, but this step installs via npm. @latest is also unpinned — a new Vercel CLI major release could silently break the workflow. Pin to a specific version and use pnpm consistently:

🔧 Proposed fix
-      - run: npm i -g vercel@latest
+      - run: pnpm add -g vercel@39  # pin to the latest known-good major
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- run: npm i -g vercel@latest
- run: pnpm add -g vercel@39 # pin to the latest known-good major
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml` at line 23,
Replace the unpinned, npm-based install step that runs "npm i -g vercel@latest"
with a pinned pnpm install to avoid mixing package managers and accidental
breaking upgrades; specifically change the workflow step that contains the run
command "npm i -g vercel@latest" to use pnpm global install with a fixed version
(e.g., "pnpm add -g vercel@<major.minor.patch>") or use a pinned CLI invocation
pattern that matches the repo's pnpm usage.

Comment on lines +25 to +39
- name: Pull Vercel env
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
else
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
fi

- name: Build web
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel build --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel build --prod --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

vercel pull creates .vercel/ in the repo root but vercel build --cwd apps/web looks for it in apps/web/.vercel/ — and both are missing VERCEL_ORG_ID/VERCEL_PROJECT_ID.

Two related problems:

  1. Missing env vars: The Vercel CLI needs VERCEL_ORG_ID and VERCEL_PROJECT_ID to know which project to operate on. These are typically set as GitHub repository secrets alongside VERCEL_TOKEN, but they are absent from the workflow. Without them vercel pull will either prompt interactively (hanging the runner) or fail.

  2. Working-directory mismatch: vercel pull is run from the repo root, placing .vercel/project.json there. vercel build --cwd apps/web executes relative to apps/web/ and looks for .vercel/ inside that directory — a different location. Add --cwd apps/web to the vercel pull step as well, or consistently run all Vercel commands from apps/web.

🔧 Proposed fix
+      env:
+        VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
+        VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
       - name: Pull Vercel env
         run: |
           if [ "${{ github.event_name }}" = "pull_request" ]; then
-            vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
+            vercel pull --yes --environment=preview --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
           else
-            vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
+            vercel pull --yes --environment=production --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
           fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Pull Vercel env
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
else
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
fi
- name: Build web
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel build --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel build --prod --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi
- name: Pull Vercel env
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel pull --yes --environment=preview --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel pull --yes --environment=production --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi
- name: Build web
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
vercel build --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
else
vercel build --prod --cwd apps/web --token=${{ secrets.VERCEL_TOKEN }}
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/.github/workflows/deploy-web.yml` around lines
25 - 39, The workflow is missing VERCEL_ORG_ID and VERCEL_PROJECT_ID and is
pulling at repo root while building in apps/web, so update the GitHub Actions
steps to export/use the VERCEL_ORG_ID and VERCEL_PROJECT_ID secrets alongside
VERCEL_TOKEN and run vercel pull from the same cwd as the build; specifically
ensure the "Pull Vercel env" step passes VERCEL_ORG_ID and VERCEL_PROJECT_ID
(from secrets) into the environment and invoke vercel pull with the same --cwd
apps/web flag (or run both vercel pull and vercel build from apps/web) so
.vercel/project.json is created where vercel build --cwd apps/web can find it.

Comment on lines +14 to +16
"@ai-sdk/react": "^1.0.0",
"@repo/agents": "workspace:*",
"ai": "^4.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the latest stable version of the "ai" npm package (Vercel AI SDK v5)?

💡 Result:

Latest stable version of the Vercel AI SDK “ai” npm package (package name: ai) as of 2026-05-06: 6.0.175 [1][2]. Source evidence: npm’s version history for the ai package shows 6.0.175 published May 4, 2026, and the package page shows the currently viewed/installed version as 6.0.175 [1][2].

Citations:


🏁 Script executed:

# First, find the route.ts and agents/index.ts files to check if they actually use the claimed v5 APIs
find . -name "route.ts" -o -name "agents" -type d

Repository: hypha-dao/hypha-web

Length of output: 2409


🏁 Script executed:

# Check the chat route.ts file for API usage
cat -n docs/agentic-monorepo-starter/apps/web/app/api/chat/route.ts

# Check for agents index file
find ./docs/agentic-monorepo-starter/packages/agents -name "index.ts" -o -name "index.js" | head -5

Repository: hypha-dao/hypha-web

Length of output: 746


"ai": "^4.0.0" will not install AI SDK v5+ — the template is non-functional as shipped.

The code uses four v5-only APIs from the "ai" package: stopWhen, inputSchema, UIMessage, and convertToModelMessages (confirmed in docs/agentic-monorepo-starter/apps/web/app/api/chat/route.ts). The semver range ^4.0.0 resolves to ai@4.x and will not include these exports, causing compilation failure.

Update to "^6.0.0" to match the latest stable version (6.0.175 as of May 2026). At minimum, "^5.0.0" is required for the v5 APIs, but v6 is the current stable release.

🔧 Proposed fix
-    "ai": "^4.0.0",
+    "ai": "^6.0.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@ai-sdk/react": "^1.0.0",
"@repo/agents": "workspace:*",
"ai": "^4.0.0",
"@ai-sdk/react": "^1.0.0",
"@repo/agents": "workspace:*",
"ai": "^6.0.0",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/apps/web/package.json` around lines 14 - 16,
The project depends on "ai": "^4.0.0" which does not provide the v5+ exports
used (stopWhen, inputSchema, UIMessage, convertToModelMessages) and causes build
failures; update the package.json dependency entry for "ai": "^4.0.0" to "ai":
"^6.0.0" (or at minimum "^5.0.0") so those APIs are available, then
reinstall/update the lockfile (npm/yarn/pnpm install) to ensure the new version
is installed.

"main": "src/index.ts",
"types": "src/index.ts",
"dependencies": {
"ai": "^4.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, check the actual package.json file content
cat -n docs/agentic-monorepo-starter/packages/agents/package.json

Repository: hypha-dao/hypha-web

Length of output: 356


🏁 Script executed:

# Check if src/index.ts exists and examine its contents
find docs/agentic-monorepo-starter/packages/agents -name "index.ts" -o -name "index.js"

Repository: hypha-dao/hypha-web

Length of output: 122


🌐 Web query:

ai package npm latest version Vercel AI SDK current release

💡 Result:

Latest npm version of the Vercel AI SDK package "ai" (i.e., install command: npm i ai) is 6.0.175 (published May 4, 2026) [1][2]. How to install: npm i ai This corresponds to the GitHub release list showing ai@6.0.175 among recent releases [3].

Citations:


🌐 Web query:

ai package npm version 6 breaking changes from v4 v5

💡 Result:

The npm package "ai" (Vercel AI SDK) is currently at version 6.0.175 as of May 4, 2026 [1][2]. It has official migration guides detailing breaking changes. Breaking changes from v4 to v5 (major overhaul) [3][4]: - UIMessage and type system changes, including tool definitions (parameters → inputSchema), tool properties (args/result → input/output). - Stream data removal; custom streaming changes (writeMessageAnnotation/writeData removed). - Tool call streaming now default. - UI hooks moved (e.g., React to @ai-sdk/react); useChat changes like removed managed input state, onResponse removal, addToolResult → addToolOutput. - Run codemod: npx @ai-sdk/codemod v5 [5][3][6]. - Data migration needed for stored messages [7]. Breaking changes from v5 to v6 [8][9][10][11]: - CoreMessage removed → ModelMessage; convertToCoreMessages → convertToModelMessages (now async, add await). - textEmbeddingModel/embedding → embeddingModel/embedding. - ToolCallOptions → ToolExecutionOptions. - Mock classes V2 → V3 in ai/test. - Provider-specific: @ai-sdk/azure uses Responses API (use.chat for old); @ai-sdk/openai treats unknown models as reasoning. - generateObject/streamObject deprecated. - Run codemod: npx @ai-sdk/codemod v6 [8][10]. For full details and codemods covering most renames/refactors, see official docs [3][8][12]. Upgrade all related packages (ai, @ai-sdk/*) together [3][8].

Citations:


🏁 Script executed:

cat -n docs/agentic-monorepo-starter/packages/agents/src/index.ts

Repository: hypha-dao/hypha-web

Length of output: 555


Bump ai to ^6.0.0 — current version ^4.0.0 is incompatible with the starter code

The ai package is pinned to ^4.0.0 but the src/index.ts tool definitions already use v5/v6 API signatures. Specifically, the code uses inputSchema: z.object(...) which is the v5+ syntax; v4 uses parameters: z.object(...) instead. This incompatibility means the starter template will fail at runtime.

Bump to ^6.0.0 (current stable, v6.0.175 as of May 4, 2026) to align dependency with actual code requirements. Breaking changes exist between v4→v5→v6, but since the code is already written for v5/v6 APIs, the only action needed is updating the version constraint.

🔧 Proposed fix
-    "ai": "^4.0.0",
+    "ai": "^6.0.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"ai": "^4.0.0",
"ai": "^6.0.0",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/packages/agents/package.json` at line 9,
Package.json currently pins the "ai" dependency to "^4.0.0" which is
incompatible with the code in src/index.ts that uses v5/v6 APIs (e.g.,
inputSchema: z.object(...)); update the version constraint for the "ai"
dependency in packages/agents/package.json from "^4.0.0" to "^6.0.0" so the
installed package matches the inputSchema API used by the tool definitions and
restore runtime compatibility.

Comment on lines +10 to +12
execute: async ({ timezone }) => {
const now = new Date().toLocaleString("en-US", { timeZone: timezone });
return { timezone, now };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unvalidated timezone string will throw RangeError for invalid IANA zone names.

new Date().toLocaleString("en-US", { timeZone: timezone }) throws a RangeError when timezone is not a valid IANA timezone identifier (e.g. "foo", "UTC+5"). Since the AI model supplies this argument, it may occasionally produce invalid values. Wrap the body in a try/catch or validate with a known-valid zone list.

🛡️ Proposed fix
     execute: async ({ timezone }) => {
-      const now = new Date().toLocaleString("en-US", { timeZone: timezone });
-      return { timezone, now };
+      try {
+        const now = new Date().toLocaleString("en-US", { timeZone: timezone });
+        return { timezone, now };
+      } catch {
+        return { error: `Invalid timezone: "${timezone}". Please use a valid IANA timezone name such as "America/New_York".` };
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
execute: async ({ timezone }) => {
const now = new Date().toLocaleString("en-US", { timeZone: timezone });
return { timezone, now };
execute: async ({ timezone }) => {
try {
const now = new Date().toLocaleString("en-US", { timeZone: timezone });
return { timezone, now };
} catch {
return { error: `Invalid timezone: "${timezone}". Please use a valid IANA timezone name such as "America/New_York".` };
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/packages/agents/src/index.ts` around lines 10 -
12, The execute handler currently calls new Date().toLocaleString("en-US", {
timeZone: timezone }) which will throw a RangeError for invalid IANA zone names;
update the execute function to validate or safely handle invalid timezones by
either wrapping the date conversion in a try/catch and returning a sensible
fallback (e.g., UTC or an error field) or by checking timezone against an
allowed IANA list before calling toLocaleString; refer to the execute function
and the timezone/now variables to implement the validation or try/catch and
ensure the returned object always contains a stable now value and clear
indication when the provided timezone was invalid.

Comment on lines +13 to +22
1. Copy this folder to a new repo root.
2. Run install and development:

```bash
pnpm install
pnpm dev
```

3. Open <http://localhost:3000>.
4. Ask: "What time is it in Europe/Amsterdam?"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ordered list numbering breaks after the code block (MD029)

The fenced code block between items 2 and 3 is not indented under the list item, so most parsers and linters treat items 3 and 4 as a new list that unexpectedly starts at 3. markdownlint flags this as MD029.

The simplest fix is to indent the code block (3 spaces, to align with the list item content) so it stays part of item 2, keeping the numbered list continuous:

🔧 Proposed fix
 1. Copy this folder to a new repo root.
 2. Run install and development:

-```bash
-pnpm install
-pnpm dev
-```
+   ```bash
+   pnpm install
+   pnpm dev
+   ```

 3. Open <http://localhost:3000>.
 4. Ask: "What time is it in Europe/Amsterdam?"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
1. Copy this folder to a new repo root.
2. Run install and development:
```bash
pnpm install
pnpm dev
```
3. Open <http://localhost:3000>.
4. Ask: "What time is it in Europe/Amsterdam?"
1. Copy this folder to a new repo root.
2. Run install and development:
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 21-21: Ordered list item prefix
Expected: 1; Actual: 3; Style: 1/2/3

(MD029, ol-prefix)


[warning] 22-22: Ordered list item prefix
Expected: 2; Actual: 4; Style: 1/2/3

(MD029, ol-prefix)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agentic-monorepo-starter/README.md` around lines 13 - 22, In
docs/agentic-monorepo-starter/README.md the fenced code block after list item
"2." breaks the numbered list (MD029); fix by indenting the entire code fence
and its contents three spaces so the opening ```bash, its lines ("pnpm install",
"pnpm dev") and the closing ``` are aligned under list item 2, keeping items "3.
Open <http://localhost:3000>." and "4. Ask: ..." as the continued numbered list.

Comment on lines 159 to +166
const initialize = async () => {
if (initialized) {
console.warn('OneSignal should be initialized only once!');
if (initialized || hasInitAttemptRef.current) {
return;
}
if (!person?.slug || !appId) {
return;
}
hasInitAttemptRef.current = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Listeners are permanently removed after init: initialized in the dep array + early-exit guard creates a listener lifecycle bug.

The sequence after a successful init:

  1. OneSignal.init() succeeds → setInitialized(true) is called (line 203).
  2. All event listeners are registered (lines 206–250).
  3. React re-renders because initialized is now true (it's in the dep array at line 304).
  4. Effect cleanup runs → all listeners are removed (lines 257–303).
  5. Effect re-runs → initialize() hits if (initialized || ...) at line 160 and returns early.
  6. Listeners are never re-registered. Notification clicks, permission changes, subscription changes, etc., all stop working silently.

The root cause is that registering listeners inside initialize() (which guards on initialized) and including initialized in the dep array creates a self-defeating cycle: the state change that marks success triggers the cleanup that erases the work.

Fix — separate listener registration from initialization:

🐛 Proposed fix
- const initialize = async () => {
-   if (initialized || hasInitAttemptRef.current) {
-     return;
-   }
-   if (!person?.slug || !appId) {
-     return;
-   }
-   hasInitAttemptRef.current = true;
-   try {
-     // ... OneSignal.init() ...
-     setInitialized(true);
-     const isSubscribed = await hasPermission();
-     setSubscribed(isSubscribed);
-     OneSignal.Notifications.addEventListener('click', notificationClickHandler);
-     // ... all other addEventListener calls ...
-   } catch (err) {
-     hasInitAttemptRef.current = false;
-     console.log('Initialize error:', err);
-   }
- };
- initialize();
- return () => {
-   OneSignal.Notifications.removeEventListener('click', notificationClickHandler);
-   // ... all other removeEventListener calls ...
- };
+ // Phase 1: only initialize once; listeners are wired in a separate effect below.
+ if (!initialized && !hasInitAttemptRef.current && person?.slug && appId) {
+   hasInitAttemptRef.current = true;
+   (async () => {
+     try {
+       const scope = serviceWorkerPath
+         ? `/${serviceWorkerPath.split('/').filter(Boolean)[0]}/`
+         : '/';
+       await OneSignal.init({ appId, safari_web_id: safariWebId, serviceWorkerPath, serviceWorkerParam: { scope }, /* … */ });
+       console.log('OneSignal initialized');
+       setInitialized(true);
+       const isSubscribed = await hasPermission();
+       setSubscribed(isSubscribed);
+     } catch (err) {
+       hasInitAttemptRef.current = false;
+       console.log('Initialize error:', err);
+     }
+   })();
+ }

Then wire listeners in a separate effect that depends only on initialized:

+ React.useEffect(() => {
+   if (!initialized) return;
+   OneSignal.Notifications.addEventListener('click', notificationClickHandler);
+   // ... all other addEventListener calls ...
+   return () => {
+     OneSignal.Notifications.removeEventListener('click', notificationClickHandler);
+     // ... all other removeEventListener calls ...
+   };
+ }, [initialized]);  // handlers must be stable refs (useCallback) or defined outside

And remove initialized from the initialization effect's dep array:

- }, [appId, safariWebId, serviceWorkerPath, router, initialized, person?.slug]);
+ }, [appId, safariWebId, serviceWorkerPath, person?.slug]);

Also applies to: 251-253, 304-304

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/notifications/src/components/notifications-subscriber.tsx` around
lines 159 - 166, The initialization effect's early-exit guard (initialize()
checking initialized and hasInitAttemptRef.current) plus having initialized in
its dependency array causes listeners registered during initialize() to be
removed on re-render and never re-registered; fix by moving all OneSignal event
listener registration/removal out of initialize() into a separate effect that
watches only initialized (e.g., create a new useEffect that subscribes to
OneSignal.on('notificationDisplay', ...), OneSignal.on('notificationClick',
...), onPermissionChange, onSubscriptionChange, etc., and returns a cleanup to
remove them), remove initialized from the dependency array of the existing
initialization effect that calls initialize() and keep the
hasInitAttemptRef/slug/appId guards there so initialize() only handles
OneSignal.init() and setInitialized(true).

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.

1 participant