Compare commits

Author SHA1 Message Date
jjbrunton 11f89b1345 chore(ios): record v1.0.6 build 45 2026-08-30 10:33:16 +01:00
jjbrunton 8804e121b6 chore(release): promote iOS compliance fix
chore(release): promote iOS compliance fix
2026-08-30 10:15:27 +01:00
jjbrunton 7b3de48dbd Merge pull request #11 from jjbrunton/task/ios-export-compliance
chore(ios): declare exempt encryption
2026-08-30 10:07:24 +01:00
jjbrunton 5ebf57bdbf Merge branch 'staging' into task/ios-export-compliance 2026-08-30 10:07:10 +01:00
jjbrunton 089e629021 docs(agents): delegate substantive repository tasks 2026-08-30 10:06:37 +01:00
jjbrunton 58b70710db chore(ios): declare exempt encryption 2026-08-30 09:58:26 +01:00
jjbrunton d645d83c47 chore(ios): record v1.0.6 build 44 2026-08-30 01:29:55 +01:00
jjbrunton 179f912170 chore(android): record v1.0.6 build 48 2026-08-30 01:05:13 +01:00
jjbrunton a604f82110 chore(release): prepare v1.0.6 2026-08-30 00:46:46 +01:00
jjbrunton ae8e72ddb5 Merge pull request #10 from jjbrunton/staging
chore(release): complete mobile release configuration
2026-08-30 00:41:09 +01:00
jjbrunton 2e4cddd1c8 fix(mobile): restore iOS SDK 54 runtime 2026-08-30 00:29:45 +01:00
jjbrunton 440f224095 docs(release): require upload confirmation 2026-08-30 00:28:14 +01:00
jjbrunton 22871d037c chore(release): configure store submissions
Route Android uploads to an internal draft and bind iOS submissions to the verified App Store Connect app. Document explicit-path local artifact submission and preserve separate release authorization.
2026-08-30 00:13:40 +01:00
jjbrunton fd38b303f0 Merge pull request #9 from jjbrunton/staging
fix(api): keep auth sync timestamps monotonic
2026-08-29 23:57:39 +01:00
jjbrunton 266233a473 fix(api): keep auth sync timestamps monotonic
Use PostgreSQL's clock for both the initial auth sync timestamp and later identity refreshes so host clock skew cannot move the marker backwards.
2026-08-29 23:45:39 +01:00
jjbrunton 387b242aea Merge pull request #8 from jjbrunton/staging
chore(release): promote staging for v1.0.6
2026-08-29 23:34:30 +01:00
jjbrunton 2bc69c2790 feat(mobile): surface pending questions
Add a conditional Home cue for partner-answered questions and route both the card and catch-up reminders into the pending answer flow. Track prompt engagement and cover hidden, visible, CTA, and notification behavior.
2026-08-29 23:24:19 +01:00
jjbrunton 9da44217f7 feat(release): add generation workflow
Add a guarded mobile release skill that synchronizes public versions, writes on-brand notes, verifies signed iOS and Android artifacts, and tags the verified release commit.
2026-08-29 23:22:35 +01:00
jjbrunton 5f77b06044 test(coverage): enforce API and app floors
Measure untouched mobile source, add regression coverage for native media paths and account operations, and enforce ratcheting API and mobile thresholds in required CI checks.
2026-08-29 22:50:07 +01:00
jjbrunton 5c973c4ac5 perf(api): bound worker pools and add telemetry
Share one configured PostgreSQL pool across worker repositories and make API and worker connection budgets explicit.

Add privacy-safe aggregate request, query, pool, sync, and outbox telemetry, a loopback-only representative load fixture, bounded worker shutdown, and focused regression and integration coverage.

Document the database-wide budget formula and defer pg_stat_statements activation to a reviewed migration and planned restart.
2026-08-29 22:43:27 +01:00
jjbrunton a13bbc3f65 docs(agents): require published completion 2026-08-29 21:59:50 +01:00
jjbrunton 10c4ae8d84 docs(agents): require isolated implementation 2026-08-29 21:58:13 +01:00
jjbrunton ac4a05d5ed chore(staging): reconcile main ancestry 2026-08-29 21:49:46 +01:00
jjbrunton c7ee8a4851 perf(app): reduce redundant data fetching
Share bounded database pools across API repositories, consolidate sync and chat queries, and make mobile refreshes cache-aware and foreground-aware.

Add focused regression coverage, client freshness documentation, and repository agent role configuration.
2026-08-29 21:47:27 +01:00
jjbrunton 9957fa43f2 feat(agents): add Claude delegation skill
Add a repository-owned Codex skill and helper for bounded Claude Code execution, model and effort selection, resumable follow-ups, and independent acceptance checks.
2026-08-29 18:29:29 +01:00
jjbrunton 502452c9e4 Merge pull request #4 from jjbrunton/staging
chore(release): promote staging to production
2026-08-29 00:12:03 +01:00
jjbrunton e6bb368bc8 Merge pull request #7 from jjbrunton/task/production-cutover-import
feat(api): harden production data import
2026-08-29 00:08:15 +01:00
jjbrunton aedba2a480 feat(api): harden production data import
Preserve live Dokploy state while importing legacy Supabase data. Add schema parity, safe relationship reconciliation, storage quarantine, and production-shaped integration coverage.
2026-08-29 00:04:11 +01:00
jjbrunton 78900a24fb ci(staging): enable direct verification
Allow agents to run required checks manually against an exact commit before pushing it directly to staging, while retaining protected status checks.
2026-08-28 20:41:18 +01:00
jjbrunton 03443fe898 chore(staging): integrate verified changes
Combine the Android API 36 release, dares loop, RevenueCat documentation correction, and daily engagement features. Resolve migration ordering and preserve both dares expiry and streak reminder operations.
2026-08-28 20:36:05 +01:00
jjbrunton b60c788362 feat(engagement): localize limits and streaks
Use reported user timezones for daily response limits, expose couple streak state and reminders, and improve swipe and live-drawing synchronization with focused tests and documentation.
2026-08-28 20:28:31 +01:00
jjbrunton 61ec3692b7 chore(android): target API 36 with Expo 54
Upgrade the mobile native toolchain and Expo dependencies for Android 16 compatibility while preserving web and admin runtime isolation.
2026-08-28 20:28:31 +01:00
jjbrunton 1482305810 Merge remote-tracking branch 'origin/staging' into task/android-api-36 2026-08-28 20:22:16 +01:00
jjbrunton 8b37569818 Merge pull request #5 from jjbrunton/feat/dares-loop
feat(dares): ship the dares loop to mobile
2026-08-28 20:18:11 +01:00
jjbrunton 2178750d07 chore(android): bump version code to 47 2026-08-28 20:17:19 +01:00
jjbrunton 028e21e54c Merge origin/staging (Expo 54 / Android API 36) into feat/dares-loop 2026-08-28 20:12:55 +01:00
jjbrunton b24a402b4c Merge pull request #3 from jjbrunton/task/android-api-36
chore(android): target API 36 with Expo 54
2026-08-28 20:09:20 +01:00
jjbruntonandClaude Opus 5 ce0b188776 feat(dares): ship the dares loop to mobile
Completes the half-built dares feature: send a dare, respond, report done,
confirm, and get a push notification on every transition.

Backend (apps/api):
- New `dares` domain with couple-shared premium entitlement, a free weekly
  send allowance, and server-enforced actor/state rules on every transition.
- `submitted` status so the recipient reports completion and the sender
  confirms, rather than the sender guessing.
- Notifications ride the existing operations outbox via a trigger on
  `sent_dares`. Push copy is generic; store compliance forbids dare text in a
  preview and a test asserts no payload leaks it.
- Worker expiry sweep now covers `pending` as well as `active`/`submitted`;
  an invitation nobody opened previously stayed open forever.

Fixes two latent defects:
- Deleting any dare that had been sent aborted with a check violation, because
  `dare_id` is ON DELETE SET NULL while `sent_dares_dare_or_custom_check`
  required it. Dare text is now snapshotted at send time, which also keeps a
  couple's history stable across catalogue edits.
- The free send allowance was raceable: concurrent requests all read the same
  count and every one succeeded. The count and insert are now serialized on
  the sender's profile row.

Also ports `content_status` review metadata to the API catalogue, which had no
store-safety gating at all, and mirrors the review decision already enforced on
the legacy catalogue.

Mobile: dares screen (active/send/history), send sheet with duration presets,
home tile badging incoming dares, paywall on both proactive and 402 paths.

Verified: lint, typecheck, 379 unit tests, build, 71 integration tests against
local PostgreSQL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 20:03:11 +01:00
jjbrunton f35114dc77 chore(android): target API 36 with Expo 54
Upgrade the mobile runtime and tracked Android project to compile and target API 36. Align Expo modules, React Native, Node, Gradle, and Android build tooling while preserving the custom widget sources.\n\nAdapt file-system, notification, animation, test, and Metro compatibility for the SDK 54 runtime.
2026-08-28 20:01:15 +01:00
jjbruntonandClaude Opus 5 aacb4f8731 docs(subscription): correct stale Android RevenueCat status
The Platform Support section claimed Android was "not yet implemented".
Android has full native SDK support: getApiKey() selects the Android key,
com.android.vending.BILLING is declared in app.json, the paywall has no
platform gating, and the webhook passes event.store through unchanged so
PLAY_STORE events need no separate handling.

Also flag the entitlement ID mismatch between the "pro" fallback in
revenuecat.ts and the "Sauci Pro" value set in eas.json. A build profile
missing that variable yields isProUser: false with no error, which
presents as churn rather than as misconfiguration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 19:26:53 +01:00
jjbrunton 1be54175e4 Merge pull request #2 from jjbrunton/staging
chore(release): promote mobile build hardening
2026-08-28 18:44:58 +01:00
jjbrunton a7470310ae Merge pull request #1 from jjbrunton/task/mobile-release-hardening
chore(release): harden mobile build workflow
2026-08-28 18:39:14 +01:00
jjbrunton 3acadeff7c chore(release): harden mobile build workflow
Promote the reproduced release lessons into harness enforcement: keep tracked native regeneration separate from artifact builds, separate Android build and upload authorization, use stable login readiness in Maestro, and retain the iOS 43 and Android 46 version increments.
2026-08-28 18:34:00 +01:00
jjbrunton 91544629fa fix(worker): keep polling process alive 2026-08-28 15:11:12 +01:00
jjbrunton 32f5214751 fix(backend): allow deferred provider integrations 2026-08-28 15:00:57 +01:00
jjbrunton 96882642c7 fix(ci): start healthcheck-free worker separately 2026-08-28 14:56:53 +01:00
jjbrunton a455325147 ci(deploy): establish staged Dokploy promotion 2026-08-28 14:54:12 +01:00
jjbrunton 2dad7aa352 feat(admin): serve response media from standalone API 2026-08-28 14:54:07 +01:00
jjbrunton be20391788 Merge remote-tracking branch 'origin/master' into task/self-hosted-backend
# Conflicts:
#	apps/mobile/app/(app)/onboarding.tsx
#	apps/mobile/src/__tests__/stores/packsStore.test.ts
#	apps/mobile/src/features/profile/hooks/useProfileSettings.ts
#	apps/mobile/src/store/packsStore.ts
2026-08-28 14:44:51 +01:00
jjbrunton 006761d3b0 feat(backend): add standalone data plane
Replace the Supabase application data plane with the Node/PostgreSQL API, migrate mobile, admin, web, and MCP callers, add controlled cutover tooling, and define hardened Dokploy infrastructure.
2026-08-28 14:42:11 +01:00
jjbrunton 7a0de45482 feat(mobile): align store-safe experience
Remove client-side adult-content preferences and badges so the centrally reviewed catalogue is the single visibility boundary.\n\nUse neutral relationship-focused copy across onboarding, discovery, paywall, and login surfaces, and retain biometric controls under App Lock for both native platforms.
2026-08-28 14:36:33 +01:00
jjbrunton 72e031eb84 fix(content): keep catalogue migration portable
Classify every environment through the immutable allowlist and archive unknown concurrent additions. Remove the remote-project dependency while retaining fail-closed postconditions.
2026-08-28 13:24:43 +01:00
jjbrunton 2ab976ec42 fix(content): scope production catalogue guard
Keep exact audited-count assertions for the production project while allowing local and non-production fixture catalogues to be classified by the same immutable allowlist.
2026-08-28 13:21:35 +01:00
jjbrunton c0650ed5b7 feat(content): enforce reviewed catalogue
Hide archived and unreviewed catalogue content from existing clients through RLS and guarded recommendation RPCs. Reject stale writes, preserve historical rows, and seed reviewed defaults when the cutover removes a couple's full active selection.
2026-08-28 13:15:33 +01:00
jjbrunton 94c76535a4 feat(content): add catalogue review metadata
Add reversible review states, audit history, and super-admin controls for catalogue entities without changing current client visibility.

Stage 30 high-risk production packs for archival review, cap generated content at non-explicit language, and preserve a checksummed pre-change production catalogue snapshot.
2026-08-28 11:32:55 +01:00
jjbrunton b43804d8fd fix(redemption): show custom validation error 2026-08-27 22:57:27 +01:00
jjbrunton 79862dac13 fix(redemption): restore profile email on signup 2026-08-27 22:48:57 +01:00
jjbrunton bf2ef59688 fix(supabase): allow public code redemption
Mark redeem-code as verify_jwt=false in local configuration, matching its intentional unauthenticated email-and-code flow and production deployment settings.
2026-08-27 22:35:54 +01:00
jjbrunton d869592a8b fix(tests): narrow redeemed profile
Assert that the profile lookup returned a row before reading premium status so Deno strict type checking can execute the Supabase integration suite.
2026-08-27 22:30:39 +01:00
jjbrunton d43ae1ef05 fix(database): enable pg_net extension
Install the extension required by notification triggers and cron jobs so fresh local and preview databases expose net.http_post before fixtures are seeded.
2026-08-27 22:26:09 +01:00
jjbrunton 024dc4d288 fix(database): defer cron URL resolution
Resolve each Edge Function URL when its cron job executes so fresh local databases can replay migrations before environment data is seeded. Missing configuration remains fail-closed instead of aborting database bootstrap.
2026-08-27 22:21:28 +01:00
jjbrunton 04dee69888 fix(ci): scope Terraform deployments
Skip Terraform for function-only Supabase releases to avoid unrelated provider drift failures. Continue applying infrastructure for terraform path changes or explicit manual deployment requests.
2026-08-27 22:08:08 +01:00
jjbrunton 650c81856f feat(admin): preview response media
Show who-likely choices in human terms and provide protected photo and audio previews in activity and user response views. Fetch media by response ID through an authenticated Edge Function that enforces the view_responses permission.
2026-08-27 22:02:06 +01:00
jjbrunton 7545289dd3 fix(admin): use npm workspace in Docker
Align admin production and development images with the repository's committed npm workspace and lockfile. This ensures shared and Expo-linked dependencies are installed before the admin build runs.
2026-08-27 21:49:42 +01:00
jjbrunton 6df9442cd0 fix(admin): support typed question responses
Render database-backed text, audio, photo, and choice responses across admin activity and user detail views. Add support for the both_answered match state while retaining legacy swipe behavior.
2026-08-27 21:37:34 +01:00
jjbrunton 01bb8225ae chore(harness): make repository agent-ready
Add concise routed context, task-aware agent roles, focused skills, and structured learning promotion.\n\nUnify local development and workspace tooling, add layered test and E2E verification, harden MCP authentication, and gate deployment behind verification and explicit production promotion.
2026-08-27 20:58:48 +01:00
581 changed files with 87525 additions and 39903 deletions

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
---
id: YYYY-MM-DD-short-slug
date: YYYY-MM-DD
status: inbox
scope: path or workflow
evidence: command, test, or artifact
review_after: YYYY-MM-DD
---
## Symptom
Observable failure or friction.
## Reproduction
Exact bounded reproduction.
## Cause
Confirmed cause, not a hypothesis.
## Remediation
Proven correction.
## Evidence
Read-back, test, or other verification.
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,34 @@
---
id: 2026-08-27-migration-context-conflict
date: 2026-08-27
status: promoted
scope: database migration instructions
evidence: root and scoped agent context comparison
destination: AGENTS.md and scripts/lint-harness.mjs
review_after: 2027-02-27
---
## Symptom
Root instructions prohibited remote migration application while scoped Supabase
instructions authorized it.
## Reproduction
Compare the former root `AGENTS.md` migration rule with the former
`apps/supabase/agents.md` migration section.
## Cause
Runtime-specific instruction files evolved independently without an executable
consistency check.
## Remediation
Make root context authoritative, scope only additional local rules, and reject
positive `apply_migration` instructions in the harness lint.
## Evidence
`npm run lint:harness` checks instruction consistency and the authoritative
migration location.
@@ -0,0 +1,37 @@
---
id: 2026-08-28-maestro-stable-login-readiness
date: 2026-08-28
status: promoted
scope: native Auth-shell acceptance flows
evidence: iOS simulator login-shell acceptance on current main
destination: e2e/maestro
review_after: 2027-02-28
---
## Symptom
The credential-free native smoke failed even though the current app reached the
login screen and displayed its required controls.
## Reproduction
Run `e2e/maestro/login-screen-smoke.yaml` against current main. The flow waited
for the retired tagline `Explore intimacy together`; the app displayed updated
marketing copy while `Sauci` and `Password` were present.
## Cause
Login readiness was coupled to mutable marketing copy rather than a stable
required control.
## Remediation
Wait for the password control that defines login readiness, while retaining the
brand and password assertions. Apply the same readiness condition to the
authenticated and credential-free flows.
## Evidence
Simulator read-back confirmed the current login shell exposes `Sauci` and
`Password`. The maintained Maestro flows now wait for that stable control rather
than the tagline.
@@ -0,0 +1,40 @@
---
id: 2026-08-28-mobile-release-source-integrity
date: 2026-08-28
status: promoted
scope: mobile release build and upload workflow
evidence: clean-source local EAS builds for iOS 43 and Android 46
destination: scripts/lint-harness.mjs
review_after: 2027-02-28
---
## Symptom
A build-only request initially regenerated tracked native projects. The generated
Android widget provider replaced the checked-in implementation, and the Android
release script would also have uploaded after building.
## Reproduction
Run the former mobile release scripts or run `expo prebuild` before a local EAS
build, then inspect `git diff`. The reproduced diff included the Android widget
provider, widget XML, and Xcode project rather than version metadata alone.
## Cause
The release scripts combined three independently reviewed operations: native
regeneration, artifact creation, and store upload. Sauci already tracks native
projects, and EAS skips prebuild when those directories exist.
## Remediation
Keep native regeneration explicit, make release scripts artifact-only, expose
upload separately, and lint the scripts so those responsibilities cannot be
silently recombined.
## Evidence
Corrected local EAS logs reported `PREBUILD` skipped. Both production artifacts
built successfully from the clean tracked sources; the post-build worktree
contained only the four expected build-number files. `npm run lint:harness`
enforces the release-script boundary.
+6 -3
View File
@@ -40,7 +40,8 @@ If the story details are empty or missing, STOP and report that the PRD story fo
- Implement completely; no placeholders or stubs.
- If No-commit is true, do NOT commit or push changes.
- Do NOT edit the PRD JSON (status is handled by the loop).
- All changes made during the run must be committed (including updates to progress/logs).
- Commit only task-owned source, tests, and maintained documentation. Runtime logs
and evidence remain ignored.
- Before committing, perform a final **security**, **performance**, and **regression** review of your changes.
## Your Task (Do this in order)
@@ -60,7 +61,7 @@ If the story details are empty or missing, STOP and report that the PRD story fo
- **Performance:** check for avoidable regressions (extra queries, heavy loops, unnecessary re-renders).
- **Regression:** verify existing behavior that could be impacted still works.
10. If No-commit is false, commit changes using the `$commit` skill.
- Stage everything: `git add -A`
- Stage only the files owned by this story; never blanket-stage unrelated work.
- Confirm a clean working tree after commit: `git status --porcelain` should be empty.
- After committing, capture the commit hash and subject using:
`git show -s --format="%h %s" HEAD`.
@@ -103,7 +104,9 @@ Otherwise, end normally without the signal.
- When authoring documentation, capture the why (tests + implementation intent).
- If you learn how to run/build/test the project, update {{AGENTS_PATH}} briefly (operational only).
- Keep AGENTS operational only; progress notes belong in {{PROGRESS_PATH}}.
- If you hit repeated errors, log them in {{ERRORS_LOG_PATH}} and add a Sign to {{GUARDRAILS_PATH}} using {{GUARDRAILS_REF}} as the template.
- If you reproduce a reusable failure, record it using
`.agents/learnings/TEMPLATE.md`. Promote it only after review into a maintained
doc, skill, fixture, or executable invariant; do not add speculative Signs.
## Activity Logging (Required)
Log major actions to {{ACTIVITY_LOG_PATH}} using the helper:
-73
View File
@@ -1,73 +0,0 @@
# Agents Documentation
This file contains important project-specific context for Claude Code agents working on this repository.
## Redemption Code System
**IMPORTANT: Codes can only be redeemed on the website, NOT in the mobile app.**
Apple's App Store guidelines require that any premium features purchased within an iOS app must go through In-App Purchase. Adding a redemption code feature to the mobile app would violate these guidelines and risk app rejection.
### Overview
Promotional codes that grant users premium access without going through RevenueCat/App Store subscription flow.
### Database Schema
**`redemption_codes` table:**
- `id` (UUID) - Primary key
- `code` (TEXT, UNIQUE) - The redemption code
- `description` (TEXT) - Optional description
- `max_uses` (INTEGER) - Maximum number of redemptions allowed
- `current_uses` (INTEGER) - Current number of redemptions
- `expires_at` (TIMESTAMPTZ) - Optional expiration date
- `is_active` (BOOLEAN) - Whether the code can be redeemed
- `created_by` (UUID) - Admin who created the code
- `created_at` (TIMESTAMPTZ)
- `updated_at` (TIMESTAMPTZ)
**`code_redemptions` table:**
- `id` (UUID) - Primary key
- `code_id` (UUID) - Reference to redemption_codes
- `user_id` (UUID) - User who redeemed the code
- `redeemed_at` (TIMESTAMPTZ)
- UNIQUE constraint on (code_id, user_id) - Each user can only redeem a code once
### Components
1. **Admin UI** (`apps/admin/src/pages/RedemptionCodesPage.tsx`)
- Super admins can generate codes
- View all codes with usage stats
- Toggle active/inactive
- View who redeemed each code
- Delete codes
2. **Edge Function** (`apps/supabase/functions/redeem-code/index.ts`)
- **Unauthenticated** - accepts email + code in request body
- Calls the `redeem_code_by_email()` database function
- Returns success/error response
3. **Database Function** (`redeem_code_by_email(p_email TEXT, p_code TEXT)`)
- Atomic operation with row locking
- Looks up user by email (must have signed up in app first)
- Validates: code exists, is active, not expired, has uses remaining
- Checks user hasn't already redeemed
- Records redemption in `code_redemptions`
- Increments `current_uses`
- Sets `profiles.is_premium = true`
4. **Web UI** (`apps/web/app/redeem/page.tsx`)
- Public page at `/redeem`
- User enters email + redemption code
- Calls edge function via fetch
- Shows success or error message
### Environment Variables
The web app requires:
- `NEXT_PUBLIC_SUPABASE_URL` - Supabase project URL for API calls
### DO NOT
- **DO NOT** add redemption code UI to the mobile app
- **DO NOT** create any in-app mechanism to bypass App Store purchases
- **DO NOT** expose the redeem endpoint in mobile app Supabase calls
+10
View File
@@ -0,0 +1,10 @@
---
name: architecture-reviewer
description: Review cross-cutting or hard-to-reverse architecture decisions. Use only when multiple system boundaries or long-lived contracts change.
tools: Read, Bash, Glob, Grep
model: opus
---
Evaluate the proposed decision against current boundaries, migration path,
operability, reversibility, and verification. Prefer the smallest coherent change.
Return trade-offs and a recommendation; do not implement.
+11
View File
@@ -0,0 +1,11 @@
---
name: database-reviewer
description: Review migrations, RLS, Edge Functions, and query boundaries. Use for database-affecting diffs, not ordinary UI changes.
tools: Read, Bash, Glob, Grep
model: sonnet
---
Read `apps/supabase/AGENTS.md` and the migration safety document. Review local
migration tracking, idempotency, authorization/RLS, environment portability,
rollback implications, types, and integration coverage. Never mutate a remote
database. Return prioritized, line-specific findings.
-32
View File
@@ -1,32 +0,0 @@
---
name: frontend-developer
description: Frontend development specialist for React applications and responsive design. Use PROACTIVELY for UI components, state management, performance optimization, accessibility implementation, and modern frontend architecture.
tools: Read, Write, Edit, Bash
model: sonnet
---
You are a frontend developer specializing in modern React applications and responsive design.
## Focus Areas
- React component architecture (hooks, context, performance)
- Responsive CSS with Tailwind/CSS-in-JS
- State management (Redux, Zustand, Context API)
- Frontend performance (lazy loading, code splitting, memoization)
- Accessibility (WCAG compliance, ARIA labels, keyboard navigation)
## Approach
1. Component-first thinking - reusable, composable UI pieces
2. Mobile-first responsive design
3. Performance budgets - aim for sub-3s load times
4. Semantic HTML and proper ARIA attributes
5. Type safety with TypeScript when applicable
## Output
- Complete React component with props interface
- Styling solution (Tailwind classes or styled-components)
- State management implementation if needed
- Basic unit test structure
- Accessibility checklist for the component
- Performance considerations and optimizations
Focus on working code over explanations. Include usage examples in comments.
+11
View File
@@ -0,0 +1,11 @@
---
name: implementation
description: Implement a bounded Sauci product or harness task. Default build agent for ordinary changes after scope and acceptance criteria are known.
tools: Read, Write, Edit, Bash, Glob, Grep
model: sonnet
---
Read root and nearest scoped `AGENTS.md`. Implement only the requested behavior,
update the maintained contract documentation, and add focused tests. Use the
repository skills for database, local-stack, simulator, and verification
procedures. Stop for missing authority or a materially ambiguous product choice.
-104
View File
@@ -1,104 +0,0 @@
# Pack Generator Agent
A specialized agent for generating Sauci question packs.
## Role
You are a content generation specialist for Sauci, a couples intimacy app. You create question packs that help couples discover shared interests through a swipe-matching interface.
## Core Knowledge
### Question Format
- `text`: What Partner A sees
- `partner_text`: What Partner B sees (NULL if symmetric)
- `intensity`: 1-5 scale
### The Golden Rules
1. **Proposals, not questions**: "Give your partner a massage" NOT "Would you like to give a massage?"
2. **Always "your partner"**: Never him/her/he/she or gendered terms
3. **No time words**: Never tonight/now/today/right now
4. **Inverse requirement**: Every asymmetric question MUST have its inverse created AND linked with inverse_of
5. **Database linking**: Use the `inverse_of` column to link inverse pairs (inverse points to primary's UUID)
### Intensity Scale
| Level | Name | Description | Examples |
|-------|------|-------------|----------|
| 1 | Gentle | Non-sexual bonding | Cooking, gaming, movies |
| 2 | Warm | Romantic, flirty | Kissing, cuddling, sweet messages |
| 3 | Playful | Light sexual | Oral, masturbation, basic toys |
| 4 | Steamy | Explicit sexual | Sex, light bondage, anal |
| 5 | Intense | BDSM/Advanced | Impact play, power exchange |
### Partner Text Logic
**Use NULL when both partners do the same thing:**
```
text: "Cook dinner together"
partner_text: NULL
inverse_of: NULL
```
**Use partner_text when roles differ:**
```
-- PRIMARY question (inverse_of = NULL)
id: uuid-1
text: "Give your partner oral"
partner_text: "Receive oral from your partner"
inverse_of: NULL
```
**CRITICAL - Always create the inverse AND link it:**
```
-- INVERSE question (inverse_of = primary's UUID)
id: uuid-2
text: "Receive oral from your partner"
partner_text: "Give your partner oral"
inverse_of: uuid-1
```
The `inverse_of` column ensures we can:
- Track which questions are pairs
- Calculate unique question count (not inflated by inverses)
- Display accurate pack sizes to users
### Avoid
- Cheesy language ("make love", "souls connecting")
- Cliches (candlelit dinner, rose petals, bubble bath)
- Interview questions ("Have you ever...", "Would you...")
- Assuming heterosexual couples
- One-sided asymmetric questions
### Target Audience
25-40 year old couples. Modern, realistic, not preachy. Think about what actual couples do and say.
## Generation Process
1. **Understand the brief**: Category, intensity range, pack position in progression
2. **Research existing content**: Check what packs exist, avoid overlap
3. **Brainstorm themes**: What specific activities fit this pack?
4. **Generate questions**: Mix symmetric and asymmetric, create all inverses with inverse_of links
5. **Review**: Check all rules, remove duplicates, verify intensity, confirm inverse pairs are linked
6. **Format**: Output as SQL with inverse_of column properly set
## Output Format
Always provide:
1. Pack metadata (name, description, category, premium status)
2. Questions in SQL INSERT format
3. Summary statistics (total, by intensity, symmetric vs asymmetric)
## Invocation
This agent should be used when:
- Creating new question packs
- Expanding existing categories
- Reviewing/improving existing pack content
- Planning category progression structures
-112
View File
@@ -1,112 +0,0 @@
---
name: prompt-engineer
description: Expert prompt optimization for LLMs and AI systems. Use PROACTIVELY when building AI features, improving agent performance, or crafting system prompts. Masters prompt patterns and techniques.
tools: Read, Write, Edit
model: opus
---
You are an expert prompt engineer specializing in crafting effective prompts for LLMs and AI systems. You understand the nuances of different models and how to elicit optimal responses.
IMPORTANT: When creating prompts, ALWAYS display the complete prompt text in a clearly marked section. Never describe a prompt without showing it.
## Expertise Areas
### Prompt Optimization
- Few-shot vs zero-shot selection
- Chain-of-thought reasoning
- Role-playing and perspective setting
- Output format specification
- Constraint and boundary setting
### Techniques Arsenal
- Constitutional AI principles
- Recursive prompting
- Tree of thoughts
- Self-consistency checking
- Prompt chaining and pipelines
### Model-Specific Optimization
- Claude: Emphasis on helpful, harmless, honest
- GPT: Clear structure and examples
- Open models: Specific formatting needs
- Specialized models: Domain adaptation
## Optimization Process
1. Analyze the intended use case
2. Identify key requirements and constraints
3. Select appropriate prompting techniques
4. Create initial prompt with clear structure
5. Test and iterate based on outputs
6. Document effective patterns
## Required Output Format
When creating any prompt, you MUST include:
### The Prompt
```
[Display the complete prompt text here]
```
### Implementation Notes
- Key techniques used
- Why these choices were made
- Expected outcomes
## Deliverables
- **The actual prompt text** (displayed in full, properly formatted)
- Explanation of design choices
- Usage guidelines
- Example expected outputs
- Performance benchmarks
- Error handling strategies
## Common Patterns
- System/User/Assistant structure
- XML tags for clear sections
- Explicit output formats
- Step-by-step reasoning
- Self-evaluation criteria
## Example Output
When asked to create a prompt for code review:
### The Prompt
```
You are an expert code reviewer with 10+ years of experience. Review the provided code focusing on:
1. Security vulnerabilities
2. Performance optimizations
3. Code maintainability
4. Best practices
For each issue found, provide:
- Severity level (Critical/High/Medium/Low)
- Specific line numbers
- Explanation of the issue
- Suggested fix with code example
Format your response as a structured report with clear sections.
```
### Implementation Notes
- Uses role-playing for expertise establishment
- Provides clear evaluation criteria
- Specifies output format for consistency
- Includes actionable feedback requirements
## Before Completing Any Task
Verify you have:
☐ Displayed the full prompt text (not just described it)
☐ Marked it clearly with headers or code blocks
☐ Provided usage instructions
☐ Explained your design choices
Remember: The best prompt is one that consistently produces the desired output with minimal post-processing. ALWAYS show the prompt, never just describe it.
-98
View File
@@ -1,98 +0,0 @@
---
name: react-native-reviewer
description: "Use this agent when you need to review React Native code for best practices, component architecture, performance optimization, and maintainability. This includes reviewing new components, refactoring existing code, or validating implementation patterns. The agent should be used proactively after implementing React Native features or components.\\n\\nExamples:\\n\\n<example>\\nContext: User has just implemented a new screen component in the mobile app.\\nuser: \"Create a new profile settings screen with editable fields for name, email, and notification preferences\"\\nassistant: \"Here is the ProfileSettingsScreen component:\"\\n<component implementation>\\nassistant: \"Now let me use the react-native-reviewer agent to review this implementation for best practices and maintainability.\"\\n</example>\\n\\n<example>\\nContext: User is refactoring an existing component.\\nuser: \"Can you refactor the QuestionCard component to be more reusable?\"\\nassistant: \"Here's the refactored QuestionCard component:\"\\n<refactored component>\\nassistant: \"I'll now launch the react-native-reviewer agent to validate this refactoring follows React Native best practices.\"\\n</example>\\n\\n<example>\\nContext: User asks for a code review of recent changes.\\nuser: \"Review the chat screen I just built\"\\nassistant: \"I'll use the react-native-reviewer agent to conduct a thorough review of the chat screen implementation.\"\\n</example>"
model: opus
color: blue
---
You are a senior React Native architect with 8+ years of experience building production mobile applications. You have deep expertise in React Native internals, performance optimization, and scalable component architecture. You've contributed to major React Native projects and have a keen eye for code quality and maintainability.
## Your Review Focus Areas
### 1. Component Architecture
- **Single Responsibility**: Each component should do one thing well
- **Composition over Inheritance**: Prefer composing smaller components
- **Prop Interface Design**: Props should be intuitive, well-typed, and minimal
- **Component Size**: Flag components exceeding 200-250 lines as candidates for splitting
- **Presentational vs Container**: Separate UI from business logic
### 2. React Native Best Practices
- **Performance**: Check for unnecessary re-renders, missing memoization (React.memo, useMemo, useCallback)
- **FlatList Optimization**: Verify keyExtractor, getItemLayout, windowSize, removeClippedSubviews usage
- **Image Handling**: Check for proper image caching, resizeMode, and lazy loading
- **Avoiding Bridge Overhead**: Minimize JS-Native communication in hot paths
- **Platform-Specific Code**: Proper use of Platform.select() and .ios.tsx/.android.tsx files
### 3. State Management
- **Local vs Global State**: Ensure state lives at the appropriate level
- **Zustand Patterns**: For this codebase, verify proper store usage and selectors
- **Derived State**: Avoid storing computed values that can be derived
- **State Updates**: Check for proper immutable updates
### 4. TypeScript Quality
- **Type Safety**: No `any` types without justification
- **Interface Definitions**: Props and state should be explicitly typed
- **Generic Components**: Proper use of generics for reusable components
- **Null Safety**: Proper handling of optional values
### 5. Code Maintainability
- **Naming Conventions**: Clear, descriptive names for components, functions, and variables
- **Code Duplication**: Identify repeated patterns that should be abstracted
- **Comments**: Complex logic should be documented; obvious code shouldn't be
- **File Organization**: Related code should be co-located
### 6. Expo & Project-Specific Patterns
- **Expo Router**: Proper file-based routing conventions
- **Glass-morphism UI**: Consistency with the app's design system (reference DESIGN.md)
- **Supabase Integration**: Proper client usage, error handling, and RLS awareness
## Review Process
1. **Read DESIGN.md first** if reviewing UI components (apps/mobile/DESIGN.md)
2. **Identify the component's purpose** and primary responsibilities
3. **Check imports and dependencies** for unnecessary or heavy packages
4. **Analyze the component structure** from top to bottom
5. **Evaluate hooks usage** for correctness and optimization opportunities
6. **Review render logic** for performance and readability
7. **Check styling approach** for consistency with the design system
8. **Assess error handling** and edge cases
9. **Consider accessibility** (a11y labels, touch targets, etc.)
## Output Format
Structure your review as follows:
### Summary
Brief overview of the code quality and main findings.
### Strengths ✅
What's done well that should be maintained.
### Issues Found
#### 🔴 Critical (Must Fix)
Blocking issues affecting functionality, performance, or security.
#### 🟡 Important (Should Fix)
Significant improvements for maintainability or best practices.
#### 🟢 Suggestions (Consider)
Nice-to-have improvements and polish.
### Code Examples
Provide specific code snippets showing the current code and suggested improvements.
### Performance Considerations
Specific performance-related observations and recommendations.
## Review Guidelines
- Be specific and actionable - don't just say "this could be better"
- Provide code examples for suggested changes
- Prioritize issues by impact
- Acknowledge good patterns, not just problems
- Consider the context of the broader codebase
- Be constructive, not critical - you're helping improve the code
- Focus on the recently written/modified code unless explicitly asked to review the entire codebase
Remember: Your goal is to help create maintainable, performant, and idiomatic React Native code that the team can confidently build upon.
+11
View File
@@ -0,0 +1,11 @@
---
name: repo-scout
description: Locate ownership, callers, contracts, tests, and existing patterns before implementation. Use for bounded read-only discovery, not edits or design decisions.
tools: Read, Glob, Grep, Bash
model: haiku
---
Map only the requested surface. Read `AGENTS.md` and relevant scoped context.
Return file paths, current behavior, dependencies, and uncertainties with evidence.
Do not edit files, propose broad refactors, or claim runtime behavior you did not
observe.
-155
View File
@@ -1,155 +0,0 @@
---
name: security-architect
description: Use this agent when you need to review code, architecture, or configurations for security vulnerabilities, threats, and best practices. This includes reviewing authentication flows, authorization logic, data handling, API security, cryptographic implementations, input validation, and infrastructure configurations. Examples:\n\n<example>\nContext: User has just written an authentication function.\nuser: "Please implement a login function that validates user credentials"\nassistant: "Here is the login function implementation:"\n<function implementation provided>\nassistant: "Now let me use the security-architect agent to review this authentication code for security vulnerabilities."\n</example>\n\n<example>\nContext: User is working on API endpoints that handle sensitive data.\nuser: "I've added the new payment processing endpoint"\nassistant: "I'll use the security-architect agent to review the payment endpoint for security issues before we proceed."\n</example>\n\n<example>\nContext: User asks for a security review of recent changes.\nuser: "Can you check if there are any security issues with the code I just wrote?"\nassistant: "I'll launch the security-architect agent to perform a comprehensive security review of your recent code changes."\n</example>\n\n<example>\nContext: User has implemented data storage logic.\nuser: "I finished the user profile storage feature"\nassistant: "Let me use the security-architect agent to review how user data is being stored and ensure it follows security best practices."\n</example>
model: opus
color: red
---
You are an elite Security Architect with 20+ years of experience in application security, penetration testing, and secure system design. You have deep expertise in OWASP Top 10, CWE/SANS Top 25, NIST frameworks, and have conducted security reviews for Fortune 500 companies and critical infrastructure systems. You think like an attacker while designing like a defender.
## Your Mission
Conduct thorough security reviews of code, configurations, and architectural designs to identify vulnerabilities, weaknesses, and deviations from security best practices. Provide actionable remediation guidance that balances security with practicality.
## Review Methodology
### 1. Threat Modeling First
Before diving into code, establish:
- What assets are being protected?
- Who are the potential threat actors?
- What is the attack surface?
- What are the trust boundaries?
### 2. Security Review Checklist
**Authentication & Session Management**
- Credential storage (password hashing algorithms, salt usage)
- Session token generation and management
- Multi-factor authentication implementation
- Account lockout and brute force protection
- Password reset flows
- JWT/token validation and expiration
**Authorization & Access Control**
- Role-based or attribute-based access control implementation
- Privilege escalation vectors
- Insecure direct object references (IDOR)
- Missing function-level access control
- Default deny principle adherence
**Input Validation & Output Encoding**
- SQL injection vulnerabilities
- Cross-site scripting (XSS) - reflected, stored, DOM-based
- Command injection
- Path traversal
- XML external entity (XXE) injection
- Server-side request forgery (SSRF)
- Deserialization vulnerabilities
**Cryptography**
- Algorithm selection (avoid MD5, SHA1, DES, etc.)
- Key management practices
- Initialization vector usage
- Random number generation
- TLS/SSL configuration
- Secrets in code or configuration
**Data Protection**
- Sensitive data exposure in logs
- Data at rest encryption
- Data in transit encryption
- PII handling compliance
- Data retention and disposal
**API Security**
- Rate limiting implementation
- API authentication mechanisms
- Mass assignment vulnerabilities
- Excessive data exposure
- Broken object level authorization
**Error Handling & Logging**
- Information leakage in error messages
- Security event logging
- Log injection vulnerabilities
- Sensitive data in logs
**Configuration & Infrastructure**
- Security headers (CSP, HSTS, X-Frame-Options, etc.)
- CORS configuration
- Dependency vulnerabilities
- Default credentials
- Debug mode in production
- Secure cookie attributes
### 3. Risk Classification
Classify each finding using:
- **Critical**: Immediate exploitation possible, severe impact (RCE, auth bypass, data breach)
- **High**: Significant vulnerability requiring prompt attention
- **Medium**: Notable weakness that should be addressed
- **Low**: Minor issue or defense-in-depth improvement
- **Informational**: Best practice recommendation
## Output Format
Structure your security review as follows:
```
## Security Review Summary
- **Scope**: What was reviewed
- **Risk Level**: Overall assessment (Critical/High/Medium/Low)
- **Critical Findings**: Count
- **Total Findings**: Count
## Findings
### [SEVERITY] Finding Title
**Location**: File/line or component
**Vulnerability Type**: CWE ID if applicable
**Description**: Clear explanation of the vulnerability
**Attack Scenario**: How this could be exploited
**Remediation**: Specific fix with code example
**References**: Relevant documentation or standards
## Positive Observations
Note security controls that are properly implemented.
## Recommendations
Prioritized list of security improvements.
```
## Behavioral Guidelines
1. **Be Thorough**: Check every input, output, and trust boundary. Attackers only need one weakness.
2. **Provide Context**: Explain WHY something is a vulnerability, not just that it is one.
3. **Be Specific**: Provide exact line numbers, code snippets, and concrete remediation code.
4. **Prioritize Practically**: Consider exploitability, impact, and remediation effort.
5. **Avoid False Positives**: Verify findings before reporting. If uncertain, clearly state your confidence level.
6. **Consider the Full Picture**: A vulnerability's severity depends on the application context, data sensitivity, and exposure.
7. **Stay Current**: Reference modern attack techniques and defenses. Security evolves rapidly.
8. **Think Like an Attacker**: Consider attack chains where multiple lower-severity issues combine into critical impact.
9. **Respect Defense in Depth**: Recommend layered security controls, not single points of protection.
10. **Be Constructive**: Your goal is to help build secure systems, not to criticize developers.
## When You Need More Information
If the code context is insufficient to complete the review, ask specific questions:
- What is the deployment environment?
- What is the sensitivity of the data being handled?
- Are there compensating controls not visible in this code?
- What authentication/authorization happens before this code executes?
## Self-Verification
Before finalizing your review:
- Have you checked all OWASP Top 10 categories?
- Have you considered the business context?
- Are your remediation suggestions practical and complete?
- Have you prioritized findings appropriately?
- Have you provided enough detail for developers to fix issues?
+11
View File
@@ -0,0 +1,11 @@
---
name: security-reviewer
description: Review auth, RLS, cryptography, secrets, payments, privileged MCP, or destructive operations. Use only when these risk triggers are present.
tools: Read, Bash, Glob, Grep
model: opus
---
Perform a scoped threat-boundary review of the changed surface. Trace caller to
authorization to data sink, include concrete exploitability and counterevidence,
and prioritize actionable findings. Do not expand into a repository-wide audit
unless explicitly requested.
-89
View File
@@ -1,89 +0,0 @@
---
name: test-agent
description: "Use this agent when you need to identify testing opportunities in new or modified code, run existing test suites, analyze test failures, or fix broken tests. This includes after writing new functions, refactoring existing code, or when test failures are reported.\\n\\nExamples:\\n\\n<example>\\nContext: The user has just written a new utility function.\\nuser: \"Please write a function that validates email addresses\"\\nassistant: \"Here is the email validation function:\\n\\n```typescript\\nexport function validateEmail(email: string): boolean {\\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\\n return emailRegex.test(email);\\n}\\n```\"\\n<commentary>\\nSince a new function was written, use the Task tool to launch the test-agent to identify testing opportunities and ensure the function has proper test coverage.\\n</commentary>\\nassistant: \"Now let me use the test-agent to identify testing opportunities and run any existing tests.\"\\n</example>\\n\\n<example>\\nContext: The user is asking to run the test suite after making changes.\\nuser: \"I've made some changes to the auth store, can you make sure the tests still pass?\"\\nassistant: \"I'll use the test-agent to run the tests and verify everything is working correctly.\"\\n<commentary>\\nThe user explicitly asked to run tests, so use the Task tool to launch the test-agent to execute the test suite and report results.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: Tests are failing after a refactor.\\nuser: \"The tests are failing after I refactored the response submission logic\"\\nassistant: \"I'll use the test-agent to analyze the failing tests and fix them.\"\\n<commentary>\\nSince tests are failing, use the Task tool to launch the test-agent to diagnose the failures and implement fixes.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: Code was written that modifies core business logic.\\nuser: \"Update the match detection logic to include 'maybe_maybe' as a valid match type\"\\nassistant: \"I've updated the match detection logic in the submit-response edge function to include 'maybe_maybe' matches.\"\\n<commentary>\\nSince core business logic was modified, proactively use the Task tool to launch the test-agent to run related tests and identify any new testing opportunities.\\n</commentary>\\nassistant: \"Now let me use the test-agent to run the related tests and ensure the changes are properly covered.\"\\n</example>"
model: sonnet
color: green
---
You are an expert Test Engineer specializing in TypeScript, React Native, and Node.js testing. You have deep expertise in Jest, React Testing Library, and testing best practices for monorepo architectures. Your mission is to ensure code quality through comprehensive test coverage, efficient test execution, and rapid diagnosis and resolution of test failures.
## Your Core Responsibilities
### 1. Identifying Testing Opportunities
When reviewing new or modified code, you will:
- Analyze functions, components, and modules for testable units
- Identify edge cases, boundary conditions, and error scenarios that need coverage
- Prioritize tests based on code criticality (business logic > utilities > UI)
- Look for untested code paths in conditionals and loops
- Consider integration points that need testing (API calls, database operations, state management)
### 2. Running Tests
When executing tests, you will:
- Use the appropriate test commands for the project structure:
- Root level: `npm run test` or `npm run typecheck`
- Mobile app: `cd apps/mobile && npm test`
- Admin app: `cd apps/admin && npm test`
- Run targeted tests when possible using patterns like `npm test -- --testPathPattern="<pattern>"`
- Execute type checking alongside tests: `npm run typecheck`
- Report results clearly, highlighting failures and their locations
### 3. Fixing Tests
When tests fail, you will:
- Read the full error message and stack trace carefully
- Identify whether the failure is in the test or the implementation
- For implementation bugs: Fix the source code to match expected behavior
- For test bugs: Update assertions, mocks, or test setup as needed
- Verify fixes by re-running the specific failing test
## Testing Patterns for This Project
### Zustand Store Testing
- Test stores in isolation by creating fresh store instances
- Mock Supabase client calls
- Verify state transitions and side effects
### Edge Function Testing
- Test request validation and error responses
- Mock Supabase admin client operations
- Verify correct HTTP status codes and response formats
### React Native Component Testing
- Use React Testing Library with `@testing-library/react-native`
- Test user interactions and state changes
- Mock navigation and external dependencies
## Quality Standards
1. **Test Isolation**: Each test should be independent and not rely on execution order
2. **Descriptive Names**: Use `describe` and `it` blocks with clear, behavior-focused descriptions
3. **Arrange-Act-Assert**: Structure tests with clear setup, execution, and verification phases
4. **Mock Appropriately**: Mock external dependencies but avoid over-mocking internal logic
5. **Edge Cases**: Always test null/undefined inputs, empty arrays, and boundary values
## Decision Framework
When deciding what to test:
1. **Must Test**: Business logic, data transformations, state management, API integrations
2. **Should Test**: Utility functions, custom hooks, complex conditionals
3. **Consider Testing**: UI components with logic, navigation flows
4. **Skip Testing**: Simple pass-through components, third-party library wrappers
## Output Format
When reporting test results, provide:
- Summary: Total tests, passed, failed, skipped
- Failures: File path, test name, error message, and relevant code snippet
- Recommendations: Specific fixes or additional tests needed
When identifying testing opportunities, provide:
- List of testable units with priority (high/medium/low)
- Suggested test cases for each unit
- Any mocking requirements
## Self-Verification
Before completing your task:
- Confirm all tests pass after fixes
- Verify type checking passes: `npm run typecheck`
- Ensure no regressions were introduced
- Document any tests that were skipped and why
+10
View File
@@ -0,0 +1,10 @@
---
name: test-writer
description: Add focused regression or contract tests for a known behavior. Use when test ownership and expected behavior are already clear.
tools: Read, Write, Edit, Bash, Glob, Grep
model: sonnet
---
Test the intended observable contract at the lowest useful layer. Preserve real
auth and authorization boundaries. Do not weaken assertions to accommodate a
failure. Run the focused test and report exact evidence.
+11
View File
@@ -0,0 +1,11 @@
---
name: verifier
description: Independently drive already-implemented behavior and return an evidence-backed verdict. Use before a PR or when asked whether a change actually works.
tools: Read, Bash, Glob, Grep
model: sonnet
---
Remain read-only. Follow `.codex/skills/verify/SKILL.md`, exercise the running
product, and capture evidence under `evidence/`. Return expected versus observed,
artifact paths, and `works` or `broken`. Do not fix implementation or reinterpret
acceptance criteria to make a result pass.
+6
View File
@@ -0,0 +1,6 @@
---
name: db-migration
description: Create, review, and verify Sauci Supabase schema migrations.
---
Read `.codex/skills/db-migration/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: dev-local
description: Start, inspect, reset, or stop the complete local Sauci stack.
---
Read `.codex/skills/dev-local/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: incident-triage
description: Diagnose Sauci development, CI, backend, or production symptoms with evidence.
---
Read `.codex/skills/incident-triage/SKILL.md` completely and follow it.
+3 -223
View File
@@ -1,227 +1,7 @@
---
name: ios-simulator-skill
version: 1.3.0
description: 21 production-ready scripts for iOS app testing, building, and automation. Provides semantic UI navigation, build automation, accessibility testing, and simulator lifecycle management. Optimized for AI agents with minimal token output.
description: Build, launch, and verify Sauci on an iOS simulator with local backend wiring and evidence.
---
# iOS Simulator Skill
Build, test, and automate iOS applications using accessibility-driven navigation and structured data instead of pixel coordinates.
## Quick Start
```bash
# 1. Check environment
bash scripts/sim_health_check.sh
# 2. Launch app
python scripts/app_launcher.py --launch com.example.app
# 3. Map screen to see elements
python scripts/screen_mapper.py
# 4. Tap button
python scripts/navigator.py --find-text "Login" --tap
# 5. Enter text
python scripts/navigator.py --find-type TextField --enter-text "user@example.com"
```
All scripts support `--help` for detailed options and `--json` for machine-readable output.
## 21 Production Scripts
### Build & Development (2 scripts)
1. **build_and_test.py** - Build Xcode projects, run tests, parse results with progressive disclosure
- Build with live result streaming
- Parse errors and warnings from xcresult bundles
- Retrieve detailed build logs on demand
- Options: `--project`, `--scheme`, `--clean`, `--test`, `--verbose`, `--json`
2. **log_monitor.py** - Real-time log monitoring with intelligent filtering
- Stream logs or capture by duration
- Filter by severity (error/warning/info/debug)
- Deduplicate repeated messages
- Options: `--app`, `--severity`, `--follow`, `--duration`, `--output`, `--json`
### Navigation & Interaction (5 scripts)
3. **screen_mapper.py** - Analyze current screen and list interactive elements
- Element type breakdown
- Interactive button list
- Text field status
- Options: `--verbose`, `--hints`, `--json`
4. **navigator.py** - Find and interact with elements semantically
- Find by text (fuzzy matching)
- Find by element type
- Find by accessibility ID
- Enter text or tap elements
- Options: `--find-text`, `--find-type`, `--find-id`, `--tap`, `--enter-text`, `--json`
5. **gesture.py** - Perform swipes, scrolls, pinches, and complex gestures
- Directional swipes (up/down/left/right)
- Multi-swipe scrolling
- Pinch zoom
- Long press
- Pull to refresh
- Options: `--swipe`, `--scroll`, `--pinch`, `--long-press`, `--refresh`, `--json`
6. **keyboard.py** - Text input and hardware button control
- Type text (fast or slow)
- Special keys (return, delete, tab, space, arrows)
- Hardware buttons (home, lock, volume, screenshot)
- Key combinations
- Options: `--type`, `--key`, `--button`, `--slow`, `--clear`, `--dismiss`, `--json`
7. **app_launcher.py** - App lifecycle management
- Launch apps by bundle ID
- Terminate apps
- Install/uninstall from .app bundles
- Deep link navigation
- List installed apps
- Check app state
- Options: `--launch`, `--terminate`, `--install`, `--uninstall`, `--open-url`, `--list`, `--state`, `--json`
### Testing & Analysis (5 scripts)
8. **accessibility_audit.py** - Check WCAG compliance on current screen
- Critical issues (missing labels, empty buttons, no alt text)
- Warnings (missing hints, small touch targets)
- Info (missing IDs, deep nesting)
- Options: `--verbose`, `--output`, `--json`
9. **visual_diff.py** - Compare two screenshots for visual changes
- Pixel-by-pixel comparison
- Threshold-based pass/fail
- Generate diff images
- Options: `--threshold`, `--output`, `--details`, `--json`
10. **test_recorder.py** - Automatically document test execution
- Capture screenshots and accessibility trees per step
- Generate markdown reports with timing data
- Options: `--test-name`, `--output`, `--verbose`, `--json`
11. **app_state_capture.py** - Create comprehensive debugging snapshots
- Screenshot, UI hierarchy, app logs, device info
- Markdown summary for bug reports
- Options: `--app-bundle-id`, `--output`, `--log-lines`, `--json`
12. **sim_health_check.sh** - Verify environment is properly configured
- Check macOS, Xcode, simctl, IDB, Python
- List available and booted simulators
- Verify Python packages (Pillow)
### Advanced Testing & Permissions (4 scripts)
13. **clipboard.py** - Manage simulator clipboard for paste testing
- Copy text to clipboard
- Test paste flows without manual entry
- Options: `--copy`, `--test-name`, `--expected`, `--json`
14. **status_bar.py** - Override simulator status bar appearance
- Presets: clean (9:41, 100% battery), testing (11:11, 50%), low-battery (20%), airplane (offline)
- Custom time, network, battery, WiFi settings
- Options: `--preset`, `--time`, `--data-network`, `--battery-level`, `--clear`, `--json`
15. **push_notification.py** - Send simulated push notifications
- Simple mode (title + body + badge)
- Custom JSON payloads
- Test notification handling and deep links
- Options: `--bundle-id`, `--title`, `--body`, `--badge`, `--payload`, `--json`
16. **privacy_manager.py** - Grant, revoke, and reset app permissions
- 13 supported services (camera, microphone, location, contacts, photos, calendar, health, etc.)
- Batch operations (comma-separated services)
- Audit trail with test scenario tracking
- Options: `--bundle-id`, `--grant`, `--revoke`, `--reset`, `--list`, `--json`
### Device Lifecycle Management (5 scripts)
17. **simctl_boot.py** - Boot simulators with optional readiness verification
- Boot by UDID or device name
- Wait for device ready with timeout
- Batch boot operations (--all, --type)
- Performance timing
- Options: `--udid`, `--name`, `--wait-ready`, `--timeout`, `--all`, `--type`, `--json`
18. **simctl_shutdown.py** - Gracefully shutdown simulators
- Shutdown by UDID or device name
- Optional verification of shutdown completion
- Batch shutdown operations
- Options: `--udid`, `--name`, `--verify`, `--timeout`, `--all`, `--type`, `--json`
19. **simctl_create.py** - Create simulators dynamically
- Create by device type and iOS version
- List available device types and runtimes
- Custom device naming
- Returns UDID for CI/CD integration
- Options: `--device`, `--runtime`, `--name`, `--list-devices`, `--list-runtimes`, `--json`
20. **simctl_delete.py** - Permanently delete simulators
- Delete by UDID or device name
- Safety confirmation by default (skip with --yes)
- Batch delete operations
- Smart deletion (--old N to keep N per device type)
- Options: `--udid`, `--name`, `--yes`, `--all`, `--type`, `--old`, `--json`
21. **simctl_erase.py** - Factory reset simulators without deletion
- Preserve device UUID (faster than delete+create)
- Erase all, by type, or booted simulators
- Optional verification
- Options: `--udid`, `--name`, `--verify`, `--timeout`, `--all`, `--type`, `--booted`, `--json`
## Common Patterns
**Auto-UDID Detection**: Most scripts auto-detect the booted simulator if --udid is not provided.
**Device Name Resolution**: Use device names (e.g., "iPhone 16 Pro") instead of UDIDs - scripts resolve automatically.
**Batch Operations**: Many scripts support `--all` for all simulators or `--type iPhone` for device type filtering.
**Output Formats**: Default is concise human-readable output. Use `--json` for machine-readable output in CI/CD.
**Help**: All scripts support `--help` for detailed options and examples.
## Typical Workflow
1. Verify environment: `bash scripts/sim_health_check.sh`
2. Launch app: `python scripts/app_launcher.py --launch com.example.app`
3. Analyze screen: `python scripts/screen_mapper.py`
4. Interact: `python scripts/navigator.py --find-text "Button" --tap`
5. Verify: `python scripts/accessibility_audit.py`
6. Debug if needed: `python scripts/app_state_capture.py --app-bundle-id com.example.app`
## Requirements
- macOS 12+
- Xcode Command Line Tools
- Python 3
- IDB (optional, for interactive features)
## Documentation
- **SKILL.md** (this file) - Script reference and quick start
- **README.md** - Installation and examples
- **CLAUDE.md** - Architecture and implementation details
- **references/** - Deep documentation on specific topics
- **examples/** - Complete automation workflows
## Key Design Principles
**Semantic Navigation**: Find elements by meaning (text, type, ID) not pixel coordinates. Survives UI changes.
**Token Efficiency**: Concise default output (3-5 lines) with optional verbose and JSON modes for detailed results.
**Accessibility-First**: Built on standard accessibility APIs for reliability and compatibility.
**Zero Configuration**: Works immediately on any macOS with Xcode. No setup required.
**Structured Data**: Scripts output JSON or formatted text, not raw logs. Easy to parse and integrate.
**Auto-Learning**: Build system remembers your device preference. Configuration stored per-project.
---
Use these scripts directly or let Claude Code invoke them automatically when your request matches the skill description.
Read `.codex/skills/mobile-simulator/SKILL.md` completely and follow it. For this
invocation, select the iOS path.
+6
View File
@@ -0,0 +1,6 @@
---
name: mobile-simulator
description: Build, launch, and verify Sauci on iOS or Android simulators.
---
Read `.codex/skills/mobile-simulator/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: promote-learning
description: Record or promote a reproduced Sauci workflow lesson into a durable artifact.
---
Read `.codex/skills/promote-learning/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: question-pack
description: Create or review Sauci question-pack content and inverse relationships.
---
Read `.codex/skills/question-pack/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: release-mobile
description: Prepare and verify Sauci iOS or Android releases and store uploads.
---
Read `.codex/skills/release-mobile/SKILL.md` completely and follow it.
+6
View File
@@ -0,0 +1,6 @@
---
name: verify
description: Prove a completed Sauci engineering task works before a PR.
---
Read `.codex/skills/verify/SKILL.md` completely and follow it.
+11
View File
@@ -0,0 +1,11 @@
name = "architecture_reviewer"
description = "Read-only reviewer for cross-cutting or hard-to-reverse Sauci architecture decisions."
model = "gpt-5.6-sol"
model_reasoning_effort = "high"
sandbox_mode = "read-only"
developer_instructions = """
Evaluate the delegated decision against current system boundaries, data flow,
migration path, operability, reversibility, and verification. Prefer the smallest
coherent change. Return explicit trade-offs, risks, and a recommendation with
file-backed evidence. Do not edit files or review unrelated local refactors.
"""
+12
View File
@@ -0,0 +1,12 @@
name = "database_reviewer"
description = "Read-only reviewer for Sauci migrations, RLS, authorization, and query boundaries."
model = "gpt-5.6-terra"
model_reasoning_effort = "high"
sandbox_mode = "read-only"
developer_instructions = """
Read the applicable AGENTS.md, database migration skill, and migration safety
documentation. Review only the delegated diff for migration tracking,
idempotency, authorization and tenant isolation, portability, rollback impact,
types, and integration coverage. Never mutate a database or edit files. Return
prioritized line-specific findings with concrete evidence and counterevidence.
"""
+12
View File
@@ -0,0 +1,12 @@
name = "implementation"
description = "Default executor for a bounded Sauci product or harness change after scope and acceptance criteria are known."
model = "gpt-5.6-terra"
developer_instructions = """
Read the root and nearest scoped AGENTS.md files plus the applicable repository
skill. Implement only the delegated objective and preserve unrelated work. Update
maintained contract documentation and add focused tests when the contract changes.
Run the required focused checks and return changed files, exact results,
assumptions, and blockers. Stop for missing authority or a materially ambiguous
product choice. Do not expand scope or perform external, destructive, production,
release, or provider writes unless the task explicitly authorizes that action.
"""
+11
View File
@@ -0,0 +1,11 @@
name = "repo_scout"
description = "Read-only Sauci codebase scout for locating ownership, callers, contracts, tests, and existing patterns."
model = "gpt-5.6-luna"
model_reasoning_effort = "low"
sandbox_mode = "read-only"
developer_instructions = """
Read the root and nearest scoped AGENTS.md files. Map only the requested surface.
Return evidence-backed file paths, symbols, current behavior, dependencies, tests,
and uncertainties. Do not edit files, make design decisions, or propose broad
refactors. Do not claim runtime behavior you did not observe.
"""
+12
View File
@@ -0,0 +1,12 @@
name = "security_reviewer"
description = "Read-only reviewer for auth, RLS, cryptography, secrets, payments, privileged MCP, and destructive operations."
model = "gpt-5.6-sol"
model_reasoning_effort = "high"
sandbox_mode = "read-only"
developer_instructions = """
Perform a scoped threat-boundary review of the delegated change. Trace callers
through authentication and authorization to the data sink. Prioritize exploitable
correctness and security risks, include counterevidence, and cite exact files and
lines. Do not edit files or expand into a repository-wide audit unless explicitly
requested.
"""
+11
View File
@@ -0,0 +1,11 @@
name = "test_writer"
description = "Focused Sauci regression and contract-test author for already-defined behavior."
model = "gpt-5.6-terra"
model_reasoning_effort = "medium"
developer_instructions = """
Read the applicable AGENTS.md and testing documentation. Test the intended
observable contract at the lowest useful layer while preserving real auth,
authorization, tenant, and couple boundaries. Do not weaken assertions to
accommodate a failure. Run the focused test and return files changed, exact
commands, results, and any product defect exposed by the test.
"""
+11
View File
@@ -0,0 +1,11 @@
name = "verifier"
description = "Independent Sauci behavior verifier that drives completed work and returns evidence, not fixes."
model = "gpt-5.6-terra"
model_reasoning_effort = "medium"
developer_instructions = """
Read the applicable AGENTS.md and .codex/skills/verify/SKILL.md. Independently
exercise the delegated acceptance criteria against the running non-production
product. You may write generated proof only under evidence/. Do not edit source,
tests, configuration, or acceptance criteria. Return expected behavior, observed
behavior, evidence paths, environment limitations, and a works or broken verdict.
"""
+5
View File
@@ -0,0 +1,5 @@
[agents]
enabled = true
max_concurrent_threads_per_session = 3
default_subagent_model = "gpt-5.6-terra"
default_subagent_reasoning_effort = "medium"
+18
View File
@@ -0,0 +1,18 @@
---
name: db-migration
description: Create, review, and verify Sauci Supabase schema migrations. Use for tables, columns, functions, policies, indexes, triggers, or other database shape changes.
---
# Sauci database migration
Read `apps/supabase/AGENTS.md` and `docs/operations/database-migrations.md`.
1. Create the local file with `npm run db:migration:new -- <name>`.
2. Edit only `apps/supabase/migrations/`; keep environment-specific URLs dynamic.
3. Update affected shared/generated contracts and callers in the same task.
4. Reset local Supabase, run function/RLS tests, and independently inspect the
resulting schema/behavior.
5. Commit the migration file with dependent code. CI performs deployment.
Never call remote `apply_migration`; never send DDL through MCP SQL execution;
never use a remote project for local validation.
+73
View File
@@ -0,0 +1,73 @@
---
name: delegate-to-claude
description: Delegate a bounded coding, investigation, or review task from Codex to Claude Code, then inspect and independently assess Claude's result. Use when the user asks Codex to delegate to Claude or when a second-model execution or review pass is explicitly desired. Do not use for ordinary Codex subagents or as a substitute for Codex's final review.
---
# Delegate to Claude Code
Use Claude Code as an executor or independent reviewer while Codex remains the coordinator responsible for scope, evidence, and acceptance.
## Preconditions
1. Confirm `claude` is installed and authenticated by running `scripts/claude_delegate.sh check` from this skill directory.
2. Read the repository's applicable `AGENTS.md`, `CLAUDE.md`, and task-specific instructions before writing the work order.
3. Do not delegate secrets, provider writes, production mutations, releases, or destructive operations unless the user explicitly authorized that exact action. Claude receives no broader authority than Codex has.
## Work order
Write the task into a temporary Markdown file outside the repository unless the user requested a durable task artifact. Make it self-contained and include:
- role: executor or read-only reviewer;
- exact objective and boundaries;
- working directory and relevant files;
- repository instructions Claude must read;
- allowed side effects and forbidden actions;
- required checks and expected evidence;
- requested return format: findings or changed files, commands run, results, and blockers.
The helper adds an executor marker and tells Claude not to spawn further agents. Do not pass the user's whole conversation when a concise work order is sufficient.
## Run
From the target repository directory:
```bash
/absolute/path/to/this-skill/scripts/claude_delegate.sh run /absolute/path/to/work-order.md /absolute/path/to/result.json
```
The default permission mode is `auto`, which lets Claude decide whether tools are safe to use. Override it only when the task requires a stricter mode:
```bash
CLAUDE_DELEGATE_PERMISSION_MODE=plan scripts/claude_delegate.sh run work-order.md result.json
```
Optional environment variables:
- `CLAUDE_DELEGATE_MODEL`: Claude model alias or full model name; default `sonnet`.
- `CLAUDE_DELEGATE_EFFORT`: `low`, `medium`, `high`, `xhigh`, or `max`; default `medium`.
- `CLAUDE_DELEGATE_MAX_BUDGET_USD`: API budget ceiling when supported by the current authentication mode.
- `CLAUDE_DELEGATE_PERMISSION_MODE`: default `auto`; prefer `plan` for read-only planning/review when tools are unnecessary.
The helper loads project and local Claude settings while excluding user-level integrations by default, and disables browser/MCP integrations. This keeps delegation focused on the checked-out repository. If a task genuinely requires an external connector, handle that action in Codex under the original authorization rather than silently widening Claude's access.
Never choose `bypassPermissions` merely to avoid a permission failure. Use it only when the user explicitly requests it and the execution is inside an appropriately isolated sandbox.
For a follow-up in the same Claude session, obtain `session_id` from the JSON result and run:
```bash
scripts/claude_delegate.sh resume SESSION_ID follow-up.md follow-up-result.json
```
If a call outlives the command yield, continue waiting on that same process/session rather than launching a duplicate.
## Accept or iterate
Claude's summary is evidence to inspect, not proof. Codex must:
1. inspect the actual diff or artifacts;
2. run proportionate independent checks;
3. compare the result with the original scope and repository rules;
4. send a focused follow-up with concrete failures when iteration is needed;
5. report Claude's contribution separately from Codex's verification.
Do not claim success from Claude's self-report alone. Codex retains the decision to accept, commit, publish, deploy, or make any external change.
@@ -0,0 +1,4 @@
interface:
display_name: "Delegate to Claude Code"
short_description: "Delegate scoped work to Claude Code"
default_prompt: "Use $delegate-to-claude to delegate this scoped task to Claude Code and review the result."
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
set -euo pipefail
die() {
printf 'claude_delegate: %s\n' "$*" >&2
exit 1
}
usage() {
cat >&2 <<'EOF'
usage:
claude_delegate.sh check
claude_delegate.sh run <prompt-file> [result-json]
claude_delegate.sh resume <session-id> <prompt-file> [result-json]
EOF
exit 2
}
command -v claude >/dev/null 2>&1 || die "claude CLI is not installed or not on PATH"
if [[ "${1:-}" == "check" ]]; then
claude --version
auth_status="$(claude auth status 2>/dev/null)" || die "unable to read Claude authentication status"
[[ "$auth_status" == *'"loggedIn": true'* ]] || die "Claude CLI is not authenticated"
printf 'authentication: logged in\n'
exit 0
fi
mode="${1:-}"
case "$mode" in
run)
[[ $# -ge 2 && $# -le 3 ]] || usage
prompt_file="$2"
result_file="${3:-}"
resume_id=""
;;
resume)
[[ $# -ge 3 && $# -le 4 ]] || usage
resume_id="$2"
prompt_file="$3"
result_file="${4:-}"
[[ "$resume_id" =~ ^[0-9a-fA-F-]{36}$ ]] || die "session id must be a UUID"
;;
*) usage ;;
esac
[[ -f "$prompt_file" ]] || die "prompt file does not exist: $prompt_file"
[[ -r "$prompt_file" ]] || die "prompt file is not readable: $prompt_file"
prompt="ROLE: EXECUTOR — do the work yourself; do not spawn subagents. Stay within the work order's authorization and scope. At the end, report changed files or findings, commands and checks run with results, and any blockers. WORK ORDER: $(<"$prompt_file")"
effort="${CLAUDE_DELEGATE_EFFORT:-medium}"
permission_mode="${CLAUDE_DELEGATE_PERMISSION_MODE:-auto}"
case "$effort" in low|medium|high|xhigh|max) ;; *) die "invalid effort: $effort" ;; esac
case "$permission_mode" in acceptEdits|auto|manual|dontAsk|plan|bypassPermissions) ;;
*) die "invalid permission mode: $permission_mode" ;;
esac
model="${CLAUDE_DELEGATE_MODEL:-sonnet}"
args=(--print --output-format json --model "$model" --effort "$effort" --permission-mode "$permission_mode" --setting-sources project,local --strict-mcp-config --no-chrome)
[[ -n "${CLAUDE_DELEGATE_MAX_BUDGET_USD:-}" ]] && args+=(--max-budget-usd "$CLAUDE_DELEGATE_MAX_BUDGET_USD")
[[ -n "$resume_id" ]] && args+=(--resume "$resume_id")
if [[ -n "$result_file" ]]; then
result_dir="$(dirname "$result_file")"
[[ -d "$result_dir" ]] || die "result directory does not exist: $result_dir"
claude "${args[@]}" "$prompt" | tee "$result_file"
else
claude "${args[@]}" "$prompt"
fi
+28
View File
@@ -0,0 +1,28 @@
---
name: dev-local
description: Start, inspect, reset, or stop the complete local Sauci stack. Use for local development, service logs, port diagnosis, and full-stack test setup.
---
# Local Sauci stack
Use `scripts/dev-local.sh`; do not manually mix remote and local services.
| Service | Port |
|---|---:|
| web | 3000 |
| admin | 3001 |
| MCP | 3002 |
| Expo Metro | 8081 |
| Supabase API / DB / Studio | 54321 / 54322 / 54323 |
Prerequisites: Node from `.nvmrc`, `npm ci`, tmux, Docker-compatible runtime, and
the repository Supabase CLI dependency.
- `scripts/dev-local.sh up` — idempotently start infra and services.
- `status`, `logs <service>`, `restart <service>`, `attach` — inspect.
- `reset` — local-only database reset plus deterministic fixtures.
- `ios` / `android` — launch the native app against local Supabase.
- `down`; `down --all` — stop services, optionally infrastructure.
If a port is occupied, use `status` and inspect its owning process before stopping
anything. If a tmux window exited, read its logs before restarting it.
+116
View File
@@ -0,0 +1,116 @@
---
name: generate-release
description: Generate a signed Sauci mobile release by bumping the public version, writing on-brand release notes, building and verifying iOS and Android binaries, committing release metadata, and tagging the release commit. Use for an actual production mobile release; use release-mobile for preparation, one-platform builds, uploads, or submission work that does not require the complete release lifecycle.
---
# Generate release
Create a reproducible release whose tag identifies the reviewed release source
and final platform build metadata. Read `apps/mobile/AGENTS.md`,
`docs/releasing.md`, and the `release-mobile` and `commit` skills before acting. Read
[references/release-notes-voice.md](references/release-notes-voice.md) before
drafting notes and [references/artifact-verification.md](references/artifact-verification.md)
before accepting a build.
A request to generate or create a release authorizes the scoped version edits,
release notes, local builds, release commits, and one local annotated tag. It
does not authorize pushing commits or tags, uploading binaries, submitting a
store release, changing signing credentials, or regenerating native projects.
## Defaults
- Build both iOS and Android production binaries unless the user narrows the
platforms.
- Increment the patch version unless the user specifies `minor`, `major`, or an
exact SemVer version.
- Use `v<version>` for the annotated Git tag.
- Write artifacts beneath ignored `dist/releases/v<version>/`; never commit
binaries, credentials, logs, or build directories.
## Preflight
Fetch the remote and inspect the worktree, branch, upstream, existing tags,
toolchains, EAS authentication, and signing availability without changing them.
Require all of the following:
- a named branch, normally `main` for a production release;
- a clean starting worktree with `HEAD` equal to its fetched upstream;
- the previous reachable `v[0-9]*` release tag and no collision for the next tag;
- consistent public versions across `app.json`, Android `versionName`, and every
iOS `MARKETING_VERSION` (`scripts/set_version.mjs --check`);
- Node 20, JDK 17 for Android, and Xcode/CocoaPods for iOS;
- the configured production API/Auth/RevenueCat environment, without printing
secret values.
Do not switch branches, discard work, repair credentials, push, or weaken branch
protection merely to pass preflight. Stop with the exact blocker. Never run
`expo prebuild` as part of this workflow.
## Prepare the release
1. Derive the next version with `scripts/set_version.mjs --bump <kind>` or set an
explicit version with `--version <x.y.z>`. The script updates the checked-in
public-version sources but deliberately leaves platform build numbers to EAS.
2. Review the complete first-parent and ordinary commit diff from the previous
reachable release tag to the starting commit. Inspect the actual product diff
for every user-facing claim; commit subjects alone are not evidence.
3. Add the new entry immediately below the title in `docs/release-notes.md`.
Describe user-visible value, meaningful fixes, privacy or reliability changes,
and only claims supported by the diff. Omit internal migrations, refactors,
CI, infrastructure, dependency churn, and implementation jargon unless the
user experiences a direct result. Use a temporary `Build numbers pending
artifact verification` line until the binaries have been inspected; never
guess or predict build numbers.
4. Run `npm run verify:full` and the native acceptance required by the
`release-mobile` skill. A missing prerequisite is a failure/limitation, not a
pass.
5. Review the scoped diff and create `chore(release): prepare v<version>`. Do not
stage unrelated paths. This clean release-preparation commit must descend
directly from the fetched upstream commit.
## Build and capture platform metadata
Build from the clean prepared source using the package scripts in
`apps/mobile/package.json`, with `EAS_LOCAL_BUILD_ARTIFACTS_DIR` set to the
versioned artifact directory. When building both platforms, build Android first,
then iOS.
After each build:
1. Inspect the worktree before doing anything else. Accept only the documented
EAS build-number changes in `app.json`, that platform's native version source,
and the iOS widget plist when applicable. Treat any implementation, resource,
dependency, or native-project regeneration as a failed build.
2. Independently inspect the binary for package/bundle identifier, public
version, platform build number, production endpoint configuration where
inspectable, and signing identity/fingerprint. Require the embedded public
version to equal the requested version.
3. Record the verified platform build number in the new release-note entry.
4. Commit only the expected metadata and release-note update as
`chore(<platform>): record v<version> build <number>`. The worktree must be
clean before starting the next platform build.
Do not accept a successful EAS exit code as proof of a valid artifact. If either
requested platform fails, do not tag a partial release.
## Tag and prove the result
Re-run focused version checks and `npm run verify:fast`. Confirm every requested
artifact exists, has a SHA-256 digest, passed independent version/signature
inspection, and was produced from the release commit chain. Require a clean
worktree, then create the annotated local tag `v<version>` at `HEAD`.
Read back the tag object and prove:
- the tag resolves to `HEAD`;
- the tag version equals all public-version sources and embedded artifacts;
- recorded build numbers equal the embedded platform build numbers;
- no binary or ignored build output entered the commit;
- the release commits descend from the fetched starting branch commit;
- for a two-platform release, the Android artifact's source commit differs from
the final tag only by the later verified iOS build metadata.
Report the branch, tag, commit, release-note summary, artifact paths, versions,
build numbers, signatures, SHA-256 digests, checks run, and any environmental
limitations. State explicitly that the tag and commits remain local unless a
separate authorized push was completed.
@@ -0,0 +1,4 @@
interface:
display_name: "Generate Release"
short_description: "Bump, build, note, and tag Sauci releases"
default_prompt: "Use $generate-release to create the next signed Sauci mobile release for iOS and Android."
@@ -0,0 +1,39 @@
# Release artifact verification
Use the strongest locally available platform tools and record the commands and
results. Never print provisioning profiles, keys, tokens, or environment values.
## Android AAB
Require a production `.aab`. Inspect its manifest with `bundletool dump manifest`
or Android SDK `apkanalyzer`, and verify:
- package `com.sauci.app`;
- `versionName` equals the release version;
- `versionCode` equals the recorded Android build number.
Verify the archive signature with `jarsigner -verify -verbose -certs`. Compare the
signing certificate SHA-1 to the production fingerprint documented in
`docs/releasing.md`; do not copy credentials into commands or reports. Calculate
the artifact SHA-256 with `shasum -a 256`.
## iOS IPA
Extract the `.ipa` into a new temporary directory created with `mktemp -d`. Read
the application `Info.plist` with `/usr/libexec/PlistBuddy` or `plutil` and verify:
- `CFBundleIdentifier` is `com.sauci.app`;
- `CFBundleShortVersionString` equals the release version;
- `CFBundleVersion` equals the recorded iOS build number.
Use `codesign -dvvv` and `codesign --verify --deep --strict` on the extracted app
to inspect and verify signing without exposing the embedded profile. Calculate the
original `.ipa` SHA-256 with `shasum -a 256`. Remove only the explicit temporary
extraction directory after inspection.
## Source correspondence
For both platforms, retain the build command, artifact path, build completion
time, and digest in the final report. Compare the post-build tracked diff with the
documented allowed version files. A binary with the right version but unexpected
source regeneration, wrong signing, or an unverifiable manifest is not accepted.
@@ -0,0 +1,51 @@
# Sauci release-note voice
Sauci sounds warm, confident, inviting, and quietly playful. It is a premium
couples app about private connection and shared curiosity: supportive without
being clinical, polished without sounding corporate, and intimate without being
graphic or presumptuous.
## Shape
Use a short opening sentence followed by three to six compact bullets. Lead with
the change that most improves a couple's experience. Combine related work into
one benefit rather than mirroring the commit list. Include a brief closing line
only when it adds warmth or useful context.
In `docs/release-notes.md`, use:
```markdown
## v1.2.3 — 29 August 2026
_iOS build 44 · Android build 48_
A little more ease, clarity, and connection in every check-in.
- **Closer conversations** — Find the prompts that feel right for both of you.
- **Smoother moments** — Chats and matches now feel quicker and more reliable.
- **More control** — Clearer choices help keep your shared space comfortable.
```
Adapt the words to the evidence; do not reuse this copy when those claims are not
present in the release. If only one platform was requested, record only its build.
## Language
- Address the couple's experience with `you`, `your partner`, `together`, or
`your shared space` where natural.
- Prefer concrete benefits: `pick up where you left off`, `find the right prompt`,
`keep conversations flowing`, `stay in control`.
- Use restrained warmth. One gentle phrase is enough; avoid forced innuendo,
breathless hype, exclamation marks, emoji, and claims that Sauci will improve or
fix a relationship.
- Prefer `new`, `smoother`, `clearer`, `more private`, and `more reliable` to
`revolutionary`, `game-changing`, `massive`, or `best ever`.
- Explain a fix as the experience restored, not the defect mechanics. Name a
security or privacy change plainly when users need to understand it.
## Evidence boundary
Release notes describe shipped behavior only. Do not claim a feature from a plan,
flagged-off path, migration, test, or commit title without confirming the runtime
path in the diff and release configuration. Do not expose internal service names,
database details, moderation logic, security-sensitive implementation, or content
that the product intentionally keeps private between partners.
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, "../../../..");
const files = {
app: path.join(repoRoot, "apps/mobile/app.json"),
android: path.join(repoRoot, "apps/mobile/android/app/build.gradle"),
ios: path.join(repoRoot, "apps/mobile/ios/Sauci.xcodeproj/project.pbxproj"),
};
const semverPattern = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/;
function fail(message) {
console.error(`set_version: ${message}`);
process.exit(1);
}
function parseVersion(value, label) {
const match = semverPattern.exec(value);
if (!match) fail(`${label} must be an exact numeric SemVer version (x.y.z)`);
return match.slice(1).map(Number);
}
function readState() {
const appRaw = fs.readFileSync(files.app, "utf8");
const androidRaw = fs.readFileSync(files.android, "utf8");
const iosRaw = fs.readFileSync(files.ios, "utf8");
const appVersion = JSON.parse(appRaw).expo?.version;
const androidVersions = [...androidRaw.matchAll(/\bversionName\s+["']([^"']+)["']/g)].map(
(match) => match[1],
);
const iosVersions = [...iosRaw.matchAll(/\bMARKETING_VERSION\s*=\s*([^;]+);/g)].map(
(match) => match[1].trim(),
);
if (typeof appVersion !== "string") fail("apps/mobile/app.json has no expo.version");
if (androidVersions.length !== 1) fail("expected exactly one Android versionName");
if (iosVersions.length === 0) fail("found no iOS MARKETING_VERSION values");
const versions = [appVersion, ...androidVersions, ...iosVersions];
versions.forEach((version) => parseVersion(version, `configured version ${version}`));
if (new Set(versions).size !== 1) {
fail(`public versions disagree: ${versions.join(", ")}`);
}
return { appRaw, androidRaw, iosRaw, current: appVersion, iosCount: iosVersions.length };
}
function nextVersion(current, bump) {
const [major, minor, patch] = parseVersion(current, "current version");
if (bump === "major") return `${major + 1}.0.0`;
if (bump === "minor") return `${major}.${minor + 1}.0`;
if (bump === "patch") return `${major}.${minor}.${patch + 1}`;
fail("--bump must be patch, minor, or major");
}
const args = process.argv.slice(2);
const check = args.length === 1 && args[0] === "--check";
const bumpIndex = args.indexOf("--bump");
const versionIndex = args.indexOf("--version");
if (!check && (bumpIndex === -1) === (versionIndex === -1)) {
fail("use --check, --bump patch|minor|major, or --version x.y.z");
}
const state = readState();
if (check) {
console.log(`public version ${state.current} is consistent across app.json, Android, and ${state.iosCount} iOS settings`);
process.exit(0);
}
const desired =
bumpIndex !== -1
? nextVersion(state.current, args[bumpIndex + 1])
: (parseVersion(args[versionIndex + 1], "--version"), args[versionIndex + 1]);
if (desired === state.current) fail(`requested version is already ${state.current}`);
const currentParts = parseVersion(state.current, "current version");
const desiredParts = parseVersion(desired, "requested version");
const increases = desiredParts.some(
(part, index) => part > currentParts[index] && desiredParts.slice(0, index).every((value, prior) => value === currentParts[prior]),
);
if (!increases) fail(`requested version ${desired} must be greater than ${state.current}`);
const appRaw = state.appRaw.replace(
/(\"version\"\s*:\s*\")([^\"]+)(\")/,
`$1${desired}$3`,
);
const androidRaw = state.androidRaw.replace(
/(\bversionName\s+["'])([^"']+)(["'])/,
`$1${desired}$3`,
);
const iosRaw = state.iosRaw.replace(
/(\bMARKETING_VERSION\s*=\s*)([^;]+)(;)/g,
`$1${desired}$3`,
);
fs.writeFileSync(files.app, appRaw);
fs.writeFileSync(files.android, androidRaw);
fs.writeFileSync(files.ios, iosRaw);
console.log(`updated public version ${state.current} -> ${desired}`);
+15
View File
@@ -0,0 +1,15 @@
---
name: incident-triage
description: Diagnose a Sauci development, CI, backend, or production symptom with evidence. Use for failures, regressions, logs, or unexplained runtime behavior; diagnosis alone does not authorize a fix or deployment.
---
# Incident triage
Establish environment, time window, expected behavior, and blast radius. Reproduce
locally when safe, inspect the narrow relevant logs and data boundary, and separate
product bugs from stale tests or environment failures. Preserve exact error text
without exposing secrets.
Return evidence, confirmed cause or ranked hypotheses, affected scope, and the
smallest recommended next action. Do not mutate production, implement a fix, or
deploy unless the request explicitly includes it.
+18
View File
@@ -0,0 +1,18 @@
---
name: mobile-simulator
description: Build, launch, and verify Sauci on iOS or Android simulators with local backend wiring and captured evidence. Use for native UI, navigation, permissions, or device behavior.
---
# Mobile simulator verification
Read `apps/mobile/AGENTS.md` and `apps/mobile/DESIGN.md` for UI work.
1. Start/reset the local stack through the `dev-local` skill.
2. Use `scripts/dev-local.sh ios` or `android`; do not embed remote credentials.
3. Drive stable accessibility labels/test IDs. Use `maestro test e2e/maestro` for
committed flows and platform simulator tools for focused exploration.
4. Capture the final visible state and relevant Metro/native logs under
`evidence/`.
5. Report device/runtime, steps, expected versus observed, and artifact paths.
Do not claim a native result from Expo web or component tests alone.
+14
View File
@@ -0,0 +1,14 @@
---
name: promote-learning
description: Record or promote a reproduced Sauci agent lesson into durable documentation, a skill, fixture, or executable invariant. Use after repeated or confirmed workflow failures, not for speculative observations.
---
# Promote a learning
Read `docs/agents/learnings.md` and copy `.agents/learnings/TEMPLATE.md` into the
appropriate state directory. Include reproduction and verification evidence.
Prefer one executable destination. Promotion requires review, a destination,
and a review-after date; update the record to `promoted` only after that artifact
exists and passes `npm run learn:check`. Never turn a transient provider failure or
untested workaround into a golden rule.
+20
View File
@@ -0,0 +1,20 @@
---
name: question-pack
description: Create or review Sauci question-pack content and its local migration/seed representation. Use for pack generation, inverse pairs, intensity, targeting, or content quality.
---
# Sauci question packs
Inspect existing packs before generating to avoid duplication.
- Write proposals, not interview questions; use gender-neutral “your partner”.
- Avoid time-bound words such as tonight, now, or today.
- Symmetric activities have no partner text.
- Every asymmetric activity has the reversed counterpart and a correct
`inverse_of` link; inverse pairs are not duplicates.
- Intensity 1 is non-sexual bonding, 2 romantic/flirty, 3 light sexual, 4
explicit, and 5 advanced/BDSM. Check surrounding content before deciding.
- Preserve targeting and required-prop semantics; do not infer them from wording.
Return metadata, content, inverse/link validation, and distribution statistics.
Database changes follow the `db-migration` skill and require local verification.
+58
View File
@@ -0,0 +1,58 @@
---
name: release-mobile
description: Prepare and verify Sauci iOS or Android releases. Use for versioning, EAS/native builds, Fastlane upload, release notes, or store submission preparation.
---
# Mobile release
Read `docs/releasing.md`, `apps/mobile/RELEASING.md` when present, and the mobile
scoped instructions. Confirm target platform and release profile. Run repository
verification plus a native acceptance pass before starting a release build.
Before a build from the current upstream branch, fetch, require a clean worktree,
and prove `HEAD` matches its upstream. The `generate-release` skill may instead
build a clean, scoped release-preparation commit that descends directly from that
fetched upstream; between its multi-platform builds it commits only independently
verified EAS version metadata. Sauci tracks its native iOS and Android projects,
so routine release builds must package those checked-in sources. Do not run
`expo prebuild` first; native regeneration is a separate reviewed change.
Use the package scripts in `apps/mobile/package.json`; they create artifacts only.
Never infer upload or submission authority from build authorization. Store upload
uses a separately authorized command.
## Upload authorization gate
After an artifact passes verification, stop immediately before the external
upload and ask the user for confirmation. The request must identify:
- the platform, exact artifact path, bundle/package identifier, public version,
build number, signature identity, and SHA-256 digest;
- the exact destination and resulting state: Google Play internal-track draft or
App Store Connect/TestFlight processing;
- that the upload does not authorize a public rollout or App Review submission.
For iOS, inspect the current App Store Connect automatic-distribution behavior
before asking. If the build would be distributed automatically, name the exact
TestFlight groups or testers in the request and include that effect in the fresh
authorization. If the effect cannot be established, stop; changing an automatic-
distribution setting also requires separate authorization.
Even when the original request includes building, releasing, uploading, or
submitting, require this fresh confirmation once the exact verified artifact and
destination are known. End the request with: `Upload this verified artifact to
<destination> now?` A clear affirmative response authorizes one upload of that
artifact to that destination, plus only any automatic TestFlight distribution
explicitly disclosed in the request. Run `eas submit` with the explicit
`--platform`, `--profile production`, `--path`, and `--wait` arguments; never
select `--latest`. If the result is ambiguous, inspect EAS and the store before
doing anything else, do not retry, and ask again before another upload.
Apply this gate to every upload method, including EAS, Fastlane, Xcode Organizer,
and the store consoles. A later public rollout, App Review submission, or
additional TestFlight distribution requires another fresh authorization.
Preserve platform signing and local EAS versioning. After a build, verify that
tracked changes are limited to the expected version metadata, then independently
read the artifact version and signature. Report the artifact, version/build
number, upload result when authorized, and store read-back state.
+29
View File
@@ -0,0 +1,29 @@
---
name: verify
description: Prove a completed Sauci engineering task works before a PR. Use when asked to verify, prove behavior, or open a PR with runtime evidence.
---
# Verify Sauci work
Run locally on a non-default branch with the implementation committed.
1. Start the stack once with `scripts/dev-local.sh up`; reset fixtures first when
the task changes durable state. Web is `http://127.0.0.1:3000`, admin is
`http://127.0.0.1:3001`, and MCP health is `http://127.0.0.1:3002/health`.
2. Give a fresh read-only verifier the acceptance criteria. It drives browser
surfaces with the repository browser capability, native surfaces with Maestro
or the iOS simulator skill, and APIs with `curl`. Authentication fixtures are
described by `e2e/.auth/fixtures.json` after `dev-local.sh reset`.
3. The verifier writes screenshots, traces, video, or response evidence under
`evidence/` and returns only expected, observed, evidence paths, and
`works|broken`. It never edits code.
4. If broken, fix and use a fresh verifier. Stop after three failed rounds and
report the unresolved evidence.
5. After the task works, run `npm run verify:fast`, affected E2E, and
`npm run verify:full` when local Supabase/Deno are available. Never weaken an
assertion just to pass.
6. Open a PR only after both task verification and regression checks pass. Upload
evidence to the established CI artifact or `pr-evidence` prerelease and embed
the screenshot plus a video/trace link in the PR body.
A green test suite without an independently observed task outcome is incomplete.
+23 -2
View File
@@ -1,14 +1,35 @@
node_modules
**/node_modules
.git
.gitignore
*.md
.env*
.DS_Store
dist
**/dist
.expo
android
ios
**/.expo
**/android
**/ios
*.log
.turbo
**/.turbo
coverage
.nyc_output
apps/admin
apps/mcp
!apps/mcp
!apps/mcp/**
apps/mobile
apps/supabase
apps/web
e2e
evidence
etc
infra
scripts
terraform
.agents
.claude
.codex
.ralph
-163
View File
@@ -1,163 +0,0 @@
name: Deploy Supabase to Production
on:
workflow_dispatch:
push:
branches:
- master
paths:
- 'apps/supabase/**'
- 'terraform/**'
- '.github/workflows/deploy-supabase.yml'
jobs:
deploy-database:
name: Deploy Migrations & Functions
runs-on: ubuntu-latest
strategy:
max-parallel: 1
matrix:
environment:
- name: non-prod
project_ref: itbzhrvlgvdmzbnhzhyx
- name: prod
project_ref: ckjcrkjpmhqhiucifukx
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
SUPABASE_PROJECT_REF: ${{ matrix.environment.project_ref }}
ENVIRONMENT_NAME: ${{ matrix.environment.name }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
- name: Link to ${{ matrix.environment.name }} Project
run: supabase link --project-ref $SUPABASE_PROJECT_REF --workdir apps
- name: Apply Database Migrations (${{ matrix.environment.name }})
run: supabase db push --workdir apps --include-all
- name: Sync Edge Function Secrets (${{ matrix.environment.name }})
env:
# Add secrets from GitHub Secrets
REVENUECAT_WEBHOOK_SECRET: ${{ secrets.REVENUECAT_WEBHOOK_SECRET }}
REVENUECAT_API_KEY: ${{ secrets.REVENUECAT_API_KEY }}
REVENUECAT_ENTITLEMENT_ID: ${{ secrets.REVENUECAT_ENTITLEMENT_ID }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
ADMIN_PRIVATE_KEY_JWK: ${{ secrets.ADMIN_PRIVATE_KEY_JWK }}
ADMIN_KEYS_JSON: ${{ secrets.ADMIN_KEYS_JSON }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
# Sync secrets to Supabase (only if they're set in GitHub)
secrets_to_set=""
[ -n "$REVENUECAT_WEBHOOK_SECRET" ] && secrets_to_set="$secrets_to_set REVENUECAT_WEBHOOK_SECRET=$REVENUECAT_WEBHOOK_SECRET"
[ -n "$REVENUECAT_API_KEY" ] && secrets_to_set="$secrets_to_set REVENUECAT_API_KEY=$REVENUECAT_API_KEY"
[ -n "$REVENUECAT_ENTITLEMENT_ID" ] && secrets_to_set="$secrets_to_set REVENUECAT_ENTITLEMENT_ID=$REVENUECAT_ENTITLEMENT_ID"
[ -n "$OPENROUTER_API_KEY" ] && secrets_to_set="$secrets_to_set OPENROUTER_API_KEY=$OPENROUTER_API_KEY"
[ -n "$ADMIN_PRIVATE_KEY_JWK" ] && secrets_to_set="$secrets_to_set ADMIN_PRIVATE_KEY_JWK=$ADMIN_PRIVATE_KEY_JWK"
[ -n "$ADMIN_KEYS_JSON" ] && secrets_to_set="$secrets_to_set ADMIN_KEYS_JSON=$ADMIN_KEYS_JSON"
[ -n "$DISCORD_WEBHOOK_URL" ] && secrets_to_set="$secrets_to_set DISCORD_WEBHOOK_URL=$DISCORD_WEBHOOK_URL"
if [ -n "$secrets_to_set" ]; then
echo "Syncing secrets to Supabase ($ENVIRONMENT_NAME)..."
supabase secrets set $secrets_to_set --project-ref $SUPABASE_PROJECT_REF
else
echo "No secrets to sync"
fi
- name: Deploy Edge Functions (${{ matrix.environment.name }})
run: |
# Deploy all edge functions (excluding _shared directory)
for dir in apps/supabase/functions/*/; do
function_name=$(basename "$dir")
# Skip _shared directory (it's a shared module, not a function)
if [ "$function_name" = "_shared" ]; then
echo "Skipping _shared (shared module)"
continue
fi
echo "Deploying function: $function_name to $ENVIRONMENT_NAME"
supabase functions deploy "$function_name" --no-verify-jwt --project-ref $SUPABASE_PROJECT_REF --workdir apps
done
- name: Remove Deleted Functions (${{ matrix.environment.name }})
run: |
# Get list of functions deployed on Supabase (skip header lines, extract slug column)
deployed=$(supabase functions list --project-ref $SUPABASE_PROJECT_REF | grep -E '^[a-z]' | awk '{print $1}')
# Get list of functions in the repo (excluding _shared)
local_functions=$(ls -d apps/supabase/functions/*/ 2>/dev/null | xargs -I {} basename {} | grep -v '^_shared$' || true)
# Delete any deployed functions that no longer exist in the repo
for func in $deployed; do
# Skip empty or invalid function names
[ -z "$func" ] && continue
[[ "$func" =~ ^[^a-z] ]] && continue
if ! echo "$local_functions" | grep -q "^${func}$"; then
echo "Deleting removed function from $ENVIRONMENT_NAME: $func"
supabase functions delete "$func" --project-ref $SUPABASE_PROJECT_REF
fi
done
deploy-infrastructure:
name: Deploy Infrastructure (Terraform)
runs-on: ubuntu-latest
env:
TF_VAR_supabase_access_token: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.6"
# Non-Production Environment
- name: Terraform Init (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform init
- name: Terraform Format Check
working-directory: terraform/modules/supabase
run: terraform fmt -check
- name: Terraform Validate (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform validate
- name: Terraform Plan (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform plan -out=tfplan
- name: Terraform Apply (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform apply -auto-approve tfplan
# Production Environment
- name: Terraform Init (Prod)
working-directory: terraform/environments/prod
run: terraform init
- name: Terraform Validate (Prod)
working-directory: terraform/environments/prod
run: terraform validate
- name: Terraform Plan (Prod)
working-directory: terraform/environments/prod
run: terraform plan -out=tfplan
- name: Terraform Apply (Prod)
working-directory: terraform/environments/prod
run: terraform apply -auto-approve tfplan
+89
View File
@@ -0,0 +1,89 @@
name: Verify
on:
workflow_dispatch:
pull_request:
push:
branches: [staging, main]
concurrency:
group: verify-${{ github.ref }}
cancel-in-progress: true
jobs:
branch-policy:
runs-on: ubuntu-latest
steps:
- name: Enforce promotion flow
if: github.event_name == 'pull_request'
env:
BASE_BRANCH: ${{ github.base_ref }}
HEAD_BRANCH: ${{ github.head_ref }}
run: |
if [ "$BASE_BRANCH" = "master" ]; then
echo "master is frozen; target staging or main" >&2
exit 1
fi
if [ "$BASE_BRANCH" = "main" ] && [ "$HEAD_BRANCH" != "staging" ]; then
echo "Only staging may be promoted to main" >&2
exit 1
fi
if [ "$BASE_BRANCH" != "staging" ] && [ "$BASE_BRANCH" != "main" ]; then
echo "Feature pull requests target staging; promotions target main" >&2
exit 1
fi
- name: Confirm direct verification
if: github.event_name != 'pull_request'
run: echo "Direct branch verification requested for $GITHUB_SHA"
static:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run learn:check
- run: npm run verify:fast
- name: Enforce API and mobile coverage floors
run: npm run test:coverage
- run: npm run build
backend-integration:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_DB: sauci_test
POSTGRES_USER: sauci
POSTGRES_PASSWORD: sauci_ci_only
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U sauci -d sauci_test"
--health-interval 5s
--health-timeout 3s
--health-retries 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- name: Run standalone backend integration tests
env:
DATABASE_URL: postgresql://sauci:sauci_ci_only@127.0.0.1:5432/sauci_test
run: npm run test:integration:coverage -w @sauci/api
container-contract:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Verify production Compose contract
run: infra/backend/verify-production.sh
+14 -4
View File
@@ -48,9 +48,14 @@ npm_install_log.txt
pnpm-debug.log*
# Testing
coverage/
.nyc_output/
*.lcov
coverage/
.nyc_output/
*.lcov
evidence/
e2e/.auth/
e2e/test-results/
e2e/playwright-report/
.playwright-mcp/
# Misc
*.pem
@@ -61,4 +66,9 @@ coverage/
nul
# Opencode
.opencode/
.opencode/
# Agent runtime evidence (maintained lessons remain tracked)
.ralph/activity.log
.ralph/runs/
.migration-state/
+1
View File
@@ -0,0 +1 @@
20.19.4
-115
View File
@@ -1,115 +0,0 @@
# Activity Log
## Run Summary
- 2026-01-26 14:33:54 | run=20260126-140911-55357 | iter=3 | mode=build | story=US-005 | duration=344s | status=success
- 2026-01-26 14:28:08 | run=20260126-140911-55357 | iter=2 | mode=build | story=US-004 | duration=336s | status=success
- 2026-01-26 14:22:30 | run=20260126-140911-55357 | iter=1 | mode=build | story=US-003 | duration=799s | status=success
- 2026-01-25 21:09:07 | run=20260125-204012-65223 | iter=25 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:09:04 | run=20260125-204012-65223 | iter=24 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:09:01 | run=20260125-204012-65223 | iter=23 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:58 | run=20260125-204012-65223 | iter=22 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:55 | run=20260125-204012-65223 | iter=21 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:52 | run=20260125-204012-65223 | iter=20 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:49 | run=20260125-204012-65223 | iter=19 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:46 | run=20260125-204012-65223 | iter=18 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:42 | run=20260125-204012-65223 | iter=17 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:39 | run=20260125-204012-65223 | iter=16 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:37 | run=20260125-204012-65223 | iter=15 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:34 | run=20260125-204012-65223 | iter=14 | mode=build | story=US-003 | duration=0s | status=error
- 2026-01-25 21:08:31 | run=20260125-204012-65223 | iter=13 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:28 | run=20260125-204012-65223 | iter=12 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:25 | run=20260125-204012-65223 | iter=11 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:22 | run=20260125-204012-65223 | iter=10 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:19 | run=20260125-204012-65223 | iter=9 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:16 | run=20260125-204012-65223 | iter=8 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:13 | run=20260125-204012-65223 | iter=7 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:10 | run=20260125-204012-65223 | iter=6 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:07 | run=20260125-204012-65223 | iter=5 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:04 | run=20260125-204012-65223 | iter=4 | mode=build | story=US-003 | duration=1s | status=error
- 2026-01-25 21:08:01 | run=20260125-204012-65223 | iter=3 | mode=build | story=US-003 | duration=2s | status=error
- 2026-01-25 21:07:57 | run=20260125-204012-65223 | iter=2 | mode=build | story=US-002 | duration=1250s | status=success
- 2026-01-25 20:47:05 | run=20260125-204012-65223 | iter=1 | mode=build | story=US-001 | duration=413s | status=success
## Events
[2026-01-25 20:40:12] ITERATION 1 start (mode=build story=US-001)
[2026-01-25 20:40:36] Start US-001 inventory oversized files
[2026-01-25 20:42:52] Added oversized file inventory and split plan
[2026-01-25 20:43:19] Completed verification commands (test/lint/typecheck/build)
[2026-01-25 20:46:15] Updated progress log for US-001
[2026-01-25 20:47:05] ITERATION 1 end (duration=413s)
[2026-01-25 20:47:07] ITERATION 2 start (mode=build story=US-002)
[2026-01-25 20:47:25] Start US-002 work
[2026-01-25 20:59:27] Refactored swipe screen into feature modules (hooks/services/components)
[2026-01-25 21:02:54] Completed verification commands (test/lint/typecheck/build/web)
[2026-01-25 21:06:57] Updated progress log for US-002
[2026-01-25 21:07:57] ITERATION 2 end (duration=1250s)
[2026-01-25 21:07:59] ITERATION 3 start (mode=build story=US-003)
[2026-01-25 21:08:01] ITERATION 3 end (duration=2s)
[2026-01-25 21:08:03] ITERATION 4 start (mode=build story=US-003)
[2026-01-25 21:08:04] ITERATION 4 end (duration=1s)
[2026-01-25 21:08:06] ITERATION 5 start (mode=build story=US-003)
[2026-01-25 21:08:07] ITERATION 5 end (duration=1s)
[2026-01-25 21:08:09] ITERATION 6 start (mode=build story=US-003)
[2026-01-25 21:08:10] ITERATION 6 end (duration=1s)
[2026-01-25 21:08:12] ITERATION 7 start (mode=build story=US-003)
[2026-01-25 21:08:13] ITERATION 7 end (duration=1s)
[2026-01-25 21:08:15] ITERATION 8 start (mode=build story=US-003)
[2026-01-25 21:08:16] ITERATION 8 end (duration=1s)
[2026-01-25 21:08:18] ITERATION 9 start (mode=build story=US-003)
[2026-01-25 21:08:19] ITERATION 9 end (duration=1s)
[2026-01-25 21:08:21] ITERATION 10 start (mode=build story=US-003)
[2026-01-25 21:08:22] ITERATION 10 end (duration=1s)
[2026-01-25 21:08:24] ITERATION 11 start (mode=build story=US-003)
[2026-01-25 21:08:25] ITERATION 11 end (duration=1s)
[2026-01-25 21:08:27] ITERATION 12 start (mode=build story=US-003)
[2026-01-25 21:08:28] ITERATION 12 end (duration=1s)
[2026-01-25 21:08:30] ITERATION 13 start (mode=build story=US-003)
[2026-01-25 21:08:31] ITERATION 13 end (duration=1s)
[2026-01-25 21:08:33] ITERATION 14 start (mode=build story=US-003)
[2026-01-25 21:08:34] ITERATION 14 end (duration=0s)
[2026-01-25 21:08:36] ITERATION 15 start (mode=build story=US-003)
[2026-01-25 21:08:36] ITERATION 15 end (duration=0s)
[2026-01-25 21:08:39] ITERATION 16 start (mode=build story=US-003)
[2026-01-25 21:08:39] ITERATION 16 end (duration=0s)
[2026-01-25 21:08:42] ITERATION 17 start (mode=build story=US-003)
[2026-01-25 21:08:42] ITERATION 17 end (duration=0s)
[2026-01-25 21:08:45] ITERATION 18 start (mode=build story=US-003)
[2026-01-25 21:08:46] ITERATION 18 end (duration=1s)
[2026-01-25 21:08:48] ITERATION 19 start (mode=build story=US-003)
[2026-01-25 21:08:49] ITERATION 19 end (duration=1s)
[2026-01-25 21:08:51] ITERATION 20 start (mode=build story=US-003)
[2026-01-25 21:08:52] ITERATION 20 end (duration=1s)
[2026-01-25 21:08:54] ITERATION 21 start (mode=build story=US-003)
[2026-01-25 21:08:55] ITERATION 21 end (duration=1s)
[2026-01-25 21:08:57] ITERATION 22 start (mode=build story=US-003)
[2026-01-25 21:08:58] ITERATION 22 end (duration=0s)
[2026-01-25 21:09:00] ITERATION 23 start (mode=build story=US-003)
[2026-01-25 21:09:00] ITERATION 23 end (duration=0s)
[2026-01-25 21:09:03] ITERATION 24 start (mode=build story=US-003)
[2026-01-25 21:09:04] ITERATION 24 end (duration=0s)
[2026-01-25 21:09:06] ITERATION 25 start (mode=build story=US-003)
[2026-01-25 21:09:06] ITERATION 25 end (duration=0s)
[2026-01-26 14:09:11] ITERATION 1 start (mode=build story=US-003)
[2026-01-26 14:09:37] Start US-003 refactor oversized shared components
[2026-01-26 14:14:12] Refactored IntensitySlider into subcomponents and extracted constants/styles
[2026-01-26 14:17:23] Attempted Expo web for browser verification; expo start --web failed with localStorage.getItem error
[2026-01-26 14:17:58] Ran quality gates and build (npm test failed: missing script; npm run lint failed with existing issues; npm run typecheck/build passed)
[2026-01-26 14:21:26] Updated progress log for US-003 with commit/tests
[2026-01-26 14:22:30] ITERATION 1 end (duration=799s)
[2026-01-26 14:22:32] ITERATION 2 start (mode=build story=US-004)
[2026-01-26 14:22:45] Start US-004 work
[2026-01-26 14:25:08] Refactored useSwipeScreen helpers
[2026-01-26 14:26:13] Ran tests and build for US-004
[2026-01-26 14:26:51] Updated progress log for US-004
[2026-01-26 14:27:24] Committed US-004 changes
[2026-01-26 14:28:08] ITERATION 2 end (duration=336s)
[2026-01-26 14:28:10] ITERATION 3 start (mode=build story=US-005)
[2026-01-26 14:28:59] Start US-005 validation run (manual log; helper missing)
[2026-01-26 14:29:29] Added root test script for US-005
[2026-01-26 14:30:41] Fixed lint hook violations for US-005
[2026-01-26 14:31:01] Ran npm test, lint, typecheck, build for US-005
[2026-01-26 14:31:14] Added AGENTS.md operational notes
[2026-01-26 14:32:00] Re-ran npm test after lint fixes
[2026-01-26 14:32:45] Updated progress log for US-005
[2026-01-26 14:33:54] ITERATION 3 end (duration=344s)
+3
View File
@@ -18,3 +18,6 @@
## Learned Signs
Durable lessons are promoted through `docs/agents/learnings.md`. Ralph runtime
logs are evidence, not instruction. Read promoted artifacts through `AGENTS.md`;
do not add permanent signs from an unreproduced failure.
@@ -1,34 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 1
- Mode: build
- Story: US-001: Inventory oversized files and refactor plan
- Started: 2026-01-25 20:40:12
- Ended: 2026-01-25 20:47:05
- Duration: 413s
- Status: success
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-1.log
## Git
- Head (before): 45499edc9a0b78094601f577c707da2374b1765d
- Head (after): 209dc652d2aaf04d31fe6dd187a28cf2da8ffd67
### Commits
- 209dc65 chore(ralph): update progress log
- 06dfc5b docs(inventory): add oversized file plan
### Changed Files (commits)
- .ralph/activity.log
- .ralph/progress.md
- docs/oversized-file-inventory.md
### Uncommitted Changes
- .ralph/activity.log
- apps/mobile/android/app/build.gradle
- apps/mobile/app.json
- apps/mobile/ios/Sauci.xcodeproj/project.pbxproj
- .agents/
- .codex/
- .ralph/guardrails.md
@@ -1,34 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 10
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:21
- Ended: 2026-01-25 21:08:22
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-10.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,35 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 11
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:24
- Ended: 2026-01-25 21:08:25
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-11.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,36 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 12
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:27
- Ended: 2026-01-25 21:08:28
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-12.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,37 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 13
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:30
- Ended: 2026-01-25 21:08:31
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-13.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,38 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 14
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:33
- Ended: 2026-01-25 21:08:33
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-14.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,39 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 15
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:36
- Ended: 2026-01-25 21:08:36
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-15.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,40 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 16
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:39
- Ended: 2026-01-25 21:08:39
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-16.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,41 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 17
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:42
- Ended: 2026-01-25 21:08:42
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-17.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,42 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 18
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:45
- Ended: 2026-01-25 21:08:46
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-18.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,43 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 19
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:48
- Ended: 2026-01-25 21:08:49
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-19.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,81 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 2
- Mode: build
- Story: US-002: Refactor screen-level files into layer-based modules
- Started: 2026-01-25 20:47:07
- Ended: 2026-01-25 21:07:57
- Duration: 1250s
- Status: success
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-2.log
## Git
- Head (before): 209dc652d2aaf04d31fe6dd187a28cf2da8ffd67
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- a01079a chore(ralph): update progress logs
- 8d4d9d8 refactor(swipe): split swipe screen modules
### Changed Files (commits)
- .agents/ralph/PROMPT_build.md
- .agents/ralph/README.md
- .agents/ralph/agents.sh
- .agents/ralph/config.sh
- .agents/ralph/diagram.svg
- .agents/ralph/log-activity.sh
- .agents/ralph/loop.sh
- .agents/ralph/ralph.webp
- .agents/ralph/references/CONTEXT_ENGINEERING.md
- .agents/ralph/references/GUARDRAILS.md
- .agents/tasks/prd-mobile-refactor.json
- .codex/skills/commit/SKILL.md
- .codex/skills/commit/references/commit_examples.md
- .codex/skills/dev-browser/SKILL.md
- .codex/skills/dev-browser/bun.lock
- .codex/skills/dev-browser/package-lock.json
- .codex/skills/dev-browser/package.json
- .codex/skills/dev-browser/references/scraping.md
- .codex/skills/dev-browser/scripts/start-relay.ts
- .codex/skills/dev-browser/scripts/start-server.ts
- .codex/skills/dev-browser/server.sh
- .codex/skills/dev-browser/src/client.ts
- .codex/skills/dev-browser/src/index.ts
- .codex/skills/dev-browser/src/relay.ts
- .codex/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts
- .codex/skills/dev-browser/src/snapshot/browser-script.ts
- .codex/skills/dev-browser/src/snapshot/index.ts
- .codex/skills/dev-browser/src/snapshot/inject.ts
- .codex/skills/dev-browser/src/types.ts
- .codex/skills/dev-browser/tsconfig.json
- .codex/skills/dev-browser/vitest.config.ts
- .codex/skills/prd/SKILL.md
- .ralph/activity.log
- .ralph/guardrails.md
- .ralph/progress.md
- .ralph/runs/run-20260125-204012-65223-iter-1.md
- apps/mobile/android/app/build.gradle
- apps/mobile/app.json
- apps/mobile/app/(app)/swipe.tsx
- apps/mobile/ios/Sauci.xcodeproj/project.pbxproj
- apps/mobile/src/features/swipe/SwipeScreen.tsx
- apps/mobile/src/features/swipe/components/SwipeBlockedState.tsx
- apps/mobile/src/features/swipe/components/SwipeCardStack.tsx
- apps/mobile/src/features/swipe/components/SwipeCaughtUpState.tsx
- apps/mobile/src/features/swipe/components/SwipeDailyLimitState.tsx
- apps/mobile/src/features/swipe/components/SwipeHeader.tsx
- apps/mobile/src/features/swipe/components/SwipeInfoStateLayout.tsx
- apps/mobile/src/features/swipe/components/SwipeLoadingState.tsx
- apps/mobile/src/features/swipe/components/SwipeNoPacksState.tsx
- apps/mobile/src/features/swipe/components/SwipeNoPartnerState.tsx
- apps/mobile/src/features/swipe/components/SwipeQuestionCard.tsx
- apps/mobile/src/features/swipe/components/SwipeUploadOverlay.tsx
- apps/mobile/src/features/swipe/hooks/useProgressShimmer.ts
- apps/mobile/src/features/swipe/hooks/useSwipeScreen.ts
- apps/mobile/src/features/swipe/services/swipeService.ts
- apps/mobile/src/features/swipe/types.ts
### Uncommitted Changes
- .ralph/activity.log
@@ -1,44 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 20
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:51
- Ended: 2026-01-25 21:08:52
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-20.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,45 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 21
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:54
- Ended: 2026-01-25 21:08:55
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-21.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,46 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 22
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:57
- Ended: 2026-01-25 21:08:57
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-22.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-21.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,47 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 23
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:09:00
- Ended: 2026-01-25 21:09:00
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-23.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-21.md
- .ralph/runs/run-20260125-204012-65223-iter-22.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,48 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 24
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:09:03
- Ended: 2026-01-25 21:09:03
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-24.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-21.md
- .ralph/runs/run-20260125-204012-65223-iter-22.md
- .ralph/runs/run-20260125-204012-65223-iter-23.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,49 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 25
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:09:06
- Ended: 2026-01-25 21:09:06
- Duration: 0s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-25.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-21.md
- .ralph/runs/run-20260125-204012-65223-iter-22.md
- .ralph/runs/run-20260125-204012-65223-iter-23.md
- .ralph/runs/run-20260125-204012-65223-iter-24.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
@@ -1,27 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 3
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:07:59
- Ended: 2026-01-25 21:08:01
- Duration: 2s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-3.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
@@ -1,28 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 4
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:03
- Ended: 2026-01-25 21:08:04
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-4.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
@@ -1,29 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 5
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:06
- Ended: 2026-01-25 21:08:07
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-5.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
@@ -1,30 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 6
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:09
- Ended: 2026-01-25 21:08:10
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-6.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
@@ -1,31 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 7
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:12
- Ended: 2026-01-25 21:08:13
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-7.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
@@ -1,32 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 8
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:15
- Ended: 2026-01-25 21:08:16
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-8.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
@@ -1,33 +0,0 @@
# Ralph Run Summary
- Run ID: 20260125-204012-65223
- Iteration: 9
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-25 21:08:18
- Ended: 2026-01-25 21:08:19
- Duration: 1s
- Status: error
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260125-204012-65223-iter-9.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): a01079a7c3c6c727a55cd46a72543e86f2378932
### Commits
- (none)
### Changed Files (commits)
- (none)
### Uncommitted Changes
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
@@ -1,59 +0,0 @@
# Ralph Run Summary
- Run ID: 20260126-140911-55357
- Iteration: 1
- Mode: build
- Story: US-003: Refactor oversized shared components
- Started: 2026-01-26 14:09:11
- Ended: 2026-01-26 14:22:30
- Duration: 799s
- Status: success
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260126-140911-55357-iter-1.log
## Git
- Head (before): a01079a7c3c6c727a55cd46a72543e86f2378932
- Head (after): 70449659cf1ded7041b71ecf07aa2e7bc76f8346
### Commits
- 7044965 chore(ralph): log US-003 progress
- f302cb6 refactor(ui): split IntensitySlider pieces
### Changed Files (commits)
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/progress.md
- .ralph/runs/run-20260125-204012-65223-iter-10.md
- .ralph/runs/run-20260125-204012-65223-iter-11.md
- .ralph/runs/run-20260125-204012-65223-iter-12.md
- .ralph/runs/run-20260125-204012-65223-iter-13.md
- .ralph/runs/run-20260125-204012-65223-iter-14.md
- .ralph/runs/run-20260125-204012-65223-iter-15.md
- .ralph/runs/run-20260125-204012-65223-iter-16.md
- .ralph/runs/run-20260125-204012-65223-iter-17.md
- .ralph/runs/run-20260125-204012-65223-iter-18.md
- .ralph/runs/run-20260125-204012-65223-iter-19.md
- .ralph/runs/run-20260125-204012-65223-iter-2.md
- .ralph/runs/run-20260125-204012-65223-iter-20.md
- .ralph/runs/run-20260125-204012-65223-iter-21.md
- .ralph/runs/run-20260125-204012-65223-iter-22.md
- .ralph/runs/run-20260125-204012-65223-iter-23.md
- .ralph/runs/run-20260125-204012-65223-iter-24.md
- .ralph/runs/run-20260125-204012-65223-iter-25.md
- .ralph/runs/run-20260125-204012-65223-iter-3.md
- .ralph/runs/run-20260125-204012-65223-iter-4.md
- .ralph/runs/run-20260125-204012-65223-iter-5.md
- .ralph/runs/run-20260125-204012-65223-iter-6.md
- .ralph/runs/run-20260125-204012-65223-iter-7.md
- .ralph/runs/run-20260125-204012-65223-iter-8.md
- .ralph/runs/run-20260125-204012-65223-iter-9.md
- apps/mobile/src/components/ui/IntensitySlider.tsx
- apps/mobile/src/components/ui/IntensitySlider/IntensitySliderCurrentCard.tsx
- apps/mobile/src/components/ui/IntensitySlider/IntensitySliderInfoModal.tsx
- apps/mobile/src/components/ui/IntensitySlider/IntensitySliderLevels.tsx
- apps/mobile/src/components/ui/IntensitySlider/IntensitySliderTrack.tsx
- apps/mobile/src/components/ui/IntensitySlider/constants.ts
- apps/mobile/src/components/ui/IntensitySlider/styles.ts
### Uncommitted Changes
- .ralph/activity.log
@@ -1,32 +0,0 @@
# Ralph Run Summary
- Run ID: 20260126-140911-55357
- Iteration: 2
- Mode: build
- Story: US-004: Refactor oversized hooks/services/utils
- Started: 2026-01-26 14:22:32
- Ended: 2026-01-26 14:28:08
- Duration: 336s
- Status: success
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260126-140911-55357-iter-2.log
## Git
- Head (before): 70449659cf1ded7041b71ecf07aa2e7bc76f8346
- Head (after): 1370576dd59180f3b13e0a8d99255dfa96210d60
### Commits
- 1370576 chore(ralph): log commit action
- 6957502 chore(ralph): update progress logs
- 5a9defb refactor(swipe): split useSwipeScreen helpers
### Changed Files (commits)
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/progress.md
- .ralph/runs/run-20260126-140911-55357-iter-1.md
- apps/mobile/src/features/swipe/hooks/useSwipeScreen.ts
- apps/mobile/src/features/swipe/utils/useSwipeScreen-helpers.ts
### Uncommitted Changes
- .ralph/activity.log
@@ -1,33 +0,0 @@
# Ralph Run Summary
- Run ID: 20260126-140911-55357
- Iteration: 3
- Mode: build
- Story: US-005: Validate refactor quality gates
- Started: 2026-01-26 14:28:10
- Ended: 2026-01-26 14:33:54
- Duration: 344s
- Status: success
- Log: /Users/jjbrunton/Projects/Sauci/.ralph/runs/run-20260126-140911-55357-iter-3.log
## Git
- Head (before): 1370576dd59180f3b13e0a8d99255dfa96210d60
- Head (after): ce419fee8b21a37f83e746e6d09ac59692c62e8c
### Commits
- ce419fe chore(logs): update US-005 progress
- 368399f fix(validation): satisfy quality gate checks
### Changed Files (commits)
- .agents/tasks/prd-mobile-refactor.json
- .ralph/activity.log
- .ralph/progress.md
- .ralph/runs/run-20260126-140911-55357-iter-2.md
- AGENTS.md
- apps/mobile/src/components/questions/QuestionCardAudio.tsx
- apps/mobile/src/features/chat/components/MessageContent.tsx
- package.json
### Uncommitted Changes
- .ralph/activity.log
+81 -15
View File
@@ -1,21 +1,87 @@
# AGENTS
# Sauci agent map
## Verification
- npm test (runs mobile Jest suite)
- npm run lint
- npm run typecheck
- npm run build
Sauci is a couples connection product: an Expo mobile app, React admin, Next.js
web app, standalone Node/PostgreSQL backend, hosted Supabase Auth, and an internal
MCP service in one npm/Turborepo workspace.
## Database Migrations - MANDATORY RULES
## Golden rules
**NEVER use `mcp__sauci-prod__apply_migration` or `mcp__sauci-non-prod__apply_migration`.** These create migration records in the remote database without local files, which breaks CI/CD deployments.
1. Read the nearest `AGENTS.md` before changing a scoped area.
2. Preserve tenant and couple isolation. Never bypass RLS, role checks, or auth.
3. Product-data schema changes are committed SQL files in `apps/api/drizzle/`.
Hosted Supabase owns Auth only; do not deploy product-data migrations or Edge
Functions there. Never execute remote DDL through MCP or provider tools.
4. Never target production from local development or E2E. Database integration
tests must fail closed unless `DATABASE_URL` resolves to loopback. Authenticated
native acceptance may use only the designated non-production Auth project.
5. Read `apps/mobile/DESIGN.md` before mobile UI work.
6. Keep shared package exports type-only and route them through `src/index.ts`.
7. Do not commit secrets, generated evidence, local logs, or runtime state.
8. Implement in a task-specific git worktree created from current `staging`,
not in the shared `staging` checkout. After the work is verified, commit the
scoped changes, merge the completed task branch back into local `staging`,
push `staging`, and prove local and remote commit parity.
9. Before merging, identify every deployed app or service the change can affect
and prove the relevant compatibility contracts still hold. Preserve deployed
interfaces and supported client behavior; repository checks alone do not
prove a live change is safe.
10. Delegate every substantive repository task to at least one project agent so
detailed exploration or execution stays out of the primary context.
"Substantive" includes repository exploration, multi-file reasoning,
implementation, test execution, or contract/risk analysis. Use `repo_scout`
for read-only investigation and `implementation` for bounded changes; use
the additional roles according to `docs/agents/routing.md`. The primary
agent retains scope, acceptance, review, and the final answer. Skip
delegation only for trivial conversation, a single obvious lookup, or a
mechanical one-line edit whose coordination cost exceeds the work.
11. Use high-effort reviewers only for the risk triggers in
`docs/agents/routing.md`.
**NEVER run DDL (`CREATE`, `ALTER`, `DROP`) via `mcp__*__execute_sql`.** This changes the schema with no migration tracking.
## Repository map
**All schema changes must be local migration files:**
1. Run `supabase migration new <name>` to create the file
2. Edit the file in `apps/supabase/migrations/`
3. Use idempotent SQL (`IF NOT EXISTS`, `CREATE OR REPLACE`)
4. Commit the file — CI deploys it automatically
| Area | Purpose | Local instructions |
|---|---|---|
| `apps/mobile` | Expo React Native product | `apps/mobile/AGENTS.md` |
| `apps/admin` | Vite administration UI | `apps/admin/AGENTS.md` |
| `apps/web` | Next.js marketing/redemption UI | `apps/web/AGENTS.md` |
| `apps/api` | standalone API, worker, PostgreSQL migrations | repository rules |
| `apps/supabase` | legacy data-plane history; hosted Auth configuration only | `apps/supabase/AGENTS.md` |
| `apps/mcp` | internal administrative MCP service | `apps/mcp/AGENTS.md` |
| `packages/shared` | shared TypeScript contracts | `packages/shared/AGENTS.md` |
| `e2e` | cross-application Playwright flows | `e2e/README.md` |
| `docs` | maintained system of record | `docs/index.md` |
**`execute_sql` is ONLY for SELECT queries.** No exceptions.
## Workflows
- Implementation: create a task worktree from current `staging`; commit verified
work, merge it back to local `staging`, push `staging`, and verify remote parity.
- Bootstrap: `npm ci`
- Full local stack: `scripts/dev-local.sh up`
- Fast checks: `npm run verify:fast`
- Authoritative repository checks: `npm run verify:full`
- Running-stack E2E: `npm run verify:e2e`
- Everything: `npm run verify`
- Before a PR: use `.codex/skills/verify/SKILL.md`
When a check cannot run because a prerequisite is absent, report it as an
environment limitation; do not call it a passing product check.
## Where to look
| Question | Source of truth |
|---|---|
| System boundaries and data flow | `docs/architecture/system.md` |
| Local services, ports, fixtures | `docs/development/local-stack.md` |
| Test layers and required gates | `docs/testing/strategy.md` |
| Database migration safety | `docs/operations/database-migrations.md` |
| Agent roles and effort routing | `docs/agents/routing.md` |
| Learning promotion | `docs/agents/learnings.md` |
| Mobile visual language | `apps/mobile/DESIGN.md` |
| Domain documentation index | `docs/index.md` |
## Documentation discipline
Update the maintained doc when a contract, command, port, or invariant changes.
Plans and historical notes are evidence, not current architecture. A repeated
lesson becomes permanent only after it is reproduced and promoted into a doc,
skill, fixture, or executable check.
+4 -357
View File
@@ -1,358 +1,5 @@
# CLAUDE.md
# Claude compatibility entrypoint
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Important: Design Guidelines
**Always read `apps/mobile/DESIGN.md` before making UI/UX changes to the mobile app.** This file contains the design system, visual language, and styling patterns that must be followed for consistency. Agents and subagents should read this file when:
- Creating new screens or components
- Modifying existing UI elements
- Adding animations or visual effects
- Working with colors, typography, or spacing
## Project Overview
Sauci is a couples relationship app that helps partners communicate better and grow closer together. Users swipe on thoughtful questions (yes/no/maybe), and when both partners answer positively, a "match" is created that unlocks a chat thread for that topic.
## Commands
```bash
# Install dependencies (from root)
npm install
# Start all apps in development
npm run dev
# Run specific apps
cd apps/mobile && npm run dev # Expo mobile app
cd apps/admin && npm run dev # React Admin dashboard
# Type checking
npm run typecheck
# Linting
npm run lint
# Build all
npm run build
```
### Mobile-specific commands
```bash
cd apps/mobile
npm run ios # Run on iOS simulator
npm run android # Run on Android emulator
npm run web # Run in browser
```
### Supabase commands
```bash
supabase start # Start local Supabase
supabase db reset # Reset DB and run all migrations
supabase functions serve # Run edge functions locally
supabase migration new <name> # Create new migration
```
### Supabase Environments
There are two Supabase projects configured via MCP servers:
| Environment | Project Ref | URL | MCP Server |
|-------------|-------------|-----|------------|
| **Production** | `ckjcrkjpmhqhiucifukx` | https://ckjcrkjpmhqhiucifukx.supabase.co | `sauci-prod` |
| **Non-Production** | `itbzhrvlgvdmzbnhzhyx` | https://itbzhrvlgvdmzbnhzhyx.supabase.co | `sauci-non-prod` |
When using Claude Code with MCP tools:
- Use `mcp__sauci-prod__*` tools for production database operations
- Use `mcp__sauci-non-prod__*` tools for development/staging database operations
- **Always verify which environment you're targeting before running destructive operations**
### Database Migrations - CRITICAL
> **STOP: Read this entire section before making ANY database schema change.**
#### The Golden Rule
**ALL schema changes MUST go through local migration files.** No exceptions.
#### Forbidden Actions
- **NEVER call `apply_migration` via MCP** (either `mcp__sauci-prod__apply_migration` or `mcp__sauci-non-prod__apply_migration`). This creates migration entries in the remote database with NO local file, which breaks `supabase db push` in CI and causes deployment failures that are painful to fix.
- **NEVER run DDL statements (`CREATE`, `ALTER`, `DROP`) via `execute_sql`**. This changes the schema without any migration tracking at all.
- **NEVER manually INSERT into `supabase_migrations.schema_migrations`** unless repairing a broken migration history (and only with explicit user approval).
#### Why This Matters
The CI/CD pipeline runs `supabase db push --include-all` which compares local migration files against the remote `schema_migrations` table. If the remote has migrations that don't exist locally, or local files exist without remote entries, the deployment **will fail** and block all subsequent deployments.
**A single `apply_migration` call can break deployments for the entire project.**
#### Correct Workflow for Schema Changes
```bash
# 1. Create migration file locally
supabase migration new <descriptive_name>
# 2. Edit the migration file in apps/supabase/migrations/
# 3. Make migrations idempotent where possible
# Use: CREATE OR REPLACE, IF NOT EXISTS, DROP IF EXISTS
# This prevents failures if re-run
# 4. Test locally
supabase db reset
# 5. Commit the migration file to git
# 6. Deploy happens automatically via CI on push to master
# OR deploy manually:
supabase link --project-ref <project_ref>
supabase db push
```
#### MCP Tools - What IS Allowed
- **Reading data:** `execute_sql` with SELECT queries only
- **Exploring schema:** `list_tables`, `list_migrations`, `list_extensions`
- **Deploying edge functions:** `deploy_edge_function`
- **Generating types:** `generate_typescript_types`
- **Checking advisors:** `get_advisors`
#### If You Accidentally Used `apply_migration`
If a migration was applied via MCP, you must immediately:
1. Retrieve the migration SQL from `schema_migrations.statements`
2. Create a matching local file: `apps/supabase/migrations/<version>_<name>.sql`
3. Commit and push the file so CI can reconcile
## Architecture
### Monorepo Structure (Turborepo)
- `apps/mobile` - Expo React Native app (expo-router for navigation)
- `apps/admin` - React Admin dashboard for content management
- `apps/supabase` - Database migrations and edge functions
- `packages/shared` - Shared TypeScript types (`@sauci/shared`)
### Mobile App Architecture
- **Routing**: Expo Router with file-based routing (`app/` directory)
- `(auth)/` - Authentication screens
- `(app)/` - Main app screens (protected)
- **State Management**: Zustand stores in `src/store/index.ts`
- `useAuthStore` - User, couple, partner state
- `useMatchStore` - Matches and notifications
- `usePacksStore` - Question packs and enabled packs per couple
- `useMessageStore` - Chat messages and unread counts
- `useSubscriptionStore` - RevenueCat subscription state
- **Backend**: Supabase client in `src/lib/supabase.ts`
- **UI Components**: Glass-morphism themed components in `src/components/ui/`
### Data Model (Core Entities)
- **Profile** - User profile extending Supabase auth (has `couple_id`)
- **Couple** - Two users linked via invite code
- **QuestionPack** - Collection of questions (can be premium)
- **Question** - Individual question with intensity level (1-5)
- **Response** - User's answer (yes/no/maybe) to a question
- **Match** - Created when both partners answer positively (yes_yes, yes_maybe, maybe_maybe)
### Edge Functions
Edge functions in `apps/supabase/functions/` handle complex operations:
- `submit-response` - Saves response, checks for matches, triggers notifications
- `manage-couple` - Join/leave couple operations
- `delete-relationship` - Deletes couple data (matches, messages, media) while keeping accounts
- `delete-account` - Permanently deletes user account and all associated data (Apple App Store requirement)
- `send-notification` - Push notifications for matches
- `send-nudge-notification` - Partner nudge notifications (rate limited to once per 12 hours)
- `send-unpaired-reminder` - Daily cron reminder for unpaired users to share invite code
- `send-catchup-reminder` - Daily cron reminder for "behind" partners with escalating frequency
- `revenuecat-webhook` - Subscription webhook handler
- `sync-subscription` - Syncs RevenueCat subscription status
- `redeem-code` - Redeems promotional codes for premium access (website only)
#### Edge Function Deployment Guidelines
When deploying edge functions via MCP tools (`mcp__sauci-*__deploy_edge_function`):
**IMPORTANT: Always use `verify_jwt: false`** for all edge functions. Supabase's built-in JWT verification causes 401 errors even with valid tokens. Instead, handle authentication manually in the function code using:
```typescript
const { data: { user }, error: authError } = await supabase.auth.getUser(
authHeader.replace("Bearer ", "")
);
```
This approach:
- Gives better error messages
- Allows custom auth handling
- Avoids mysterious 401 failures
**Current function settings (all should be verify_jwt: false):**
| Function | verify_jwt | Auth Method |
|----------|------------|-------------|
| `manage-couple` | false | Manual via getUser() |
| `submit-response` | false | Manual via getUser() |
| `sync-subscription` | false | Manual via getUser() |
| `delete-relationship` | false | Manual via getUser() |
| `delete-account` | false | Manual via getUser() |
| `redeem-code` | false | Manual via getUser() |
| `revenuecat-webhook` | false | Webhook signature validation |
| `send-notification` | false | Internal trigger (no auth) |
| `send-message-notification` | false | Internal trigger (no auth) |
| `send-nudge-notification` | false | Manual via getUser() |
| `send-unpaired-reminder` | false | Cron trigger (no auth) |
| `send-catchup-reminder` | false | Cron trigger (no auth) |
### Database Best Practices
- **Always use `.maybeSingle()` instead of `.single()`** when the row might not exist. `.single()` throws an error if 0 or >1 rows are returned.
- **Profile creation trigger**: The `on_auth_user_created` trigger automatically creates a profile when a user signs up. Never assume a profile exists without checking.
- **Check for null profiles** in edge functions before performing operations.
### Cron Jobs (pg_cron) - IMPORTANT
**NEVER hardcode Supabase URLs in cron job migrations.** Migrations run on both prod and non-prod, so a hardcoded production URL will cause non-prod cron jobs to hit production (double-firing notifications, duplicate processing, etc.).
**Correct pattern** — use `get_supabase_edge_function_url()`:
```sql
SELECT cron.schedule(
'my-cron-job',
'*/5 * * * *',
format(
$cmd$
SELECT net.http_post(
url := %L,
headers := jsonb_build_object('Content-Type', 'application/json'),
body := '{}'::jsonb
)
$cmd$,
get_supabase_edge_function_url('my-edge-function')
)
);
```
This resolves the URL from `app_config.supabase_url` at migration-time, so each environment targets its own edge functions.
**If adding a new environment**, you must seed the URL before running migrations:
```sql
UPDATE app_config SET supabase_url = 'https://<project-ref>.supabase.co';
```
### Key Patterns
- Responses use UPSERT with `onConflict: "user_id,question_id"`
- Match detection happens in `submit-response` edge function after each response
- Real-time subscriptions used for chat messages
- RevenueCat handles in-app purchases and subscription management
- RLS policies enforce couple-level data isolation
### Redemption Code System
Promotional codes that grant users premium access. **Codes can only be redeemed on the website, NOT in the mobile app.**
**Database Tables:**
- `redemption_codes` - Stores codes with max_uses, current_uses, expiration, active status
- `code_redemptions` - Tracks which users redeemed which codes
**Components:**
- **Admin UI** (`apps/admin/src/pages/RedemptionCodesPage.tsx`) - Super admins can generate, manage, and view redemption codes
- **Edge Function** (`apps/supabase/functions/redeem-code/`) - Validates and processes code redemption (unauthenticated, accepts email + code)
- **Database Function** (`redeem_code_by_email(p_email, p_code)`) - Atomic redemption with validation (active, not expired, has uses remaining, user hasn't redeemed)
- **Web UI** (`apps/web/app/redeem/page.tsx`) - Public redemption page
**Flow:**
1. Admin generates code in admin dashboard
2. User visits `/redeem` on website and enters email + code
3. `redeem-code` edge function calls `redeem_code_by_email()` database function
4. Database function looks up user by email, validates code, and sets `profiles.is_premium = true`
**Important:** Do NOT add redemption code UI to the mobile app. Apple requires in-app purchases for premium features purchased within iOS apps.
## Mobile UI - DO NOT CHANGE
### Tab Bar Background
**NEVER set `backgroundColor: 'transparent'` on the tab bar.** Always use a semi-transparent solid color:
```typescript
backgroundColor: 'rgba(18, 18, 18, 0.85)',
```
The `BlurView` in `tabBarBackground` is unreliable and can fail during re-renders/transitions, causing a fully transparent tab bar. The solid rgba background acts as a fallback while still allowing the blur effect on top.
**Location:** `apps/mobile/app/(app)/_layout.tsx` - `tabBarStyle`
## Infrastructure as Code (Terraform)
Supabase project configuration is managed via Terraform in the `terraform/` directory. This includes auth settings, OAuth providers, email templates, SMTP config, rate limits, and more.
**Directory structure:**
```
terraform/
├── modules/
│ └── supabase/ # Shared configuration module
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── templates/ # Email templates
└── environments/
├── prod/ # Production (ckjcrkjpmhqhiucifukx)
└── non-prod/ # Non-production (itbzhrvlgvdmzbnhzhyx)
```
**When to update Terraform:**
- Changing auth settings (OAuth providers, JWT expiry, MFA settings)
- Modifying email templates or subjects
- Updating rate limits or security settings
- Changing SMTP configuration
- Modifying storage or API settings
**Workflow (always test in non-prod first):**
```bash
# 1. Apply to non-prod first
cd terraform/environments/non-prod
export TF_VAR_supabase_access_token=sbp_xxxxx
terraform plan
terraform apply
# 2. Verify changes work in non-prod
# 3. Apply to prod
cd terraform/environments/prod
terraform plan
terraform apply
```
**Important:** After making Terraform changes, remind the user to:
1. Commit the updated `terraform/` files
2. The GitHub Action will auto-apply on merge to master (non-prod first, then prod)
**Do NOT use Terraform for:**
- Database schema changes (use migrations)
- Edge function deployment (use CLI or MCP)
- Storage bucket creation (use migrations)
## Edge Function Secrets
Edge functions use secrets stored in Supabase. These are synced from GitHub Secrets during CI/CD deployment.
**Current secrets:**
| Secret | Used By | Description |
|--------|---------|-------------|
| `REVENUECAT_WEBHOOK_SECRET` | revenuecat-webhook | Webhook signature validation |
| `REVENUECAT_API_KEY` | sync-subscription | RevenueCat API access |
| `REVENUECAT_ENTITLEMENT_ID` | sync-subscription | Entitlement identifier |
| `OPENROUTER_API_KEY` | classify-message | AI classification API |
| `ADMIN_PRIVATE_KEY_JWK` | admin-decrypt-* | E2EE admin decryption |
| `ADMIN_KEYS_JSON` | admin-decrypt-*, migrate-e2ee | Multiple admin keys |
| `DISCORD_WEBHOOK_URL` | send-discord-notification | Discord notifications |
**Auto-provided by Supabase (don't set):**
- `SUPABASE_URL`
- `SUPABASE_ANON_KEY`
- `SUPABASE_SERVICE_ROLE_KEY`
**When adding a new edge function that needs secrets:**
1. Add the secret to the edge function code: `Deno.env.get("SECRET_NAME")`
2. Update `.github/workflows/deploy-supabase.yml` to include the new secret in the sync step
3. Update `apps/supabase/functions/.secrets.example` with the new secret
4. **Remind the user** to add the secret to GitHub Secrets (Settings → Secrets → Actions)
5. Update this table in CLAUDE.md
**Local development:**
```bash
cp apps/supabase/functions/.secrets.example apps/supabase/functions/.secrets
# Fill in values
supabase functions serve --env-file apps/supabase/functions/.secrets
```
Read and follow [`AGENTS.md`](AGENTS.md). It is the runtime-neutral source of truth
for this repository. Scoped rules live in the nearest `AGENTS.md`; maintained
documentation is indexed by [`docs/index.md`](docs/index.md).
Executable → Regular
+35 -74
View File
@@ -1,90 +1,51 @@
# Sauci
![Node 20+](https://img.shields.io/badge/node-20%2B-339933)
![npm 10+](https://img.shields.io/badge/npm-10%2B-CB3837)
![Turborepo](https://img.shields.io/badge/monorepo-turborepo-0F1115)
Sauci is a couples connection product built as a full-stack npm/Turborepo
workspace.
Sauci is a couples intimacy app where partners swipe question packs, match on shared interests, and chat about what they unlock together.
## Applications
## What lives here
| Path | Surface |
|---|---|
| `apps/mobile` | Expo React Native customer app |
| `apps/admin` | Vite/React administration app |
| `apps/web` | Next.js marketing and redemption app |
| `apps/supabase` | database, auth, realtime, storage, and Edge Functions |
| `apps/mcp` | authenticated internal MCP service |
| `packages/shared` | shared TypeScript contracts |
```
sauci/
├── apps/
│ ├── mobile/ # Expo React Native app
│ ├── admin/ # Admin dashboard (Vite + React)
│ ├── web/ # Marketing website (Next.js)
│ └── supabase/ # Supabase project (migrations, edge functions)
├── packages/
│ └── shared/ # Shared TypeScript types
```
## Bootstrap and run
## Core product flows
- **Couple pairing** via invite codes and profile linking
- **Question packs** with two-part phrasing and smart selection bias
- **Matches + chat** when both partners answer positively
- **Notifications** for new matches and messages
- **Subscriptions** via RevenueCat with couple-level premium sharing
## Prerequisites
- Node.js 20+
- npm 10+
- Supabase CLI (`npm install -g supabase`)
- Expo CLI (`npm install -g expo-cli`)
## Quickstart
Use Node `20.10.0` from `.nvmrc`, Docker-compatible containers, and tmux.
```bash
npm install
npm run dev
npm ci
scripts/dev-local.sh reset
scripts/dev-local.sh up
```
## App commands
The launcher starts local Supabase, Edge Functions, web, admin, MCP, and Expo in
one tmux session. See [local stack documentation](docs/development/local-stack.md)
for ports, logs, native launch commands, and troubleshooting.
From each app directory:
## Verify
- **Mobile** (`apps/mobile`): `npm run dev`, `npm run ios`, `npm run android`, `npm run web`
- **Admin** (`apps/admin`): `npm run dev`, `npm run build`, `npm run typecheck`
- **Web** (`apps/web`): `npm run dev`, `npm run build`, `npm run start`
```bash
npm run verify:fast # lint, typecheck, unit/contract tests
npm run verify:full # fast checks, builds, Supabase integration
npm run verify:e2e # running-stack Playwright journeys
npm run verify # complete pre-PR gate
```
## Environment setup
Native black-box flows use `npm run test:mobile:e2e`. Pull requests run static
and full-stack gates and retain browser traces/screenshots/videos on failure.
Copy the example files and fill in values:
## Agent harness
- Mobile: `apps/mobile/.env.example`
- Admin: `apps/admin/.env.example`
- Web: `apps/web/.env.local.example`
## Supabase (local)
- Config lives in `apps/supabase/config.toml`.
- Use the Supabase CLI for local services and migrations.
## Scripts (root)
- `npm run dev` - run all apps in dev mode
- `npm run build` - build all apps
- `npm run lint` - lint all apps
- `npm run typecheck` - typecheck all apps
- `npm run clean` - clean build artifacts
## Docs
- Auth: `docs/authentication.md`
- Schema: `docs/schema.md`
- Pairing: `docs/couple-pairing.md`
- Questions: `docs/question-selection.md`
- Matches/notifications: `docs/match-notifications.md`
- Subscriptions: `docs/subscription-system.md`
- Mobile release guide: `apps/mobile/RELEASING.md`
## Tech stack
- **Mobile**: Expo (React Native)
- **Backend**: Supabase (Postgres, Auth, Edge Functions, Realtime)
- **Admin**: React + Vite
- **Web**: Next.js
- **Monorepo**: Turborepo
Start with [AGENTS.md](AGENTS.md) and [the documentation index](docs/index.md).
Task/risk agent routing lives in [docs/agents/routing.md](docs/agents/routing.md),
repository skills live under `.codex/skills`, and verified lessons follow the
[learning promotion lifecycle](docs/agents/learnings.md).
Database schema changes must use committed files in `apps/supabase/migrations`.
Remote migration tools and DDL through MCP SQL execution are forbidden.
+5 -2
View File
@@ -1,5 +1,8 @@
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
# Hosted Supabase Auth public settings only. Admin product data and mutations go
# through VITE_API_URL; never expose a service-role or admin service token here.
VITE_SUPABASE_URL=your_nonproduction_supabase_auth_url
VITE_SUPABASE_ANON_KEY=your_nonproduction_supabase_anon_key
VITE_API_URL=http://127.0.0.1:3003
VITE_OPENAI_API_KEY=your_openai_api_key
VITE_OPENROUTER_API_KEY=your_openrouter_api_key
+14
View File
@@ -0,0 +1,14 @@
# Admin application
Vite/React administration UI. Use existing layout, route, and Supabase client
patterns before introducing abstractions.
- `pack_creator` manages permitted content; `super_admin` can access privileged
user/admin/audit surfaces.
- Enforce role checks in both route visibility and the backend/RLS boundary.
- Administrative mutations must remain auditable.
- Use `.maybeSingle()` when zero rows are valid.
- Never expose credentials, tokens, or private user content unnecessarily.
- Add accessible labels and a focused test for changed behavior.
- Verify with `npm run lint -w @sauci/admin`, `npm run typecheck -w
@sauci/admin`, and `npm run build -w @sauci/admin`.
+8 -13
View File
@@ -2,23 +2,17 @@ FROM node:20-bookworm-slim AS builder
WORKDIR /app
# Enable pnpm via corepack (allow non-matching packageManager)
ENV COREPACK_ENABLE_STRICT=0
ENV PNPM_CONFIG_LINK_WORKSPACE_PACKAGES=true
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
# Copy everything first (workspaces need full structure)
# Copy the npm workspace and install from its committed lockfile.
COPY . .
# Install all dependencies
RUN pnpm install --no-frozen-lockfile
RUN npm ci
# Build shared package first
RUN pnpm exec turbo run build --filter=@sauci/shared
RUN npm run build -w @sauci/shared
# Build admin app with env vars
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY
ARG VITE_SUPABASE_ANON_KEY
ARG VITE_API_URL
ARG VITE_OPENAI_API_KEY
ARG VITE_OPENROUTER_API_KEY
ARG VITE_AI_MODEL
@@ -30,7 +24,8 @@ ARG VITE_COUNCIL_GENERATOR_MODEL
ARG VITE_COUNCIL_REVIEWER_MODEL
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
ENV VITE_API_URL=$VITE_API_URL
ENV VITE_OPENAI_API_KEY=$VITE_OPENAI_API_KEY
ENV VITE_OPENROUTER_API_KEY=$VITE_OPENROUTER_API_KEY
ENV VITE_AI_MODEL=$VITE_AI_MODEL
@@ -41,7 +36,7 @@ ENV VITE_COUNCIL_ENABLED=$VITE_COUNCIL_ENABLED
ENV VITE_COUNCIL_GENERATOR_MODEL=$VITE_COUNCIL_GENERATOR_MODEL
ENV VITE_COUNCIL_REVIEWER_MODEL=$VITE_COUNCIL_REVIEWER_MODEL
RUN pnpm exec turbo run build --filter=@sauci/admin
RUN npm run build -w @sauci/admin
FROM nginx:alpine
COPY --from=builder /app/apps/admin/dist /usr/share/nginx/html
+4 -23
View File
@@ -2,34 +2,15 @@ FROM node:20-bookworm-slim
WORKDIR /app
# Enable pnpm via corepack (allow non-matching packageManager)
ENV COREPACK_ENABLE_STRICT=0
ENV PNPM_CONFIG_LINK_WORKSPACE_PACKAGES=true
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
# Copy root package files and workspace configs
COPY package*.json ./
COPY pnpm-workspace.yaml ./
COPY turbo.json ./
# Copy all workspace package.json files
COPY apps/admin/package.json ./apps/admin/
COPY apps/mobile/package.json ./apps/mobile/
COPY apps/supabase/package.json ./apps/supabase/
COPY apps/web/package.json ./apps/web/
COPY packages/shared/package.json ./packages/shared/
# Install all dependencies at root level (workspaces)
RUN pnpm install --no-frozen-lockfile
# Copy all source code
# Install the complete npm workspace from its committed lockfile.
COPY . .
RUN npm ci
# Build shared package
RUN pnpm exec turbo run build --filter=@sauci/shared
RUN npm run build -w @sauci/shared
WORKDIR /app/apps/admin
EXPOSE 3001
CMD ["pnpm", "run", "dev"]
CMD ["npm", "run", "dev"]
-84
View File
@@ -1,84 +0,0 @@
# Admin Dashboard - Agent Guidelines
**STRICT RULES - DO NOT VIOLATE**
## Architecture
This is a React Admin dashboard using:
- **Build**: Vite
- **Routing**: react-router-dom with component-based routing in `App.tsx`
- **Styling**: Tailwind CSS
- **State**: React Context (`AuthContext`)
- **Backend**: Supabase client in `src/lib/supabase.ts`
## Directory Structure
```
src/
components/ # Reusable components
layout/ # AppLayout, Sidebar, etc.
ProtectedRoute.tsx # Route guard
contexts/ # React contexts (AuthContext)
hooks/ # Custom hooks
lib/ # Utilities (supabase)
pages/ # Page components
content/ # Categories, Packs, Questions
users/ # User management (super_admin only)
admins/ # Admin management (super_admin only)
```
## Critical Rules
### Authentication & Authorization
Two admin roles exist:
- `admin` - Content management only (categories, packs, questions)
- `super_admin` - Full access including user data, admins, audit logs
Use `<ProtectedRoute requireSuperAdmin>` for super_admin-only routes.
### Route Structure
```typescript
// Public
/login
// Protected (all admins)
/ # Dashboard
/categories # Category management
/categories/:id/packs # Packs in category
/packs/:id/questions # Questions in pack
// Protected (super_admin only)
/users # User list
/users/:id # User detail
/users/:id/matches/:id # Match chat view
/admins # Admin management
/audit-logs # Audit log viewer
```
### Supabase Queries
- **Always use `.maybeSingle()` instead of `.single()`** when the row might not exist
- All admin operations are audited - check `audit_logs` table structure
### UI Patterns
- Use Tailwind CSS for styling
- Follow existing component patterns in `src/components/`
- Use `AppLayout` wrapper for all protected pages
### Security
- Never expose user passwords or sensitive data in admin views
- Audit log all administrative actions
- Verify admin role before performing operations
## Forbidden Actions
1. **DO NOT** expose user passwords or auth tokens
2. **DO NOT** allow content admins to access user data
3. **DO NOT** bypass role checks in protected routes
4. **DO NOT** perform destructive operations without confirmation
5. **DO NOT** use `.single()` for queries that might return 0 rows
6. **DO NOT** skip audit logging for administrative actions
+2 -1
View File
@@ -5,7 +5,8 @@ services:
dockerfile: apps/admin/Dockerfile
args:
- VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
- VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
- VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
- VITE_API_URL=${VITE_API_URL}
- VITE_OPENAI_API_KEY=${VITE_OPENAI_API_KEY}
- VITE_OPENROUTER_API_KEY=${VITE_OPENROUTER_API_KEY}
- VITE_AI_MODEL=${VITE_AI_MODEL}
+1
View File
@@ -8,6 +8,7 @@ services:
environment:
- VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
- VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
- VITE_API_URL=${VITE_API_URL:-http://127.0.0.1:3003}
- VITE_OPENAI_API_KEY=${VITE_OPENAI_API_KEY}
- VITE_OPENROUTER_API_KEY=${VITE_OPENROUTER_API_KEY}
- VITE_AI_MODEL=${VITE_AI_MODEL}
+7 -5
View File
@@ -4,9 +4,11 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit",
"dev": "vite",
"build": "vite build",
"lint": "tsc --noEmit",
"test": "vitest run --passWithNoTests",
"typecheck": "tsc --noEmit",
"preview": "vite preview"
},
"dependencies": {
@@ -42,8 +44,8 @@
"tailwind-merge": "^2.2.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react": "~19.1.10",
"@types/react-dom": "~19.1.7",
"@vitejs/plugin-react": "^4.2.0",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.32",
+1 -1
View File
@@ -195,7 +195,7 @@ function App() {
<Route
path="/flagged-messages"
element={
<ProtectedRoute requiredPermission={PERMISSION_KEYS.MANAGE_AI_CONFIG}>
<ProtectedRoute requiredPermission={PERMISSION_KEYS.VIEW_CHATS}>
<FlaggedMessagesPage />
</ProtectedRoute>
}
Loaded 100 of 581 files, more files were not shown because too many files have changed in this diff. Show more