feat(share): Share-the-felt-change flow (ATM viral hook) #52

Merged
KnallerKalle merged 14 commits from feat/share-felt-change into dev 2026-08-13 06:55:03 +00:00
KnallerKalle commented 2026-08-11 04:25:45 +00:00 (Migrated from github.com)

Plan

Deliver the Core Platform "Share-the-felt-change flow (viral hook)" roadmap item — the ATM share hook built around the felt "Aha-moment": after a lesson the user records what they noticed (open-ended noticing, never a score/rating/grade) and can share that felt change, with factual, anonymized social proof.

Outcome

End-to-end share-the-felt-change flow:

  • src/lib/share.ts — felt-change store: idempotent upsert by (user, lesson) (immutable created_at, refreshes updated_at), distinct-user count for anonymized social proof, and a locale-stable buildShareText composer.
  • src/lib/share-client.ts — client-safe boundary (pure buildShareText + FeltChange type, no Node fs); server-only share.ts re-exports it. Fixes the Tauri static-export build break.
  • POST/GET /api/share — auth-gated write + read returning the user's note plus the anonymized count.
  • src/components/ShareSheet.tsx — open-ended noticing prompt ("Was hast du bei dieser Lektion bemerkt?"), save, Web Share with clipboard fallback.
  • Wired into AudioPlayer.tsx via a "notice/share" button next to Rest; lessonTitle threaded from LessonDetailClient.tsx.
  • share i18n namespace added to all 9 locales (de, en, es, fr, he, pt, pt-BR, ru, zh) + idempotent injection script scripts/add_share_i18n.py.

Update — 2026-07-04 (OG metadata + blackbox + TSC fix)

This PR was green on functionality but incomplete for release. Three additions:

  • OpenGraph metadata (generateMetadata in library/[id]/page.tsx): og:title/description/type=article/url/siteName + Twitter card. Server-side lesson lookup with graceful fallback. Now any shared lesson URL renders a rich preview card — the viral loop's last mile.
  • Share-API blackbox coverage: 11 new checks in blackbox_test.py — share-API POST/GET round-trip, note persistence, non-judgemental social-proof count, auth 401, blank-note 422, OG-metadata assertions. 32/32 green on local.
  • TSC fix: tsc --noEmit was failing on a ?raw import + untyped callback (P1 build risk). Added src/types/raw.d.ts ambient declaration + typed the callback. TSC now clean.

Impact on release goals

Core Platform "Share-the-felt-change flow (viral hook)" moves [ ] → [x]. Core Platform is now 4/5 done (Auth ✅, Progress persistence ✅, Share-the-felt-change ✅, Audio engine [~]). This is the release-defining viral feature: it operationalizes the felt Aha-moment as the share hook and the habit-loop "investment" leg, strengthening D1→D7 retention without strain/judgement.

Blackbox

  • python3 scripts/blackbox_test.py --target local → 32/32 green
  • python3 scripts/blackbox_test.py --target testing → 22/32 (the 10 share-API + OG failures are expected — they go green once this branch merges to dev and deploys; they test code that only exists on this branch).
  • Unit suite: 100/100 green (16 files). TSC clean.

Six-perspectives check

  • Product: Amplifies the felt Aha-moment as the share hook; OG card makes the share actually work in messengers; share-worthy by design; no dark patterns.
  • Engineering: Minimal additive diff; tests cover the new behaviour; auth-gated write path; 401/422 error states; client/server boundary guarded; TSC clean; no secrets in code/logs.
  • Psychology: Autonomy-supportive, noticing-framed; no streak/strain/guilt language; strengthens the habit-loop investment leg.
  • Biomechanics: N/A (no movement cues introduced).
  • Anatomy: N/A (no anatomical claims introduced).
  • Feldenkrais: No correction/judgement of the user; the felt change is the user's own noticing, never a score/rating/grade/completion flag; quality over quantity.

Risk

Low — additive lib + route + component + i18n keys + a button + OG metadata; no existing behaviour changed. The felt-change store uses the existing json-store pattern. Share text is composed by the locale-stable lib helper. The social-proof count is distinct-user and anonymized; it cannot inflate via re-saves and never becomes a leaderboard/streak. OG metadata is additive (new generateMetadata, no routing change).

Checklist

  • Branch off latest dev (rebased 2026-08-13: merged dev in, conflicts resolved behaviour-preservingly)
  • Conventional Commits
  • Patchlog entry appended
  • Roadmap status updated
  • No secrets / no large binaries
  • TSC clean (tsc --noEmit passes)

Note: The CI/Tauri build can take >15 min. Please review the code; CI status will populate asynchronously. This PR was created/updated by an AI agent (OpenHands) on behalf of the ATM automation.

## Plan Deliver the Core Platform **"Share-the-felt-change flow (viral hook)"** roadmap item — the ATM share hook built around the felt "Aha-moment": after a lesson the user records *what they noticed* (open-ended noticing, never a score/rating/grade) and can share that felt change, with factual, anonymized social proof. ## Outcome End-to-end share-the-felt-change flow: - `src/lib/share.ts` — felt-change store: idempotent upsert by `(user, lesson)` (immutable `created_at`, refreshes `updated_at`), distinct-user count for anonymized social proof, and a locale-stable `buildShareText` composer. - `src/lib/share-client.ts` — client-safe boundary (pure `buildShareText` + `FeltChange` type, no Node `fs`); server-only `share.ts` re-exports it. Fixes the Tauri static-export build break. - `POST/GET /api/share` — auth-gated write + read returning the user's note plus the anonymized `count`. - `src/components/ShareSheet.tsx` — open-ended noticing prompt ("Was hast du bei dieser Lektion bemerkt?"), save, Web Share with clipboard fallback. - Wired into `AudioPlayer.tsx` via a "notice/share" button next to Rest; `lessonTitle` threaded from `LessonDetailClient.tsx`. - `share` i18n namespace added to **all 9 locales** (de, en, es, fr, he, pt, pt-BR, ru, zh) + idempotent injection script `scripts/add_share_i18n.py`. ### Update — 2026-07-04 (OG metadata + blackbox + TSC fix) This PR was green on functionality but incomplete for release. Three additions: - **OpenGraph metadata** (`generateMetadata` in `library/[id]/page.tsx`): og:title/description/type=article/url/siteName + Twitter card. Server-side lesson lookup with graceful fallback. Now any shared lesson URL renders a rich preview card — the viral loop's last mile. - **Share-API blackbox coverage**: 11 new checks in `blackbox_test.py` — share-API POST/GET round-trip, note persistence, non-judgemental social-proof count, auth 401, blank-note 422, OG-metadata assertions. **32/32 green on local.** - **TSC fix**: `tsc --noEmit` was failing on a `?raw` import + untyped callback (P1 build risk). Added `src/types/raw.d.ts` ambient declaration + typed the callback. TSC now clean. ## Impact on release goals Core Platform **"Share-the-felt-change flow (viral hook)"** moves `[ ]` → `[x]`. Core Platform is now **4/5 done** (Auth ✅, Progress persistence ✅, Share-the-felt-change ✅, Audio engine [~]). This is the release-defining viral feature: it operationalizes the felt Aha-moment as the share hook and the habit-loop "investment" leg, strengthening D1→D7 retention without strain/judgement. ## Blackbox - [x] `python3 scripts/blackbox_test.py --target local` → **32/32 green** - [ ] `python3 scripts/blackbox_test.py --target testing` → 22/32 (the 10 share-API + OG failures are **expected** — they go green once this branch merges to dev and deploys; they test code that only exists on this branch). - Unit suite: **100/100 green** (16 files). TSC clean. ## Six-perspectives check - [x] **Product:** Amplifies the felt Aha-moment as the share hook; OG card makes the share actually work in messengers; share-worthy by design; no dark patterns. - [x] **Engineering:** Minimal additive diff; tests cover the new behaviour; auth-gated write path; 401/422 error states; client/server boundary guarded; TSC clean; no secrets in code/logs. - [x] **Psychology:** Autonomy-supportive, noticing-framed; no streak/strain/guilt language; strengthens the habit-loop investment leg. - [x] **Biomechanics:** N/A (no movement cues introduced). - [x] **Anatomy:** N/A (no anatomical claims introduced). - [x] **Feldenkrais:** No correction/judgement of the user; the felt change is the user's own noticing, never a score/rating/grade/completion flag; quality over quantity. ## Risk **Low** — additive lib + route + component + i18n keys + a button + OG metadata; no existing behaviour changed. The felt-change store uses the existing json-store pattern. Share text is composed by the locale-stable lib helper. The social-proof count is distinct-user and anonymized; it cannot inflate via re-saves and never becomes a leaderboard/streak. OG metadata is additive (new `generateMetadata`, no routing change). ## Checklist - [x] Branch off latest `dev` (rebased 2026-08-13: merged dev in, conflicts resolved behaviour-preservingly) - [x] Conventional Commits - [x] Patchlog entry appended - [x] Roadmap status updated - [x] No secrets / no large binaries - [x] TSC clean (`tsc --noEmit` passes) --- _Note: The CI/Tauri build can take >15 min. Please review the code; CI status will populate asynchronously. This PR was created/updated by an AI agent (OpenHands) on behalf of the ATM automation._
KnallerKalle commented 2026-08-11 10:21:25 +00:00 (Migrated from github.com)

P0 build break — fixed (client/server boundary)

The failing "Frontend + Tauri build" job was caused by a client/server boundary violation: ShareSheet.tsx ("use client") imported buildShareText from @/lib/share, which co-locates the fs-backed persistence (json-store). During the Tauri static export the API routes are stripped, but client components are still bundled — so Node's fs leaked into the client/SSR bundle and the build crashed.

Fix — split along the boundary:

  • src/lib/share-client.ts (NEW): pure, client-safe buildShareText + FeltChange type. No fs, no json-store.
  • src/lib/share.ts (server-only): keeps the fs-backed persistence and re-exports buildShareText so server code keeps one import surface.
  • src/components/ShareSheet.tsx: imports buildShareText from @/lib/share-client.
  • Added regression guard share-client-boundary.test.ts that fails if the forbidden fs/json-store import is re-added to the client module.

Verified locally (the exact failing CI step):

  • TAURI_BUILD=1 npm run build (static export) now exits 0 ✅
  • Unit suite 95/95 green (15 files), incl. the new 3-test boundary guard.
  • Blackbox on testing: 7/8 (the single failure is the pre-existing /api/lessons HTML-vs-JSON gap, unrelated to this change).

CI is re-running now. The code is reviewable; CI status will populate asynchronously.

This update was created by an AI agent (OpenHands) on behalf of the ATM automation.

### P0 build break — fixed (client/server boundary) The failing **"Frontend + Tauri build"** job was caused by a client/server boundary violation: `ShareSheet.tsx` (`"use client"`) imported `buildShareText` from `@/lib/share`, which co-locates the fs-backed persistence (`json-store`). During the Tauri static export the API routes are stripped, but client components are still bundled — so Node's `fs` leaked into the client/SSR bundle and the build crashed. **Fix — split along the boundary:** - `src/lib/share-client.ts` (NEW): pure, client-safe `buildShareText` + `FeltChange` type. No `fs`, no `json-store`. - `src/lib/share.ts` (server-only): keeps the fs-backed persistence and re-exports `buildShareText` so server code keeps one import surface. - `src/components/ShareSheet.tsx`: imports `buildShareText` from `@/lib/share-client`. - Added regression guard `share-client-boundary.test.ts` that fails if the forbidden `fs`/`json-store` import is re-added to the client module. **Verified locally (the exact failing CI step):** - `TAURI_BUILD=1 npm run build` (static export) now **exits 0** ✅ - Unit suite **95/95 green** (15 files), incl. the new 3-test boundary guard. - Blackbox on testing: **7/8** (the single failure is the pre-existing `/api/lessons` HTML-vs-JSON gap, unrelated to this change). CI is re-running now. The code is reviewable; CI status will populate asynchronously. _This update was created by an AI agent (OpenHands) on behalf of the ATM automation._
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Kalle/ATM!52
No description provided.