Skip to content

chore: archive old CHANGELOG entries#136

Merged
dbwg2009 merged 1 commit into
Developmentfrom
automated/changelog-archive
May 10, 2026
Merged

chore: archive old CHANGELOG entries#136
dbwg2009 merged 1 commit into
Developmentfrom
automated/changelog-archive

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Automated: moved oldest dated entries from CHANGELOG.md to CHANGELOG-legacy.md (size limit).

Merge after TypeScript & Lint is green.

@github-actions github-actions Bot requested a review from dbwg2009 as a code owner May 10, 2026 22:37
@codacy-production
Copy link
Copy Markdown

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

The PR intended to archive old changelog entries but currently introduces significant data redundancy and logic discrepancies. While Codacy reports the changes are 'up to standards', the implementation contradicts the stated goal: recent entries from 2026-05-10 were added to the legacy file rather than the oldest entries being moved.

Furthermore, the moved entries remain in the primary CHANGELOG.md, resulting in duplication. These issues, along with a factually incorrect header in the legacy file, should be resolved to maintain an accurate project history.

About this PR

  • The archival logic appears to have targeted the most recent entries (2026-05-10) for moving to the legacy file, which contradicts the goal of archiving 'older' entries. Please verify the selection logic to ensure only the oldest records are processed.

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread CHANGELOG-legacy.md
Comment on lines +7 to +28
## [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`.
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 entries (starting with 'chore: wire Release Please...') are redundant as they exist in both the active and legacy changelogs. Archival should remove entries from the source file to prevent duplication. Additionally, update the date range in the header on line 3; it currently states '2026-05-06 and earlier' but the file now contains entries up to 2026-05-10.

Comment thread CHANGELOG.md
**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.


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ LOW RISK

Nitpick: Multiple blank lines between entries are unnecessary. Use a single blank line to maintain consistency with the established format and reduce file length.

@dbwg2009 dbwg2009 merged commit 7be5bc6 into Development May 10, 2026
3 checks passed
@dbwg2009 dbwg2009 deleted the automated/changelog-archive branch May 10, 2026 22:49
dbwg2009 added a commit that referenced this pull request May 10, 2026
* chore: Sync Main to Dev (#134)

* 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>

* chore: archive old CHANGELOG entries [changelog-archive] (#136)

Co-authored-by: dbwg2009 <64641726+dbwg2009@users.noreply.github.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>
dbwg2009 added a commit that referenced this pull request May 14, 2026
* chore: Sync Main to Dev (#134)

* 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>

* chore: archive old CHANGELOG entries [changelog-archive] (#136)

Co-authored-by: dbwg2009 <64641726+dbwg2009@users.noreply.github.com>

* docs: pre-launch repo polish

- Add "Why Noted?" hook section to README (prose, homelabber tone)
- Add docs/demo.gif placeholder in README
- Add CODE_OF_CONDUCT.md (Contributor Covenant v2.1)
- README badges and CONTRIBUTING.md already present; left untouched

https://claude.ai/code/session_01R5MxTL2MTJjTecCPuW9EgR

---------

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant