Skip to content

chore: Compact CHANGELOG.MD to legacy file#139

Merged
dbwg2009 merged 3 commits into
mainfrom
Development
May 10, 2026
Merged

chore: Compact CHANGELOG.MD to legacy file#139
dbwg2009 merged 3 commits into
mainfrom
Development

Conversation

@dbwg2009
Copy link
Copy Markdown
Owner

@dbwg2009 dbwg2009 commented May 10, 2026

Summary by CodeRabbit

  • Documentation

    • Updated changelog with archived legacy entries and Phase 8 feature notes (group gifts, occasion-linked wishlist items, reminders).
  • Chores

    • Reorganized changelog and added automated archival/compaction and release/workflow updates for smoother release management.

Review Change Stack

Review Change Stack

dbwg2009 and others added 2 commits May 10, 2026 23:37
* 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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dfb7d61a-617f-4741-9dc7-86b2d2189bbf

📥 Commits

Reviewing files that changed from the base of the PR and between 7be5bc6 and a8b20df.

📒 Files selected for processing (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

The 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.

Changes

Changelog Archive & Active Update

Layer / File(s) Summary
Legacy Changelog Archive
CHANGELOG-legacy.md
Seven sections of entries dated 2026-05-10 through 2026-05-08 are inserted covering Release Please manifest wiring, security/parsing hardening (parsePence), email rendering and reminder adjustments, gift-group query refactoring, invite/contributor behavior improvements, Phase 8 Group Gifts rollout, and dependency maintenance (postcss overrides, version bump).
Active Changelog Updates
CHANGELOG.md
Five new entries document authorization header fixes in checkout workflows, bot workflow changes (upgraded peter-evans/create-pull-request and actions/setup-node), Phase 8 Group Gifts completion with automated changelog archival, Release Please manifest configuration, and Release Please token fallback setup. Previous version bump entry is removed.

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

  • dbwg2009/Noted#105: Related Release Please configuration and manifest-mode setup for release automation.

Suggested labels

chore

Poem

🐰 The changelogs dance in the archive spring,
Old tales tucked safe, new entries take wing,
Release Please flows through manifest veins,
Phase 8 gifts now flow through the reins,
Documentation blooms—our journey's refrain! 📝✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, which is a significant omission given the repository's description template requirements. Add a complete description following the repository template, including: What (brief summary), Why (related issue), Changes (key files modified), Testing (verification steps), and Notes for reviewers.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main action taken in this PR: compacting the CHANGELOG.md file by moving entries to a legacy file.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Development

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

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 10, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown

@codacy-production codacy-production Bot left a comment

Choose a reason for hiding this comment

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

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

Comment thread CHANGELOG.md
Comment on lines +210 to +242
## [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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

Comment thread CHANGELOG-legacy.md
---


## [2026-05-10] chore: wire Release Please to manifest config
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

@github-actions github-actions Bot added area: docs Markdown, design docs, README chore Version bumps, config, dependencies labels May 10, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 747e958 and 7be5bc6.

📒 Files selected for processing (2)
  • CHANGELOG-legacy.md
  • CHANGELOG.md

Comment thread CHANGELOG-legacy.md
Comment on lines +7 to +281
## [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.

---
Copy link
Copy Markdown

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

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.

Comment thread CHANGELOG.md
@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@dbwg2009 dbwg2009 merged commit 85fdef9 into main May 10, 2026
12 of 13 checks passed
@github-actions github-actions Bot added chore Version bumps, config, dependencies and removed chore Version bumps, config, dependencies labels May 10, 2026
@github-actions github-actions Bot mentioned this pull request May 10, 2026
@coderabbitai coderabbitai Bot mentioned this pull request May 14, 2026
3 tasks
@github-actions github-actions Bot mentioned this pull request May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: docs Markdown, design docs, README chore Version bumps, config, dependencies

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant