chore: Compact CHANGELOG.MD to legacy file#139
Conversation
* feat: Phase 8 group gifts (#130) * Bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: bump version to 1.3.3 * chore: add Vitest unit tests (repo advisory item 1) (#75) * chore: add Vitest unit tests for lib/birthdays and lib/occasions 57 tests covering date parsing, next-occurrence rollover, age calculation, money formatting, Easter algorithm, and occasion countdown logic. vitest.config.ts scopes coverage to the two tested files (85/80% thresholds). pr-checks.yml extended with a test+coverage step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: bump CI Node version to 24 to match local npm 11 lockfile package-lock.json was generated by npm 11 (Node 24 local); npm 10 (Node 20 CI) rejects it with missing esbuild entries. Aligning CI to Node 24 resolves the npm ci lockfile sync error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add husky + lint-staged + commitlint pre-commit enforcement (#77) Pre-commit hook runs ESLint via lint-staged on staged ts/tsx files. Commit-msg hook validates Conventional Commits format via commitlint. prepare script ensures hooks install automatically after npm install. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Sentry error tracking (#79) Installs @sentry/nextjs and wires up client, server, and edge configs. All three Sentry env vars are optional; init is skipped when SENTRY_DSN is unset so self-hosters who don't want Sentry are unaffected. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Dependabot auto-merge for patch and minor updates (#81) Workflow triggers on Dependabot PRs only and calls gh pr merge --auto --squash for patch/minor bumps. Major bumps stay open for manual review. CI must pass before GitHub actions on the auto-merge flag. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Trivy container image vulnerability scan (#83) Scans the app image after push using aquasecurity/trivy-action. Fails on CRITICAL severity CVEs with available fixes; uploads SARIF results to the GitHub Security tab on every run. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Docker memory limits and wire Sentry env vars (#85) Adds deploy.resources.limits.memory to all four compose services: db 512m, migrate 256m, app 512m, cron 64m. Prevents OOM killer taking down Postgres on the Pi during memory pressure. Also passes SENTRY_DSN/ORG/PROJECT through to the app container. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: bump trivy-action to v0.36.0 — 0.31.0 tag does not exist (#86) (#87) * chore: add app health check endpoint and Docker healthcheck (#89) GET /api/health returns {status:"ok"} with 200. docker-compose.yml gains a healthcheck on the app service and upgrades the cron depends_on condition to service_healthy, replacing the manual readiness poll loop in the cron entrypoint. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: strip esbuild binaries from runner image (CVE-2024-24790, CVE-2025-68121) (#91) * fix: bump trivy-action to v0.36.0 — 0.31.0 tag does not exist (#86) * fix: strip esbuild binaries from runner image to resolve CVE-2024-24790 and CVE-2025-68121 (#90) * fix: upgrade Next.js, next-auth, drizzle-kit to resolve CVEs (#93) next 15.2.9 → 15.5.18: fixes high severity SSRF, cache poisoning, HTTP request smuggling, DoS, and content injection CVEs. next-auth beta.25 → beta.31: fixes email misdelivery CVE. drizzle-kit 0.30.x → 0.31.10: reduces esbuild advisory surface. 6 moderate vulns remain in upstream transitive deps (unfixable). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add concurrency groups to pr-checks and docker-publish (#95) Cancels stale CI runs when new commits push to the same branch. Critical for docker-publish given the ~10 min multi-arch build time. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add release helper workflow (#97) * fix: add limit-severities-for-sarif to align Trivy exit code with CRITICAL-only scan * chore: add release helper workflow workflow_dispatch with tag/title/notes inputs that runs gh release create --latest. Keeps releases manual and phase-gated but removes the friction of remembering the exact CLI invocation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: bump version to 1.3.4 and compact CHANGELOG - package.json: 1.3.3 → 1.3.4 - CHANGELOG.md: archived 2026-05-06 and earlier entries to CHANGELOG-legacy.md * fix: resolve npm security vulnerabilities (Next.js CVEs + PostCSS XSS) (#102) * fix: upgrade Next.js, next-auth, drizzle-kit to resolve CVEs next 15.2.9 → 15.5.18: fixes high severity SSRF, cache poisoning, HTTP request smuggling, DoS, and content injection CVEs. next-auth beta.25 → beta.31: fixes email misdelivery CVE. drizzle-kit 0.30.x → 0.31.10: reduces esbuild advisory surface. 6 moderate vulns remain in upstream transitive deps (unfixable). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: force postcss >=8.5.10 via npm overrides to resolve Dependabot alert #10 Adds an npm overrides entry so Next.js's nested postcss@8.4.31 is replaced by the patched version. Bumps the direct devDep range to match. Closes #101. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: correct stale vulnerability count in CHANGELOG Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: resolve CHANGELOG merge conflict from rebase Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: bump version to 1.3.5 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve package-lock.json conflict markers and sync version to 1.3.5 Also updates CHANGELOG entry to mention the version bump. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Release Please and Codecov automation (#105) * chore: add Release Please, Codecov, and Socket Security automation - Release Please workflow + config watches main branch for conventional commits and auto-opens versioning PRs (CHANGELOG + package.json bump) - Codecov upload step added to pr-checks.yml; lcov reporter added to vitest config so coverage/lcov.info is generated on each run - Socket Security is a GitHub App install (see issue #104 for link) Closes #104 * fix: pin GitHub Actions to full commit SHAs for supply-chain security * docs: add bot-comment review step to PR workflow memory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: phase 8 — group gifts (#111) * feat: phase 8 — group gifts (#28) Add group gift coordination: track contributors, amounts, and payment status for split purchases. New gift_groups and gift_group_contributors tables, /gift-groups list + detail pages, Groups nav tab, and a Group gift button on every wishlist item. Bump version 1.3.5 → 1.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address coderabbit review issues on group gifts - parsePence: reject non-finite values (Infinity, -Infinity) - updateGiftGroup: validate status against whitelist before db update - updateContributor/deleteContributor: scope WHERE to groupId and contributorId - getGiftGroup: push userId ownership into SQL WHERE clause - schema: add CHECK constraints for non-negative pence columns - detail page: extract delete confirm into a client component Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: validate personId and wishlistItemId ownership in createGiftGroup Prevent IDOR by verifying the personId and wishlistItemId passed via form data belong to the current user before inserting a new gift group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve codacy error-prone issues in gift-groups ui wrap server actions in void arrow functions to satisfy form action void return type; remove unnecessary ?? fallbacks on status maps whose keys are always present for valid enum values * refactor: extract shared gift-groups constants to reduce duplication * chore: disable coderabbit docstring coverage check * fix: add aria-label to create group gift form inputs --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: phase 8 — group gifts + bundled enhancements (#108 #109 #110 #112) (#113) * feat: phase 8 — group gifts (#28) Add group gift coordination: track contributors, amounts, and payment status for split purchases. New gift_groups and gift_group_contributors tables, /gift-groups list + detail pages, Groups nav tab, and a Group gift button on every wishlist item. Bump version 1.3.5 → 1.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address coderabbit review issues on group gifts - parsePence: reject non-finite values (Infinity, -Infinity) - updateGiftGroup: validate status against whitelist before db update - updateContributor/deleteContributor: scope WHERE to groupId and contributorId - getGiftGroup: push userId ownership into SQL WHERE clause - schema: add CHECK constraints for non-negative pence columns - detail page: extract delete confirm into a client component Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: validate personId and wishlistItemId ownership in createGiftGroup Prevent IDOR by verifying the personId and wishlistItemId passed via form data belong to the current user before inserting a new gift group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve codacy error-prone issues in gift-groups ui wrap server actions in void arrow functions to satisfy form action void return type; remove unnecessary ?? fallbacks on status maps whose keys are always present for valid enum values * refactor: extract shared gift-groups constants to reduce duplication * chore: disable coderabbit docstring coverage check * fix: add aria-label to create group gift form inputs * feat: occasion-linked gifts, reminder suppression, duplicate occasion guard (#108 #109 #110 #112) - db/schema.ts: add nullable occasion_id to wishlist_items; update relation - wishlist-item-edit-form.tsx: new client component — edit form with occasion picker; status→given intercept triggers inline Record gift form - page.tsx: integrate WishlistItemEditForm; show violet occasion badge; remove standalone Mark as given block - actions.ts: updateWishlistItem saves occasionId - occasion-actions.ts: block duplicate preset occasion kinds per person - settings/occasion-actions.ts: block duplicate preset occasion kinds site-wide - reminders.ts: skip digest blocks when all wishlist items are done; filter people from site-wide emails when linked occasion items all purchased/given Requires: npm run db:push to add occasion_id column to wishlist_items Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revert server action arrow wrappers that broke serialization anonymous arrow functions are not serializable as form actions in server components; only "use server" functions can be passed as action props — revert to direct server action references * fix: address Codacy review issues on phase 8 bundle - db/schema.ts: add FK reference on wishlist_items.occasion_id - actions.ts: resolveOccasionId helper validates ownership + fixes NaN parse; createWishlistItem now accepts occasionId - occasion-actions.ts: replace `as any` with typed OccasionKindValue - settings/occasion-actions.ts: same as above - wishlist-item-edit-form.tsx: void-wrap server action props Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: restore formatPenceInput helper removed in client component refactor Used in the person edit form (budgetMin/budgetMax fields), not only in the wishlist item edit form. Caused TS2304 in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address remaining Codacy issues on phase 8 bundle - occasion-actions.ts + settings/occasion-actions.ts: fix as any in .values() inserts (was only fixed in eq() queries previously) - page.tsx: use getKnownOccasionLabel in occasionNameById map; add occasion picker to Add item form so occasionId can be set on creation - wishlist-item-edit-form.tsx: use braces on onClick arrow; add two-step confirm before delete to prevent accidental data loss Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: apply OccasionKindValue cast to updateOccasion and updateSiteWideOccasion Missed in previous as-any sweep — only create actions were fixed, not update. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revert server action arrow wrappers causing serialization crash anonymous arrow functions cannot be serialized as form actions from server components; only "use server" functions can be passed as action props — revert to direct server action references to fix the gift-groups page crash on production * feat: collaborative group gift contributors with invite flow (#115) * feat: collaborative group gift contributors with invite flow - schema: userId, inviteToken, inviteExpiresAt, inviteAcceptedAt on gift_group_contributors - addContributor: auto-links existing accounts or sends 30-day invite email - new actions: resendInvite, acceptInvite, leaveGroup, updateMyContribution - /gift-groups: split into owned vs contributing sections - /gift-groups/[id]: owner vs contributor view with role-based controls - /gift-groups/invite/[token]: public invite acceptance page - register page: forwards callbackUrl so invite survives sign-up flow closes #114 * chore: bump version to 1.4.1 * chore: revert version to 1.4.0 * fix: address Codacy and CodeRabbit issues on PR #115 - Refactor invite page: remove acceptInvite call during render (mutation on GET). Now shows an Accept invite button; mutation happens only on form submit via new acceptInviteAction. Errors redirect via search param instead of render path. - Fix case-insensitive email comparison in acceptInvite (toLowerCase both sides). - Validate callbackUrl starts with '/' before use in register page (XSS guard). - Add defaultRandom() to inviteToken so rows always get a UUID on insert. - Add partial unique index on (groupId, userId) WHERE userId IS NOT NULL. - Consolidate duplicate resend-invite forms into one with conditional text/class. * fix: normalize email in updateContributor and guard duplicate contributor insert - updateContributor: lowercase email on save for consistency with addContributor - addContributor: check for existing (groupId, userId) row before inserting when the invited email belongs to an existing user — prevents 500 from the unique index constraint added in the previous commit * fix: replace window.location.href with router.push and remove unused import - app/login/register/page.tsx: use Next.js useRouter().push() instead of direct window.location.href assignment (eliminates Codacy XSS flag; also more correct for a Next.js SPA as it avoids a full page reload) - lib/gift-groups-queries.ts: remove unused `or` import from drizzle-orm Note: Codacy flags action={serverAction} as "Promise-returning function in void attribute" but @types/react 19 already types form action as (FormData) => void | Promise<void>, making this pattern type-safe. The flag is a false positive from Codacy's older type resolution. * fix: introduce ActionForm client wrapper to resolve Promise-in-void-attribute flags Arrow wrappers on server action props in RSC break Next.js action serialization. Passing the server action to a client component (ActionForm) which then wraps it in a void arrow internally is the correct pattern — the server action reference is serializable, the void wrapper satisfies the linter in the client context. Replaces action={deleteContributor|leaveGroup|updateMyContribution|resendInvite} in gift-groups/[id]/page.tsx and action={acceptInviteAction} in the invite page. * fix: reject protocol-relative callbackUrl values to prevent open redirect * fix: wrap useSearchParams in Suspense boundary on /login/register Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: generate inviteToken in app code to guarantee invite emails are sent (#117) * fix: generate inviteToken in application code to guarantee invite emails are sent * refactor: extract newInvite() helper to deduplicate token generation * fix: log email errors in gift group actions (#119) * fix: log email errors instead of silently swallowing them * fix: defer inviteAcceptedAt until after notification succeeds and add recipient to error logs * feat: per-email-type from addresses with shared fallback (#121) * feat: per-email-type from addresses (EMAIL_FROM_REMINDERS, EMAIL_FROM_INVITES, EMAIL_FROM_AUTH) * refactor: export fromAddress helper and centralise Resend client construction * feat: in-app accept/decline for invited users + register flow for new users (#125) * feat: in-app accept/decline for existing users and register link for new users * fix: remove dead sendGroupGiftNotification and add expiry check to acceptLinkedInvite * refactor: extract fetchGroups named function to fix Codacy HIGH and query duplication Converts the inner async arrow function `const fetchGroups = async (ids) =>` to a module-level named `async function fetchGroups(where: SQL)` that accepts a Drizzle where condition. The owned query now uses fetchGroups, eliminating the 9-line query duplication. A thin `fetchById` helper handles the empty-ids guard for contributing/pending queries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: inline fetchById to remove remaining arrow function (Codacy HIGH) The Qwik/Biome "non-serializable expression" rule flags any const arrow function. Inline the two-branch condition directly into Promise.all to eliminate the pattern entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: sender display name and digest promotional classification (#127) * feat: in-app accept/decline for existing users and register link for new users * fix: remove dead sendGroupGiftNotification and add expiry check to acceptLinkedInvite * refactor: extract fetchGroups named function to fix Codacy HIGH and query duplication Converts the inner async arrow function `const fetchGroups = async (ids) =>` to a module-level named `async function fetchGroups(where: SQL)` that accepts a Drizzle where condition. The owned query now uses fetchGroups, eliminating the 9-line query duplication. A thin `fetchById` helper handles the empty-ids guard for contributing/pending queries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: inline fetchById to remove remaining arrow function (Codacy HIGH) The Qwik/Biome "non-serializable expression" rule flags any const arrow function. Inline the two-branch condition directly into Promise.all to eliminate the pattern entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: strip env-var quotes in fromAddress and remove prices/URLs from digest email fromAddress() now strips leading/trailing quote chars so display names survive .env parsing on all Docker Compose versions. Birthday digest shortlist no longer includes external retailer URLs or prices -- these are the primary signals Gmail uses to classify email as promotional. Shortlist shows title, kind tag, and retailer name only; the person-page link in each card leads to full details. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add unit tests for fromAddress quote stripping and digest email renderers 16 tests covering: - fromAddress: specific key, EMAIL_FROM fallback, hardcoded default, double-quote strip, single-quote strip, no inner-quote stripping - renderDigestText / renderDigestHtml: no prices, no external URLs, item titles and retailer names present, person-page link, empty shortlist Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add missing birthday and birthYearKnown fields to test DigestPersonBlock TypeScript CI caught the incomplete test fixture — DigestPersonBlock requires birthday (string) and birthYearKnown (boolean). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: include wishlist items in reminder email shortlist (#129) Reminder digest emails showed "No shortlist yet" even when a person had wishlist items, because buildShortlistForPerson only pulled from the products (AI search results) and suggestions tables. Now fetches active (non-purchased/given) wishlist items and includes them as a "wishlist" kind ShortlistEntry. Priority order: AI products -> wishlist items -> AI suggestions. Email renderers updated to label them [Wishlist]. Closes #128 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address PR #130 review comments from Codacy and CodeRabbit * fix: address final PR #130 review comments (Round 2) * fix: address CodeRabbit comprehensive feedback * revert: remove PR review fixes * fix: re-apply: restore security fixes * revert: remove all PR review fixes and restore to commit 4bcb6fc * fix: revert erroneous commits and restore stable 1865c61 * chore: revert all PR review fixes to restore stable 4bcb6fc * fix: harden gift-group contributor email edits Email changes now clear stale user links, regenerate invite tokens, and re-send invites. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: harden register, occasions, and gift-group review findings Safe error parsing on register; preset duplicate check on occasion update; gift-group validation, invites, logging; narrower git allowlist. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: address Codacy security findings and esbuild advisory parsePence uses explicit parsing; register JSON guard simplified; npm overrides force esbuild ^0.25.12 (GHSA-67mh-4wv8-2f99). Co-authored-by: Cursor <cursoragent@cursor.com> * fix: satisfy Codacy object-injection rule in parsePence Co-authored-by: Cursor <cursoragent@cursor.com> * chore: wire Release Please manifest config in workflow Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore: release 1.4.0 (#131) * chore: release 1.4.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Daniel Grey <dbwg2009@gmail.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dbwg2009 <64641726+dbwg2009@users.noreply.github.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR updates two changelog files to archive historical entries and document recent changes. CHANGELOG-legacy.md receives 280 lines of entries from May 2026 covering infrastructure, security, and feature work. CHANGELOG.md is updated with five new documentation entries and one removal, reflecting workflow fixes, Release Please configuration, Phase 8 completion, and maintenance changes. ChangesChangelog Archive & Active Update
🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Up to standards ✅🟢 Issues
|
There was a problem hiding this comment.
Pull Request Overview
While this PR is technically 'up to standards' according to Codacy, it fails to meet its primary functional objectives. Specifically, it violates the archival logic stated in the documentation and breaks the established formatting of the changelog.
Key issues that prevent merging:
- Acceptance Criteria Gaps: The PR fails to maintain reverse-chronological order and incorrectly archives entries that fall outside the defined legacy scope.
- Data Integrity: Significant duplication was introduced, with entries for May 8th through May 10th appearing in both the main and legacy files.
- Logic Inconsistency: Entries for May 10th were added to a legacy file explicitly scoped for 'May 6th and earlier'.
About this PR
- The PR description is empty. Please provide context regarding the chosen archival threshold and why recent entries (May 10th) are being moved to a legacy file.
Test suggestions
- Verify CHANGELOG.md maintains reverse-chronological order (newest entries at top)
- Verify CHANGELOG-legacy.md content matches the '2026-05-06 and earlier' scope defined in its header
- Verify no duplicate entries exist between the main and legacy changelogs
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Verify CHANGELOG.md maintains reverse-chronological order (newest entries at top)
2. Verify CHANGELOG-legacy.md content matches the '2026-05-06 and earlier' scope defined in its header
3. Verify no duplicate entries exist between the main and legacy changelogs
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
| ## [2026-05-10] fix: create-pull-request duplicate Authorization header with checkout | ||
| **By:** Cursor | ||
| **What:** **`changelog-archive`**, **`sync-gemini`**, and **`sync-main-to-development`**: **`actions/checkout@v6`** now uses **`persist-credentials: false`** (plus **`token`**) where no follow-up **`git fetch`** is needed; **`sync-main-to-development`** clears **`http.https://github.com/.extraheader`** after the merge step. All three unset that config before **`peter-evans/create-pull-request`** as a safeguard and pass **`token: ${{ secrets.GITHUB_TOKEN }}`** explicitly to the action. | ||
| **Why:** Git was sending two `Authorization` headers (`Duplicate header` / HTTP 400) because checkout persisted credentials and create-pull-request injected its own. | ||
|
|
||
|
|
||
| ## [2026-05-08] Bump version to 1.3.4 and compact CHANGELOG | ||
| **By:** Claude Code | ||
| **What:** `package.json` version `1.3.3` → `1.3.4`. Archived entries from 2026-05-06 and earlier to `CHANGELOG-legacy.md` to keep the active log under 300 lines. | ||
| **Why:** Patch release covering all repo advisory improvements: Vitest unit tests, husky/commitlint hooks, Sentry error tracking, Dependabot auto-merge, Trivy container scanning, Docker memory limits, health check endpoint, npm vulnerability upgrades (Next.js, next-auth, drizzle-kit), CI concurrency groups, and release helper workflow. | ||
|
|
||
| --- | ||
| ## [2026-05-10] fix: bot workflows use PRs + setup-node v6 (protected Development) | ||
| **By:** Cursor | ||
| **What:** **`changelog-archive`**, **`sync-gemini`**, and **`sync-main-to-development`** now use **`peter-evans/create-pull-request@v7.0.8`** so updates land via PRs (branch protection was rejecting direct pushes that lacked prior passing checks). **`changelog-archive`** uses **`actions/setup-node@v6`**. Removed **`changelog:compact`** from **`package.json`** — run **`node scripts/compact-changelog.mjs`** locally instead; **CLAUDE.md** / memory updated. | ||
| **Why:** GH006 on protected `Development`; required **TypeScript & Lint** must run on the merge path. `setup-node@v6` per security preference; npm script removed so compaction stays a plain Node script. | ||
|
|
||
|
|
||
|
|
||
| ## [2026-05-10] chore: Phase 8 done in CLAUDE + automated CHANGELOG archive on Development | ||
| **By:** Cursor | ||
| **What:** Marked **Phase 8 — Group Gifts** **done** and added **`v1.4.0`** to the release table in **CLAUDE.md**. Added **`scripts/compact-changelog.mjs`** and **`.github/workflows/changelog-archive.yml`** (later updated to open a PR on **`Development`** when **`CHANGELOG.md`** exceeds **300** lines, target **250**, after compacting). Documented in **CLAUDE.md** and **`.claude/memory`**. | ||
| **Why:** Owner request: phase table accuracy and hands-free archival so the main changelog stays readable in agent context. | ||
|
|
||
|
|
||
|
|
||
| ## [2026-05-10] chore: Release Please — skip root CHANGELOG, sync main→Development, docs | ||
| **By:** Cursor | ||
| **What:** `release-please-config.json`: `skip-changelog` for root `CHANGELOG.md` (human file only); `include-component-in-tag: false` so GitHub release names/tags are `vX.Y.Z` not `noted: vX.Y.Z`; visible sections for refactor/chore/docs; `pull-request-header` intro pointing to `CHANGELOG.md`. Manifest set to **1.4.0**. New workflow **`sync-main-to-development.yml`** merges `main` into `Development` on each push to `main` (plus `workflow_dispatch`). **CLAUDE.md** and `.claude/memory` (commits, releases, MEMORY index) updated: agents must **not** bump `package.json`; Release Please owns semver on the release PR; post-release checklist references merging the release PR. | ||
| **Why:** Match the owner’s release workflow: automated semver + GitHub Release from conventional commits, narrative changelog unchanged, Development kept in sync after squash merges to main. | ||
|
|
||
|
|
||
|
|
||
| ## [2026-05-10] chore: Release Please token fallback for PR creation | ||
| **By:** Cursor | ||
| **What:** `release-please.yml` uses `token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}` and comments document enabling Actions-created PRs or adding the `RELEASE_PLEASE_TOKEN` repo secret. | ||
| **Why:** GitHub rejects `GITHUB_TOKEN` for `POST /repos/.../pulls` unless the repo allows Actions to create/approve pull requests; optional PAT avoids that when the setting cannot be enabled. | ||
|
|
There was a problem hiding this comment.
🟡 MEDIUM RISK
These 2026-05-10 entries have been placed after the 2026-05-08 entries, breaking the reverse-chronological order of the changelog. They should be moved back to the top of the file to maintain the established 'newest-at-top' format.
| --- | ||
|
|
||
|
|
||
| ## [2026-05-10] chore: wire Release Please to manifest config |
There was a problem hiding this comment.
🟡 MEDIUM RISK
This archival process has created significant duplication: the entries for May 8th through May 10th now exist in both CHANGELOG.md and CHANGELOG-legacy.md. Furthermore, including May 10th entries contradicts the legacy file's header (line 3), which states it is for entries from May 6th and earlier.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@CHANGELOG-legacy.md`:
- Around line 7-281: The legacy changelog contains entries (2026-05-08 →
2026-05-10) that are still active in CHANGELOG.md and the preface cutoff text
still reads “2026-05-06 and earlier”; remove the duplicated active entries from
CHANGELOG-legacy.md so it only holds the archived range, and update the
preface/cutoff line to the actual archive cutoff date that the workflow
`.github/workflows/changelog-archive.yml` uses (or vice‑versa: ensure the
workflow and the cutoff text agree); verify consistency between CHANGELOG.md,
CHANGELOG-legacy.md, and the workflow so only entries older than the agreed
cutoff are moved.
In `@CHANGELOG.md`:
- Around line 210-242: The three new "## [2026-05-10]" sections (the fix:
create-pull-request duplicate Authorization header..., the fix: bot
workflows..., and the chore: Phase 8 done...) are appended below older entries;
move these entire 2026-05-10 blocks so they appear above the existing "##
[2026-05-08]" block to restore chronological order. Locate the headers matching
"## [2026-05-10]" in CHANGELOG.md, cut those contiguous sections (including
their By/What/Why paragraphs and the Release Please token fallback section) and
paste them immediately before the "## [2026-05-08]" header so the file reads
newest-to-oldest.
🪄 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: CHILL
Plan: Pro Plus
Run ID: e0a1b291-9f9d-4fc2-956f-f265024a6625
📒 Files selected for processing (2)
CHANGELOG-legacy.mdCHANGELOG.md
| ## [2026-05-10] chore: wire Release Please to manifest config | ||
| **By:** Cursor | ||
| **What:** Updated `.github/workflows/release-please.yml` to use manifest mode with `config-file` / `manifest-file` under `.github/` (removed the workflow-level `release-type: node`, which forced simple mode and ignored `release-please-config.json`). Added `issues: write` to match upstream Release Please permission recommendations. | ||
| **Why:** The repo already maintained `release-please-config.json` (changelog sections, `chore: release ${version}` PR title pattern) and `release-please-manifest.json`, but the action never loaded them; Release PRs now follow that configuration. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Fix: Codacy object-injection flags on parsePence string indexing | ||
| **By:** Cursor | ||
| **What:** In `app/gift-groups/actions.ts`, `parsePence` now uses `String.prototype.charAt` instead of bracket indexing (`s[i]`) when scanning trimmed amount strings. | ||
| **Why:** Codacy (PR #130) reported high-severity “object injection sink” findings on dynamic `s[i]` access; `charAt` preserves the same parsing behaviour without tripping that rule. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Security: Codacy ReDoS flag on parsePence + Dependabot esbuild override | ||
| **By:** Cursor | ||
| **What:** Replaced the `parsePence` regex in `app/gift-groups/actions.ts` with explicit digit/fraction parsing so static analysis no longer flags a ReDoS pattern (behaviour unchanged: non-negative GBP strings with 0–2 decimal places). Adjusted register error JSON handling in `app/login/register/page.tsx` to drop the redundant `parsed !== null` branch Codacy flagged. Added an npm `overrides` entry for `esbuild` `^0.25.12` so the transitive copy pulled in via `drizzle-kit` / `@esbuild-kit/core-utils` resolves to a patched release (GHSA-67mh-4wv8-2f99); refreshed `package-lock.json` accordingly. | ||
| **Why:** Codacy PR report listed the regex as a high-severity security issue; GitHub Dependabot still reported the moderate esbuild advisory on the default branch until the dependency tree resolves beyond `0.24.2`. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Fix: review hardening (register errors, occasions, gift-groups, Cursor permissions) | ||
| **By:** Cursor | ||
| **What:** Register API errors are parsed via `res.text()` + safe `JSON.parse` so non-JSON bodies never throw. `updateOccasion` now blocks preset-kind duplicates the same way as `createOccasion` (excluding the current row). Gift groups: stricter `parsePence`, consistent `personId`/`wishlistItemId` validation on create, duplicate contributor detection by normalized email per group, `acceptInvite` honours pre-linked `userId`, `acceptInviteAction` preserves specific error query params, invite page renders those errors, and invite email failures log `groupId`/`contributorId` plus masked recipient instead of raw email. `.claude/settings.local.json` replaces broad `git stash`/`git checkout` wildcards with scoped allow patterns. | ||
| **Why:** Address still-valid findings from PR/code review: safer client error handling, occasion update parity with create, reduced PII in logs, tighter monetary and relational validation, and narrower local agent permissions. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Fix: gift-group contributor edits no longer preserve stale account links | ||
| **By:** Cursor | ||
| **What:** Updated `updateContributor` in `app/gift-groups/actions.ts` to treat an email change as a re-invite: it clears any prior `userId` linkage when the email changes, regenerates invite tokens/expiry, and (when an email is present) sends a fresh invite. Also switched the remaining gift-group `<form action={...}>` usages in `app/gift-groups/page.tsx` and `app/gift-groups/[id]/page.tsx` to the existing `ActionForm` wrapper to satisfy Codacy’s “promise-returning function in attribute” warning. | ||
| **Why:** Codacy flagged a security flaw where editing a contributor’s email could leave them incorrectly linked to the previous user account. Separately, Codacy was marking server actions used directly in form `action` props as error-prone; `ActionForm` already wraps these safely elsewhere. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Fix: reminder email showed empty shortlist even when wishlist items exist | ||
| **By:** Claude Code | ||
| **What:** `buildShortlistForPerson` in `lib/reminders.ts` now includes active (non-purchased/given) wishlist items directly in the shortlist, not just AI-found products and AI suggestions. Wishlist items are ordered after products but before suggestions. Added a third `ShortlistEntry` kind `"wishlist"` and updated `lib/notify/email.ts` to label them as "Wishlist" in the email. | ||
| **Why:** Users who had wishlist items but had not run the AI product search saw "No shortlist yet" in the test reminder email, even though items were present in the app. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Unit tests for fromAddress and digest email renderers | ||
| **By:** Claude Code | ||
| **What:** Added `lib/__tests__/email.test.ts` with 16 tests covering `fromAddress` (specific key, fallback, hardcoded default, double-quote strip, single-quote strip, no inner-quote strip) and both text/HTML digest renderers (no prices, no external buy links, item titles present, retailer name present, person-page link present, empty shortlist message). | ||
| **Why:** Codacy AI reviewer flagged missing test coverage for the quote-stripping logic and email renderers added in the email fix PR. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Fix sender display name and reduce promotional classification of digest emails | ||
| **By:** Claude Code | ||
| **What:** Two changes to `lib/notify/email.ts`. (1) `fromAddress()` now strips leading/trailing quote characters from the env var value before returning it, so display names like "Noted Support" survive Docker Compose .env parsing. (2) Removed external retailer URLs and prices from the birthday digest shortlist (both text and HTML renderers); the shortlist now shows item title, kind tag, and retailer name only, linking to the Noted person page for full details. Prices and buy-links are the primary signals Gmail uses to classify emails as promotional. | ||
| **Why:** User reported the digest email landed in the Promotions tab and the sender display name ("Noted Support") was not showing on the help@ password reset email. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Refactor gift-groups queries to eliminate duplication and fix Codacy HIGH | ||
| **By:** Claude Code | ||
| **What:** Extracted a module-level named `fetchGroups(where: SQL)` function from `listGiftGroups` in `lib/gift-groups-queries.ts`. The owned query now calls `fetchGroups` with the user-id condition, and the contributing/pending queries use a `fetchById` helper that delegates to `fetchGroups`. Eliminates the 9-line query duplication Codacy detected and resolves the HIGH "non-serializable expression" issue caused by the inner async arrow function. | ||
| **Why:** Codacy flagged the inner `const fetchGroups = async () =>` as a HIGH issue and detected +9 duplicate lines; both rooted in the same structural problem. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] In-app accept/decline for existing users + register link for new users | ||
| **By:** Claude Code | ||
| **What:** When an existing registered user is added as a contributor, they now get an invite token (same as unregistered users) and must explicitly accept or decline via the app rather than being auto-accepted. A new "Pending invitations" section on `/gift-groups` shows these with Accept/Decline buttons. Unregistered users now receive a "Get started" link pointing to `/login/register?callbackUrl=...` instead of the invite page. New `acceptLinkedInvite` and `declineInvitation` server actions added. | ||
| **Why:** User feedback — existing users should be able to accept or decline in-app; non-members should land on sign-up not login. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-10] Per-email-type from addresses with shared fallback | ||
| **By:** Claude Code | ||
| **What:** Added `EMAIL_FROM_REMINDERS`, `EMAIL_FROM_INVITES`, and `EMAIL_FROM_AUTH` env vars. Each email type uses its specific var if set, falling back to `EMAIL_FROM`, then to the hardcoded Resend test address. Updated `lib/notify/email.ts` (`fromAddress()` helper), `app/api/auth/forgot/route.ts`, `.env.example`, and `docker-compose.yml`. | ||
| **Why:** User wanted different from addresses per email type (e.g. `reminders@noted.dbwg2009.uk`, `help@noted.dbwg2009.uk`) after setting up their own verified domain. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix accept-before-notify race and improve email error logs | ||
| **By:** Claude Code | ||
| **What:** In `addContributor`, existing-user contributors are now inserted without `inviteAcceptedAt`; it is set only after `sendGroupGiftNotification` succeeds. If the email fails, the row stays in a re-invitable state so `resendInvite` can retry. All three `console.error` calls now include the recipient email address. | ||
| **Why:** Codacy AI flagged that setting `inviteAcceptedAt` before the email meant a failed notification left the contributor permanently stuck with no retry path. Log messages also lacked the recipient address, making them hard to act on in production. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Deduplicate invite token generation into newInvite() helper | ||
| **By:** Claude Code | ||
| **What:** Extracted `randomUUID()` + 30-day expiry into a `newInvite()` helper in `app/gift-groups/actions.ts`. Both `addContributor` and `resendInvite` now call it instead of repeating the same two lines. | ||
| **Why:** Codacy AI flagged the duplication — if the invitation policy changes (e.g. expiry window), it now only needs updating in one place. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix invite email not sending — generate UUID in application code | ||
| **By:** Claude Code | ||
| **What:** `addContributor` and `resendInvite` in `app/gift-groups/actions.ts` now generate the `inviteToken` UUID via `randomUUID()` in application code and pass it explicitly in the insert/update, rather than relying on the DB column default (`gen_random_uuid()`). | ||
| **Why:** If `db:push` had not been run after adding `.defaultRandom()` to the schema, the column had no server default, so the insert returned `null` for `inviteToken` and `sendGroupGiftInvite` was never called. Generating it in application code makes the flow migration-independent. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix useSearchParams Suspense boundary on /login/register | ||
| **By:** Claude Code | ||
| **What:** Extracted form logic into `RegisterForm` component in `app/login/register/page.tsx`, wrapped it in `<Suspense>`. The page shell remains the default export. | ||
| **Why:** Next.js requires `useSearchParams()` to be inside a Suspense boundary during static prerendering. Without it the Docker build failed with "Export encountered an error on /login/register/page". | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix Promise-returning form action Codacy flags | ||
| **By:** Claude Code | ||
| **What:** Introduced `app/gift-groups/action-form.tsx` — a thin client component wrapper around `<form>` that accepts `action: (FormData) => Promise<void>` and internally wraps it in a void-returning arrow function. Replaced the 5 flagged `<form action={serverAction}>` usages in `app/gift-groups/[id]/page.tsx` and `app/gift-groups/invite/[token]/page.tsx` with `<ActionForm action={serverAction}>`. | ||
| **Why:** Codacy flags `action={asyncFn}` as "Promise-returning function in void attribute" because it lacks React 19's type context where form `action` already accepts `Promise<void>`. Arrow wrappers directly in server components break Next.js RSC action serialization; passing the server action as a prop to a client component (which then wraps it) is the correct Next.js pattern that satisfies both the type checker and the linter. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix third-round Codacy issues on PR #115 | ||
| **By:** Claude Code | ||
| **What:** Fixed 7 new issues flagged by Codacy on the latest push. | ||
| - `eslint.config.mjs`: configured `@typescript-eslint/no-misused-promises` with `checksVoidReturn: { attributes: false }` — suppresses the "Promise-returning function in void attribute" error for server action form props. Arrow-function wrappers are not viable here (they break Next.js RSC action serialization). | ||
| - `app/login/register/page.tsx`: replaced `window.location.href = dest` with `router.push(dest)` using Next.js `useRouter` — eliminates the Codacy XSS flag on direct `location.href` assignment. | ||
| - `lib/gift-groups-queries.ts`: removed unused `or` import from drizzle-orm. | ||
| **Why:** Codacy flagged the async form actions as high-severity error-prone and the location.href assignment as a high-severity XSS risk. ESLint rule configuration is the correct fix for the server action pattern; router.push is the safer and idiomatic Next.js navigation method. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix second-round Codacy issues on PR #115 | ||
| **By:** Claude Code | ||
| **What:** Fixed two further issues flagged in Codacy's follow-up review. | ||
| - `app/gift-groups/actions.ts` (`updateContributor`): normalize email to lowercase on save — consistent with `addContributor` which already did this, prevents case-fragmented duplicates. | ||
| - `app/gift-groups/actions.ts` (`addContributor`): check for existing contributor row before inserting when the invited user already has an account — prevents a 500 crash from the unique `(groupId, userId)` index added in the previous round. | ||
| **Why:** Inconsistent email casing causes silent data fragmentation; missing duplicate check causes an unhandled constraint violation crash. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Fix Codacy and CodeRabbit issues on PR #115 | ||
| **By:** Claude Code | ||
| **What:** Fixed all actionable review issues from Codacy and CodeRabbit on the collaborative contributors PR. | ||
| - `app/gift-groups/invite/[token]/page.tsx`: removed unsafe mutation-on-GET — invite page now shows an "Accept invite" button; mutation only happens on form submit via new `acceptInviteAction`. Error states (`wrong_account`, `failed`) are surfaced via URL search param redirect. | ||
| - `app/gift-groups/actions.ts`: added `acceptInviteAction(formData)` server action that delegates to `acceptInvite` and redirects on error rather than returning to render. Fixed case-sensitive email comparison in `acceptInvite` — now normalises both sides to lowercase. | ||
| - `app/login/register/page.tsx`: validated `callbackUrl` query param starts with `/` before using it in `window.location.href` — prevents potential `javascript:` redirect XSS. | ||
| - `db/schema.ts`: added `.defaultRandom()` to `inviteToken` so new contributor rows always receive a UUID token (previously NULL, breaking invite emails). Added `uniqueIndex` on `(groupId, userId) WHERE userId IS NOT NULL` to prevent duplicate contributor rows for the same user. | ||
| - `app/gift-groups/[id]/page.tsx`: consolidated two duplicate resend-invite forms into one with conditional text/colour. | ||
| **Why:** Codacy flagged mutation-on-GET, XSS risk, and case-sensitivity bug as high severity. CodeRabbit flagged the missing invite token default and duplicate form blocks. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-09] Collaborative group gift contributors | ||
| **By:** Claude Code | ||
| **What:** Extended Phase 8 group gifts with multi-user collaboration. Contributors with accounts can see and interact with group gifts they've been added to. | ||
| - `db/schema.ts`: added `userId` (FK → users), `inviteToken` (uuid unique), `inviteExpiresAt`, `inviteAcceptedAt` columns to `gift_group_contributors`. | ||
| - `lib/gift-groups-queries.ts`: `listGiftGroups` now returns `{ owned, contributing }` split; `getGiftGroup` allows contributor access; new `getContributorByInviteToken`. | ||
| - `lib/notify/email.ts`: added `sendGroupGiftNotification` (existing users) and `sendGroupGiftInvite` (new users with invite link). | ||
| - `app/gift-groups/actions.ts`: `addContributor` now checks email against users table — links immediately + notifies if found, or generates 30-day invite token + sends invite email if not. New actions: `resendInvite`, `acceptInvite`, `leaveGroup`, `updateMyContribution`. | ||
| - `app/gift-groups/page.tsx`: split into "Groups I manage" and "Groups I'm contributing to" sections. | ||
| - `app/gift-groups/[id]/page.tsx`: shows owner controls or contributor controls (edit own amount, leave group) based on role. Contributor rows show Linked/Invite pending/Invite expired badges. | ||
| - `app/gift-groups/invite/[token]/page.tsx`: new public invite acceptance page. Handles wrong-account blocking, expired tokens, already-accepted states. | ||
| - `app/login/register/page.tsx`: forwards `callbackUrl` query param through to post-registration login redirect so invite links survive the sign-up flow. | ||
| **Why:** App is multi-user; contributors should be able to view and manage their own involvement in group gifts without relying on the organiser for everything. | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-08] Fix Codacy review issues on phase 8 bundle | ||
| **By:** Claude Code | ||
| **What:** Addressed all Codacy comments on PR #113. | ||
| - `db/schema.ts`: added `.references(() => occasions.id, { onDelete: "set null" })` FK to `wishlist_items.occasion_id`. | ||
| - `app/people/actions.ts`: extracted `resolveOccasionId()` helper — validates that the submitted `occasionId` belongs to the current user (security), uses `Number.isNaN` instead of `|| null` (correctness); added `occasionId` field to `createWishlistItem` so it can be set on creation. | ||
| - `app/people/occasion-actions.ts` + `app/settings/occasion-actions.ts`: replaced `kind as any` with `kind as OccasionKindValue` (typed union from the `occasionKind` enum). | ||
| - `app/people/[id]/wishlist-item-edit-form.tsx`: wrapped server action props in `(fd) => void action(fd)` to satisfy the form `action` prop typing. | ||
| **Why:** Codacy flagged a security gap (unverified occasionId), a parse correctness bug, an `any` cast, and a missing FK — all legitimate issues. The fixes harden the data boundary without changing visible behaviour. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-08] Phase 8 bundle — occasion-linked gifts, reminder suppression, duplicate occasion guard | ||
| **By:** Claude Code | ||
| **What:** Four features bundled into the phase-8-group-gifts branch (issues #108, #109, #110, #112). | ||
| - `db/schema.ts`: added nullable `occasion_id` integer column to `wishlist_items`; updated `wishlistItemsRelations` to include the `occasion` relation. | ||
| - `app/people/[id]/wishlist-item-edit-form.tsx` (new): client component replacing the inline edit `<details>`. Renders the update form (with occasion picker) and intercepts status → "given" to switch inline to the "Record gift" form, prompting for givenOn / pricePaid / reactionNotes before submitting to `markWishlistItemGiven`. Eliminates the need for a separate "Mark as given" button. | ||
| - `app/people/[id]/page.tsx`: imports `WishlistItemEditForm`; builds `allOccasionOptions` (person-specific + site-wide) and `occasionNameById` map; shows a violet occasion badge on each wishlist item card when linked; removed the now-redundant standalone "Mark as given" `<details>` block; removed the server-only `formatPenceInput` helper (moved into the client component). | ||
| - `app/people/actions.ts` (`updateWishlistItem`): parses `occasionId` from form data and persists it on the wishlist item. | ||
| - `app/people/occasion-actions.ts` (`createOccasion`): added duplicate-kind check — if a preset occasion of the same kind already exists for the person (or site-wide when no personId), returns with a flash error rather than inserting a duplicate. Only applies to non-custom kinds. | ||
| - `app/settings/occasion-actions.ts` (`createSiteWideOccasion`): same duplicate guard for site-wide occasions; sets a `settings_flash` cookie with an error message. | ||
| - `lib/reminders.ts`: added `allWishlistItemsDone()` helper; `buildDigestForUser` now skips people whose every wishlist item is `purchased` or `given` (no email block for them); `runDailyReminders` still marks those reminders as sent to prevent daily re-triggers; site-wide occasion path filters `finalPeople` to exclude anyone whose wishlist items linked to that specific `occasionId` are all purchased/given (people with no linked items are kept). | ||
| **Why:** #110 closes the gap where gifts couldn't be associated with a specific occasion. #108 ensures the status dropdown to "given" always triggers history capture rather than a silent status update. #109 prevents noise emails when the user has already sorted all gifts for an occasion. #112 prevents accidental duplicate reminders from duplicate preset occasions. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-08] Phase 8 — Group Gifts (v1.4.0) | ||
| **By:** Claude Code | ||
| **What:** Implemented Phase 8 of the V2 roadmap. New schema: `gift_group_status` enum, `gift_groups` table (userId, personId, wishlistItemId, occasionId, title, targetAmount, status, notes), `gift_group_contributors` table (groupId, name, email, contributionAmount, paid). New files: `lib/gift-groups-queries.ts`, `app/gift-groups/actions.ts`, `app/gift-groups/page.tsx`, `app/gift-groups/[id]/page.tsx`. Updated: `components/nav.tsx` (Groups tab), `app/people/[id]/page.tsx` (👥 Group gift button on each wishlist item). Bumped version 1.3.5 → 1.4.0. | ||
| **Why:** Phase 8 of the V2 roadmap. Closes #28. Lets users coordinate split purchases across multiple contributors — track who is chipping in, how much, and whether they've paid. Target amount optional; when set, a progress bar shows funding progress. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-08] Force postcss ≥8.5.10 via npm overrides to resolve Dependabot alert #10 (v1.3.5) | ||
| **By:** Claude Code | ||
| **What:** Added `"overrides": { "postcss": "^8.5.10" }` to `package.json`. Bumped direct devDep from `^8.4.49` → `^8.5.10` to match. Bumped package version `1.3.4` → `1.3.5`. Regenerated `package-lock.json` (drops the nested `node_modules/next/node_modules/postcss@8.4.31`). Fixes #101 (GHSA-qx2v-qp2m-jg93). | ||
| **Why:** Next.js ships an internal copy of `postcss@8.4.31`; the XSS fix (unescaped `</style>` in CSS stringify output) landed in `8.5.10`. Our top-level postcss was already fixed; the override forces the same version into Next.js's nested dep tree. | ||
|
|
||
| --- | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## [2026-05-08] Bump version to 1.3.4 and compact CHANGELOG | ||
| **By:** Claude Code | ||
| **What:** `package.json` version `1.3.3` → `1.3.4`. Archived entries from 2026-05-06 and earlier to `CHANGELOG-legacy.md` to keep the active log under 300 lines. | ||
| **Why:** Patch release covering all repo advisory improvements: Vitest unit tests, husky/commitlint hooks, Sentry error tracking, Dependabot auto-merge, Trivy container scanning, Docker memory limits, health check endpoint, npm vulnerability upgrades (Next.js, next-auth, drizzle-kit), CI concurrency groups, and release helper workflow. | ||
|
|
||
| --- |
There was a problem hiding this comment.
Archive boundary is inconsistent and currently duplicates active entries.
CHANGELOG-legacy.md now includes 2026-05-10 through 2026-05-08 entries, which are still present in CHANGELOG.md and conflict with the legacy preface cutoff (“2026-05-06 and earlier”). Please re-apply the split so legacy contains only the archived range and update the boundary statement to match the actual cutoff date.
As per coding guidelines, “When CHANGELOG.md grows past ~300 lines, the workflow .github/workflows/changelog-archive.yml opens a PR to archive old entries to CHANGELOG-legacy.md on the Development branch.”
🧰 Tools
🪛 LanguageTool
[uncategorized] ~9-~9: The official name of this software platform is spelled with a capital “H”.
Context: ...config By: Cursor What: Updated .github/workflows/release-please.yml to use ma...
(GITHUB)
[uncategorized] ~9-~9: The official name of this software platform is spelled with a capital “H”.
Context: ...h config-file / manifest-file under .github/ (removed the workflow-level `release-...
(GITHUB)
[style] ~96-~96: The noun “invitation” is usually used instead of ‘invite’ in formal writing.
Context: ...added as a contributor, they now get an invite token (same as unregistered users) and ...
(AN_INVITE)
[style] ~119-~119: To elevate your writing, try using a synonym here.
Context: ...cked the recipient address, making them hard to act on in production. --- --- #...
(HARD_TO)
[grammar] ~127-~127: Ensure spelling is correct
Context: ...eduplicate invite token generation into newInvite() helper By: Claude Code What: ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 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 `@CHANGELOG-legacy.md` around lines 7 - 281, The legacy changelog contains
entries (2026-05-08 → 2026-05-10) that are still active in CHANGELOG.md and the
preface cutoff text still reads “2026-05-06 and earlier”; remove the duplicated
active entries from CHANGELOG-legacy.md so it only holds the archived range, and
update the preface/cutoff line to the actual archive cutoff date that the
workflow `.github/workflows/changelog-archive.yml` uses (or vice‑versa: ensure
the workflow and the cutoff text agree); verify consistency between
CHANGELOG.md, CHANGELOG-legacy.md, and the workflow so only entries older than
the agreed cutoff are moved.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Summary by CodeRabbit
Documentation
Chores