chore: archive old CHANGELOG entries#136
Conversation
Up to standards ✅🟢 Issues
|
There was a problem hiding this comment.
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
| ## [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`. |
There was a problem hiding this comment.
🟡 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.
| **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. | ||
|
|
||
|
|
There was a problem hiding this comment.
⚪ 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.
* 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>
* 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>
Automated: moved oldest dated entries from
CHANGELOG.mdtoCHANGELOG-legacy.md(size limit).Merge after TypeScript & Lint is green.