docs: pre-launch repo polish#150
Conversation
* feat: Phase 8 group gifts (#130) * Bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: bump version to 1.3.3 * chore: add Vitest unit tests (repo advisory item 1) (#75) * chore: add Vitest unit tests for lib/birthdays and lib/occasions 57 tests covering date parsing, next-occurrence rollover, age calculation, money formatting, Easter algorithm, and occasion countdown logic. vitest.config.ts scopes coverage to the two tested files (85/80% thresholds). pr-checks.yml extended with a test+coverage step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: bump CI Node version to 24 to match local npm 11 lockfile package-lock.json was generated by npm 11 (Node 24 local); npm 10 (Node 20 CI) rejects it with missing esbuild entries. Aligning CI to Node 24 resolves the npm ci lockfile sync error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add husky + lint-staged + commitlint pre-commit enforcement (#77) Pre-commit hook runs ESLint via lint-staged on staged ts/tsx files. Commit-msg hook validates Conventional Commits format via commitlint. prepare script ensures hooks install automatically after npm install. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Sentry error tracking (#79) Installs @sentry/nextjs and wires up client, server, and edge configs. All three Sentry env vars are optional; init is skipped when SENTRY_DSN is unset so self-hosters who don't want Sentry are unaffected. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Dependabot auto-merge for patch and minor updates (#81) Workflow triggers on Dependabot PRs only and calls gh pr merge --auto --squash for patch/minor bumps. Major bumps stay open for manual review. CI must pass before GitHub actions on the auto-merge flag. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Trivy container image vulnerability scan (#83) Scans the app image after push using aquasecurity/trivy-action. Fails on CRITICAL severity CVEs with available fixes; uploads SARIF results to the GitHub Security tab on every run. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Docker memory limits and wire Sentry env vars (#85) Adds deploy.resources.limits.memory to all four compose services: db 512m, migrate 256m, app 512m, cron 64m. Prevents OOM killer taking down Postgres on the Pi during memory pressure. Also passes SENTRY_DSN/ORG/PROJECT through to the app container. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: bump trivy-action to v0.36.0 — 0.31.0 tag does not exist (#86) (#87) * chore: add app health check endpoint and Docker healthcheck (#89) GET /api/health returns {status:"ok"} with 200. docker-compose.yml gains a healthcheck on the app service and upgrades the cron depends_on condition to service_healthy, replacing the manual readiness poll loop in the cron entrypoint. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: strip esbuild binaries from runner image (CVE-2024-24790, CVE-2025-68121) (#91) * fix: bump trivy-action to v0.36.0 — 0.31.0 tag does not exist (#86) * fix: strip esbuild binaries from runner image to resolve CVE-2024-24790 and CVE-2025-68121 (#90) * fix: upgrade Next.js, next-auth, drizzle-kit to resolve CVEs (#93) next 15.2.9 → 15.5.18: fixes high severity SSRF, cache poisoning, HTTP request smuggling, DoS, and content injection CVEs. next-auth beta.25 → beta.31: fixes email misdelivery CVE. drizzle-kit 0.30.x → 0.31.10: reduces esbuild advisory surface. 6 moderate vulns remain in upstream transitive deps (unfixable). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add concurrency groups to pr-checks and docker-publish (#95) Cancels stale CI runs when new commits push to the same branch. Critical for docker-publish given the ~10 min multi-arch build time. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add release helper workflow (#97) * fix: add limit-severities-for-sarif to align Trivy exit code with CRITICAL-only scan * chore: add release helper workflow workflow_dispatch with tag/title/notes inputs that runs gh release create --latest. Keeps releases manual and phase-gated but removes the friction of remembering the exact CLI invocation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: bump version to 1.3.4 and compact CHANGELOG - package.json: 1.3.3 → 1.3.4 - CHANGELOG.md: archived 2026-05-06 and earlier entries to CHANGELOG-legacy.md * fix: resolve npm security vulnerabilities (Next.js CVEs + PostCSS XSS) (#102) * fix: upgrade Next.js, next-auth, drizzle-kit to resolve CVEs next 15.2.9 → 15.5.18: fixes high severity SSRF, cache poisoning, HTTP request smuggling, DoS, and content injection CVEs. next-auth beta.25 → beta.31: fixes email misdelivery CVE. drizzle-kit 0.30.x → 0.31.10: reduces esbuild advisory surface. 6 moderate vulns remain in upstream transitive deps (unfixable). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: force postcss >=8.5.10 via npm overrides to resolve Dependabot alert #10 Adds an npm overrides entry so Next.js's nested postcss@8.4.31 is replaced by the patched version. Bumps the direct devDep range to match. Closes #101. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: correct stale vulnerability count in CHANGELOG Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: resolve CHANGELOG merge conflict from rebase Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: bump version to 1.3.5 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve package-lock.json conflict markers and sync version to 1.3.5 Also updates CHANGELOG entry to mention the version bump. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add Release Please and Codecov automation (#105) * chore: add Release Please, Codecov, and Socket Security automation - Release Please workflow + config watches main branch for conventional commits and auto-opens versioning PRs (CHANGELOG + package.json bump) - Codecov upload step added to pr-checks.yml; lcov reporter added to vitest config so coverage/lcov.info is generated on each run - Socket Security is a GitHub App install (see issue #104 for link) Closes #104 * fix: pin GitHub Actions to full commit SHAs for supply-chain security * docs: add bot-comment review step to PR workflow memory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: phase 8 — group gifts (#111) * feat: phase 8 — group gifts (#28) Add group gift coordination: track contributors, amounts, and payment status for split purchases. New gift_groups and gift_group_contributors tables, /gift-groups list + detail pages, Groups nav tab, and a Group gift button on every wishlist item. Bump version 1.3.5 → 1.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address coderabbit review issues on group gifts - parsePence: reject non-finite values (Infinity, -Infinity) - updateGiftGroup: validate status against whitelist before db update - updateContributor/deleteContributor: scope WHERE to groupId and contributorId - getGiftGroup: push userId ownership into SQL WHERE clause - schema: add CHECK constraints for non-negative pence columns - detail page: extract delete confirm into a client component Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: validate personId and wishlistItemId ownership in createGiftGroup Prevent IDOR by verifying the personId and wishlistItemId passed via form data belong to the current user before inserting a new gift group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve codacy error-prone issues in gift-groups ui wrap server actions in void arrow functions to satisfy form action void return type; remove unnecessary ?? fallbacks on status maps whose keys are always present for valid enum values * refactor: extract shared gift-groups constants to reduce duplication * chore: disable coderabbit docstring coverage check * fix: add aria-label to create group gift form inputs --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: phase 8 — group gifts + bundled enhancements (#108 #109 #110 #112) (#113) * feat: phase 8 — group gifts (#28) Add group gift coordination: track contributors, amounts, and payment status for split purchases. New gift_groups and gift_group_contributors tables, /gift-groups list + detail pages, Groups nav tab, and a Group gift button on every wishlist item. Bump version 1.3.5 → 1.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address coderabbit review issues on group gifts - parsePence: reject non-finite values (Infinity, -Infinity) - updateGiftGroup: validate status against whitelist before db update - updateContributor/deleteContributor: scope WHERE to groupId and contributorId - getGiftGroup: push userId ownership into SQL WHERE clause - schema: add CHECK constraints for non-negative pence columns - detail page: extract delete confirm into a client component Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: validate personId and wishlistItemId ownership in createGiftGroup Prevent IDOR by verifying the personId and wishlistItemId passed via form data belong to the current user before inserting a new gift group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve codacy error-prone issues in gift-groups ui wrap server actions in void arrow functions to satisfy form action void return type; remove unnecessary ?? fallbacks on status maps whose keys are always present for valid enum values * refactor: extract shared gift-groups constants to reduce duplication * chore: disable coderabbit docstring coverage check * fix: add aria-label to create group gift form inputs * feat: occasion-linked gifts, reminder suppression, duplicate occasion guard (#108 #109 #110 #112) - db/schema.ts: add nullable occasion_id to wishlist_items; update relation - wishlist-item-edit-form.tsx: new client component — edit form with occasion picker; status→given intercept triggers inline Record gift form - page.tsx: integrate WishlistItemEditForm; show violet occasion badge; remove standalone Mark as given block - actions.ts: updateWishlistItem saves occasionId - occasion-actions.ts: block duplicate preset occasion kinds per person - settings/occasion-actions.ts: block duplicate preset occasion kinds site-wide - reminders.ts: skip digest blocks when all wishlist items are done; filter people from site-wide emails when linked occasion items all purchased/given Requires: npm run db:push to add occasion_id column to wishlist_items Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revert server action arrow wrappers that broke serialization anonymous arrow functions are not serializable as form actions in server components; only "use server" functions can be passed as action props — revert to direct server action references * fix: address Codacy review issues on phase 8 bundle - db/schema.ts: add FK reference on wishlist_items.occasion_id - actions.ts: resolveOccasionId helper validates ownership + fixes NaN parse; createWishlistItem now accepts occasionId - occasion-actions.ts: replace `as any` with typed OccasionKindValue - settings/occasion-actions.ts: same as above - wishlist-item-edit-form.tsx: void-wrap server action props Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: restore formatPenceInput helper removed in client component refactor Used in the person edit form (budgetMin/budgetMax fields), not only in the wishlist item edit form. Caused TS2304 in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address remaining Codacy issues on phase 8 bundle - occasion-actions.ts + settings/occasion-actions.ts: fix as any in .values() inserts (was only fixed in eq() queries previously) - page.tsx: use getKnownOccasionLabel in occasionNameById map; add occasion picker to Add item form so occasionId can be set on creation - wishlist-item-edit-form.tsx: use braces on onClick arrow; add two-step confirm before delete to prevent accidental data loss Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: apply OccasionKindValue cast to updateOccasion and updateSiteWideOccasion Missed in previous as-any sweep — only create actions were fixed, not update. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revert server action arrow wrappers causing serialization crash anonymous arrow functions cannot be serialized as form actions from server components; only "use server" functions can be passed as action props — revert to direct server action references to fix the gift-groups page crash on production * feat: collaborative group gift contributors with invite flow (#115) * feat: collaborative group gift contributors with invite flow - schema: userId, inviteToken, inviteExpiresAt, inviteAcceptedAt on gift_group_contributors - addContributor: auto-links existing accounts or sends 30-day invite email - new actions: resendInvite, acceptInvite, leaveGroup, updateMyContribution - /gift-groups: split into owned vs contributing sections - /gift-groups/[id]: owner vs contributor view with role-based controls - /gift-groups/invite/[token]: public invite acceptance page - register page: forwards callbackUrl so invite survives sign-up flow closes #114 * chore: bump version to 1.4.1 * chore: revert version to 1.4.0 * fix: address Codacy and CodeRabbit issues on PR #115 - Refactor invite page: remove acceptInvite call during render (mutation on GET). Now shows an Accept invite button; mutation happens only on form submit via new acceptInviteAction. Errors redirect via search param instead of render path. - Fix case-insensitive email comparison in acceptInvite (toLowerCase both sides). - Validate callbackUrl starts with '/' before use in register page (XSS guard). - Add defaultRandom() to inviteToken so rows always get a UUID on insert. - Add partial unique index on (groupId, userId) WHERE userId IS NOT NULL. - Consolidate duplicate resend-invite forms into one with conditional text/class. * fix: normalize email in updateContributor and guard duplicate contributor insert - updateContributor: lowercase email on save for consistency with addContributor - addContributor: check for existing (groupId, userId) row before inserting when the invited email belongs to an existing user — prevents 500 from the unique index constraint added in the previous commit * fix: replace window.location.href with router.push and remove unused import - app/login/register/page.tsx: use Next.js useRouter().push() instead of direct window.location.href assignment (eliminates Codacy XSS flag; also more correct for a Next.js SPA as it avoids a full page reload) - lib/gift-groups-queries.ts: remove unused `or` import from drizzle-orm Note: Codacy flags action={serverAction} as "Promise-returning function in void attribute" but @types/react 19 already types form action as (FormData) => void | Promise<void>, making this pattern type-safe. The flag is a false positive from Codacy's older type resolution. * fix: introduce ActionForm client wrapper to resolve Promise-in-void-attribute flags Arrow wrappers on server action props in RSC break Next.js action serialization. Passing the server action to a client component (ActionForm) which then wraps it in a void arrow internally is the correct pattern — the server action reference is serializable, the void wrapper satisfies the linter in the client context. Replaces action={deleteContributor|leaveGroup|updateMyContribution|resendInvite} in gift-groups/[id]/page.tsx and action={acceptInviteAction} in the invite page. * fix: reject protocol-relative callbackUrl values to prevent open redirect * fix: wrap useSearchParams in Suspense boundary on /login/register Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: generate inviteToken in app code to guarantee invite emails are sent (#117) * fix: generate inviteToken in application code to guarantee invite emails are sent * refactor: extract newInvite() helper to deduplicate token generation * fix: log email errors in gift group actions (#119) * fix: log email errors instead of silently swallowing them * fix: defer inviteAcceptedAt until after notification succeeds and add recipient to error logs * feat: per-email-type from addresses with shared fallback (#121) * feat: per-email-type from addresses (EMAIL_FROM_REMINDERS, EMAIL_FROM_INVITES, EMAIL_FROM_AUTH) * refactor: export fromAddress helper and centralise Resend client construction * feat: in-app accept/decline for invited users + register flow for new users (#125) * feat: in-app accept/decline for existing users and register link for new users * fix: remove dead sendGroupGiftNotification and add expiry check to acceptLinkedInvite * refactor: extract fetchGroups named function to fix Codacy HIGH and query duplication Converts the inner async arrow function `const fetchGroups = async (ids) =>` to a module-level named `async function fetchGroups(where: SQL)` that accepts a Drizzle where condition. The owned query now uses fetchGroups, eliminating the 9-line query duplication. A thin `fetchById` helper handles the empty-ids guard for contributing/pending queries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: inline fetchById to remove remaining arrow function (Codacy HIGH) The Qwik/Biome "non-serializable expression" rule flags any const arrow function. Inline the two-branch condition directly into Promise.all to eliminate the pattern entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: sender display name and digest promotional classification (#127) * feat: in-app accept/decline for existing users and register link for new users * fix: remove dead sendGroupGiftNotification and add expiry check to acceptLinkedInvite * refactor: extract fetchGroups named function to fix Codacy HIGH and query duplication Converts the inner async arrow function `const fetchGroups = async (ids) =>` to a module-level named `async function fetchGroups(where: SQL)` that accepts a Drizzle where condition. The owned query now uses fetchGroups, eliminating the 9-line query duplication. A thin `fetchById` helper handles the empty-ids guard for contributing/pending queries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: inline fetchById to remove remaining arrow function (Codacy HIGH) The Qwik/Biome "non-serializable expression" rule flags any const arrow function. Inline the two-branch condition directly into Promise.all to eliminate the pattern entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: strip env-var quotes in fromAddress and remove prices/URLs from digest email fromAddress() now strips leading/trailing quote chars so display names survive .env parsing on all Docker Compose versions. Birthday digest shortlist no longer includes external retailer URLs or prices -- these are the primary signals Gmail uses to classify email as promotional. Shortlist shows title, kind tag, and retailer name only; the person-page link in each card leads to full details. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add unit tests for fromAddress quote stripping and digest email renderers 16 tests covering: - fromAddress: specific key, EMAIL_FROM fallback, hardcoded default, double-quote strip, single-quote strip, no inner-quote stripping - renderDigestText / renderDigestHtml: no prices, no external URLs, item titles and retailer names present, person-page link, empty shortlist Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add missing birthday and birthYearKnown fields to test DigestPersonBlock TypeScript CI caught the incomplete test fixture — DigestPersonBlock requires birthday (string) and birthYearKnown (boolean). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: include wishlist items in reminder email shortlist (#129) Reminder digest emails showed "No shortlist yet" even when a person had wishlist items, because buildShortlistForPerson only pulled from the products (AI search results) and suggestions tables. Now fetches active (non-purchased/given) wishlist items and includes them as a "wishlist" kind ShortlistEntry. Priority order: AI products -> wishlist items -> AI suggestions. Email renderers updated to label them [Wishlist]. Closes #128 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address PR #130 review comments from Codacy and CodeRabbit * fix: address final PR #130 review comments (Round 2) * fix: address CodeRabbit comprehensive feedback * revert: remove PR review fixes * fix: re-apply: restore security fixes * revert: remove all PR review fixes and restore to commit 4bcb6fc * fix: revert erroneous commits and restore stable 1865c61 * chore: revert all PR review fixes to restore stable 4bcb6fc * fix: harden gift-group contributor email edits Email changes now clear stale user links, regenerate invite tokens, and re-send invites. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: harden register, occasions, and gift-group review findings Safe error parsing on register; preset duplicate check on occasion update; gift-group validation, invites, logging; narrower git allowlist. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: address Codacy security findings and esbuild advisory parsePence uses explicit parsing; register JSON guard simplified; npm overrides force esbuild ^0.25.12 (GHSA-67mh-4wv8-2f99). Co-authored-by: Cursor <cursoragent@cursor.com> * fix: satisfy Codacy object-injection rule in parsePence Co-authored-by: Cursor <cursoragent@cursor.com> * chore: wire Release Please manifest config in workflow Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore: release 1.4.0 (#131) * chore: release 1.4.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Daniel Grey <dbwg2009@gmail.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dbwg2009 <64641726+dbwg2009@users.noreply.github.com>
- 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
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR adds user-facing pre-launch documentation (README "Why Noted?" section and CODE_OF_CONDUCT.md based on Contributor Covenant v2.1), updates CHANGELOG.md with entries describing the documentation changes and earlier infrastructure work, and archives older changelog entries into CHANGELOG-legacy.md. ChangesPre-launch Documentation and Changelog Updates
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Up to standards ✅🟢 Issues
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Summary
README.md— two prose paragraphs (homelabber tone) covering self-hosting, no tracking, eBay fallback, and MIT licensedocs/demo.gifplaceholder in README so the recording can be dropped in without another commitCODE_OF_CONDUCT.md(Contributor Covenant v2.1, enforcement contact: dbwg2009@gmail.com)CONTRIBUTING.mdwere already present — left untouchedDocs-only change — no release needed per CLAUDE.md.
Closes #149
Test plan
CODE_OF_CONDUCT.mdrenders at repo rootSummary by CodeRabbit
Documentation
Chores