Skip to content
This repository was archived by the owner on Nov 7, 2026. It is now read-only.

Fernly: safety fixes + free offline guided-meditation redesign - #104

Closed
camster91 wants to merge 23 commits into
mainfrom
claude/codebase-repo-review-xfjfws
Closed

camster91 wants to merge 23 commits into
mainfrom
claude/codebase-repo-review-xfjfws

Conversation

@camster91

@camster91 camster91 commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

This PR has two parts.

Part 1 — fixes (reviewed earlier)

  • Restore clean npm ci (eslint 10 / react-dom mismatch from Dependabot); Dependabot grouping + eslint-major ignore.
  • Crisis detection gaps (idiom masking, "wanna die", curly apostrophes), region-based helplines, modal fixes.
  • Storage: honest Reset, complete export, surfaced write failures; Codex review fixes.
  • Release workflow made runnable (eas.json, gate on CI, submit by build ID).

Part 2 — free guided-meditation redesign

Spec: docs/superpowers/specs/2026-10-18-free-meditation-library-design.md · Plan: docs/superpowers/plans/2026-10-18-free-meditation-library.md

  • Free forever: no paywall, ads, account; no new network calls in app code.
  • AI chat removed (screen, API client, tests). Chat storage kept so old chats are still exported/deleted by Reset.
  • 12 guided sessions (4 meditate, 3 sleep, 3 SOS, 2-part Basics): catalog src/data/sessions.ts, scripts content/scripts/*.md, audio assets/audio/*.m4a (silent placeholders for now).
  • Audio generator scripts/generate-audio.ts (dev-only, OpenAI TTS + ffmpeg) + parser tests over every script.
  • Player (expo-audio): background + lock-screen controls, back-15s, progress; completion at ≥ 90 %, once per play-through.
  • Progress: completions in AsyncStorage (cap 1,000, wiped by Reset, in export); Insights shows all-time minutes, sessions, meditation streak.
  • Meditate tab + "Today's session" card.
  • "Need help now?" button on every tab → crisis helplines modal (now with a plain Close button and Android back, since it's only user-opened).
  • Onboarding: 18+ step with "Free forever. No ads. No account."; chat-era tone step removed.
  • Release gate: scripts/check-audio.ts runs in release.yml and blocks a release while placeholder audio is present.

Testing

  • Clean npm ci + npm run ci: typecheck, lint, format, 230 tests pass.
  • npx expo export --platform web builds; Playwright screenshots of all tabs, help modal, onboarding.
  • Each task reviewed individually + final whole-branch review; all findings fixed.
  • Not tested on a device: background/lock-screen playback, Android >3 min background, real TTS output.

Before release (owner)

  1. Review the 12 scripts in content/scripts/.
  2. Run OPENAI_API_KEY=… npx tsx scripts/generate-audio.ts sos-panic (one session first; TTS params unverified), then all; update durationSec from its output; commit audio.
  3. Device checks: silent mode, lock screen, Android background > 3 min.
  4. Re-enable the CI and Release workflows (disabled manually 2026-09-22); fill ascAppId in eas.json.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB

Two merged Dependabot PRs left the lockfile unresolvable:
- eslint 10 (#39): @react-native/eslint-config peers on eslint ^8 || ^9.
  Pin eslint back to ^9.39.5.
- react-dom 19.2.8 (#38): react-dom requires the exact same react version.
  Bump react to 19.2.8 to match.

Also group react + react-dom in Dependabot and ignore eslint majors so
this doesn't recur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Detector (src/safety/crisis.ts):
- A benign idiom anywhere in a message ("I killed it at work") used to
  return 'none' before hard patterns ran, masking real ideation in the
  same message. Now the idiom is blanked out and detection continues.
- "wanna die" was missed (pattern required "wanna to").
- Curly apostrophes (iOS default) are normalized, so "don’t want to have
  been alive" matches.

Helplines:
- Route by region first, language only when no region is given. Adds
  CA/AU/NZ/IE/AT/CH, Spain's 024, and gives es-US/fr-CA 988. Unknown
  regions get the directories, never another country's number.

Crisis modal:
- Memoize resources; a fresh array each render re-ran the effect whose
  cleanup cleared the 1.5s dismiss timer, so dismiss never completed.
- Drop canOpenURL pre-check (false for tel:/sms: on Android 11+ without
  manifest queries); openURL's rejection drives the inline error.
- Fallback text no longer tells every locale to call 988; SMS number is
  read from the resource instead of hardcoded.

Chat:
- Show the modal before any storage await, and don't stack an error
  alert on it if persistence fails.
- newId() falls back when crypto.randomUUID is unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
@camster91 camster91 changed the title fix(deps): restore clean npm ci after Dependabot bumps fix: restore npm ci + close crisis-detection and crisis-modal gaps Oct 15, 2026
…failures

- clearAll(): a failed keychain delete used to be ignored and its key
  dropped from the index, orphaning that chat forever while the app
  reported success. Failed keys now stay indexed for a retry, and
  clearAll() throws so ProfileScreen's "Couldn't complete reset" shows.
- New getAllChatSessions() reads every indexed session. Data export and
  the profile chat counter used getChats(), which returns [] after the
  legacy migration, so exports had no chat history.
- setChatSession() uses the strict write, so a failed save rejects and
  ChatScreen keeps the draft + alerts instead of silently losing it.
  (SDK 57 SecureStore enforces no size cap, per the docs, but the native
  write can still fail.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
- Gate on the same checks as CI (needs: quality).
- Drop --no-wait so `eas submit --latest` submits the build just made,
  not the previous one.
- Add eas.json: remote app versioning + autoIncrement (counter survives
  ephemeral runners), production submit profile with Play internal track.
- Write the Play service-account secret to a temp file for eas submit;
  iOS uses the ASC API key stored on EAS. Fail fast while ascAppId is
  still a placeholder.
- Remove the version-bump step that called scripts that don't exist and
  pushed to a branch the tag checkout doesn't have.
- EAS CLI ^13 -> ^25. RELEASING.md updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
@camster91 camster91 changed the title fix: restore npm ci + close crisis-detection and crisis-modal gaps fix: restore npm ci, close crisis gaps, fix reset/export, make release workflow runnable Oct 16, 2026
@camster91
camster91 marked this pull request as ready for review October 18, 2026 01:05
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T01:08:16.273133Z 0ed6fd5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ed6fd550b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/release.yml Resolved
Comment thread src/storage.ts Resolved
Comment thread src/storage.ts Resolved
Comment thread src/storage.ts Resolved
claude added 17 commits October 18, 2026 01:09
- ChatScreen: clear the single-flight session-create promise in finally,
  so one rejected create doesn't leave every later send stuck on it.
- ChatScreen: once the user message is saved, a later failure no longer
  restores the draft (a retry would save it twice); it says the reply
  couldn't be saved instead.
- getAllChatSessions(): merge in the legacy sessions getChats() returns
  when migration fails, so export/stats don't show zero chats.
- release.yml: submit by the build ID from this run instead of --latest,
  so overlapping runs can't submit the wrong build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Delete ChatScreen, the chat API client and its test, and the now-unused
newId helper. Drop the Chat tab (a11y labels now "tab N of 4"), the Today
"Open Fernly" card, the deprecated CrisisModal matchedPhrase prop, and
stale ChatScreen references (eslint override, CODEOWNERS, SECURITY.md).
Chat storage and crisis detection are kept so old chats can still be
exported and deleted by Reset. Ignore .superpowers/ in prettier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
@camster91 camster91 changed the title fix: restore npm ci, close crisis gaps, fix reset/export, make release workflow runnable Fernly: safety fixes + free offline guided-meditation redesign Oct 18, 2026
Covers the "corrupt storage" review-focus case directly (previously only
the non-array case was tested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB
Clears the critical shell-quote advisory and other fixable transitive
advisories. Remaining high advisories (node-forge, braces, image-size)
are in Expo CLI / Metro build tooling with no non-breaking fix upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Y4Z1cauBe8qpx7pDxmDmB

Copy link
Copy Markdown
Owner Author

CI status: Typecheck · Lint · Format · Test fails at the "Audit production dependencies" step (npm audit --omit=dev --audit-level=high), before typecheck/lint/tests run.

Why it isn't this PR's code: the failing advisories are in transitive dependencies of expo / react-native build tooling, published after CI was last enabled (July). main would fail the same step today.

What I pushed (d3d531b): npm audit fix (non-breaking lockfile updates). Clears the critical shell-quote advisory and most others; npm ci && npm run ci → 231 tests pass, web export builds.

Still failing (no non-breaking fix upstream):

  • node-forge (all versions) ← @expo/cli, @expo/code-signing-certificates
  • braces (all versions) ← @expo/metro → metro-file-map → micromatch
  • image-size 0.6.3–2.0.2 ← react-native → metro

These are build/dev-server tools, not code shipped in the app binary. Fixing the step needs a policy decision from the owner (e.g. lower the gate to --audit-level=critical, or make the audit step report-only).


Generated by Claude Code

@camster91 camster91 closed this Nov 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants