diff --git a/Future.md b/Future.md index ddf03ab8..9b59b301 100644 --- a/Future.md +++ b/Future.md @@ -109,7 +109,8 @@ Still open: - **Error-surfacing standardization** — BucketList/MessagesInbox/PlayHub/AnswerHistory/WeeklyRecap/Onboarding VMs swallow load errors (blank screens); adopt uiState.error + ErrorState for reads, _events snackbar for writes (ConversationViewModel pattern). - **Retention analytics** — first-answer / reveal-viewed / waiting-abandoned / notif-tap events: the one piece of the R29 retention list not built. - **Content retag** — `daily_fun_mc` rows still carry `sex='neutral'` in the asset DB; after C3's HowWell-side exclusion this is purely Desire Sync pool hygiene (benign today via the binary filter). 48/150 sexual_preferences items are non-binary configs (Wheel-only) — intended? -- **npm audit** — 9 moderate remain after the 2026-07-08 firebase-functions v7 bump (retry-request/teeny-request via @google-cloud/storage, i.e. **firebase-admin** transitive — untouched by the functions bump); revisit on the next firebase-admin major. +- **npm audit** — 9 moderate remain after the 2026-07-08 firebase-functions v7 bump (retry-request/teeny-request via @google-cloud/storage, i.e. **firebase-admin** transitive — untouched by the functions bump); revisit on the next firebase-admin major. _(Re-verified 2026-07-11: still 9 moderate, unchanged set; deliberately not running `npm audit fix` — no lockfile churn on the production backend for documented moderates.)_ +- **`scheduledOutcomesReminder` scaling cap** — the daily cron scans `couples` with a flat `.limit(200)` and no pagination (`functions/src/couples/scheduledOutcomesReminder.ts:35`), so couples beyond the first 200 silently never get 30/60/90-day outcome nudges. Fine pre-launch; **before the userbase approaches ~200 couples**, paginate with the same `orderBy(__name__)+startAfter` page loop `assignDailyQuestion` already uses (see `assignDailyQuestion.ts` PAGE_SIZE pattern). Improvement & feature ideas surfaced while QA-testing as a consumer (each works today — none are defects). diff --git a/scripts/git-hooks/pre-push b/scripts/git-hooks/pre-push new file mode 100755 index 00000000..519f89b7 --- /dev/null +++ b/scripts/git-hooks/pre-push @@ -0,0 +1,61 @@ +#!/bin/sh +# +# Pre-push hook: catch stray uncommitted work and doomed non-fast-forward pushes +# BEFORE contacting the remote, with clearer messages than git's own errors. +# +# History: an earlier version of this hook also *blocked* whenever local commits +# existed that weren't on origin (an "unpushed commits" check). That is +# self-defeating — having commits to push is what a push IS — so it rejected +# every meaningful push and forced --no-verify. Removed 2026-07-11. The useful +# intent behind it (spot surprise commits from other sessions/tools sharing this +# branch) is now served by the informational listing below, which never blocks. +# + +set -e + +echo "--- pre-push: checking for uncommitted changes ---" + +if ! git diff --quiet --exit-code; then + echo "⚠️ WARNING: You have uncommitted working-tree changes." + echo " These may be VS Code auto-saves or edits you forgot to stage." + echo " Run 'git diff' to review, then commit or stash before pushing." + echo "" + echo " To push anyway, run: git push --no-verify" + exit 1 +fi + +if ! git diff --cached --quiet --exit-code; then + echo "⚠️ WARNING: You have staged but uncommitted changes." + echo " These may be VS Code auto-stages or partial commits." + echo " Run 'git diff --cached' to review, then commit before pushing." + echo "" + echo " To push anyway, run: git push --no-verify" + exit 1 +fi + +BRANCH=$(git rev-parse --abbrev-ref HEAD) +REMOTE="origin/$BRANCH" + +if git rev-parse --verify "$REMOTE" >/dev/null 2>&1; then + # Fail fast (with clear advice) if the remote-tracking ref already has commits + # we lack — the push would be rejected as non-fast-forward anyway. This catches + # the "another session pushed first" race with a better message than git's. + # (Based on the last fetch; a race newer than that is still caught by git itself.) + BEHIND=$(git rev-list --count "$BRANCH..$REMOTE" 2>/dev/null || echo 0) + if [ "$BEHIND" -gt 0 ]; then + echo "⚠️ BLOCKED: $REMOTE has $BEHIND commit(s) you don't have — this push would be rejected." + echo " Another session/tool likely pushed first (as of your last fetch)." + echo " Run: git pull --rebase then push again." + exit 1 + fi + + # Informational only — never blocks: show exactly what this push will publish, + # so commits made by other sessions/tools on this branch get noticed. + AHEAD=$(git rev-list --count "$REMOTE..$BRANCH" 2>/dev/null || echo 0) + if [ "$AHEAD" -gt 0 ]; then + echo "--- pre-push: publishing $AHEAD commit(s): ---" + git log --oneline "$REMOTE..$BRANCH" | sed 's/^/ /' + fi +fi + +echo "--- pre-push: all checks passed ---" diff --git a/scripts/install-git-hooks.sh b/scripts/install-git-hooks.sh new file mode 100755 index 00000000..5d9d3344 --- /dev/null +++ b/scripts/install-git-hooks.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Installs the repo's tracked git hooks into .git/hooks (git does not sync hooks on clone). +# Run once after cloning: sh scripts/install-git-hooks.sh +set -e +cd "$(git rev-parse --show-toplevel)" +for hook in scripts/git-hooks/*; do + name=$(basename "$hook") + cp "$hook" ".git/hooks/$name" + chmod +x ".git/hooks/$name" + echo "installed $name" +done