From 06c1269a51531e08fbb49d002af098201ae195de Mon Sep 17 00:00:00 2001 From: David Sulitzer Date: Tue, 17 Feb 2026 17:35:05 +0200 Subject: [PATCH 1/3] Add reusable AI workflow skills for davidsulitzer.com. Port 11 selected skills into .cursor/.codex and retarget references so planning, QA, issue, and release workflows are available in this repository too. --- .codex/skills/generate-assets/SKILL.md | 173 +++++++ .codex/skills/idea-generator/SKILL.md | 494 ++++++++++++++++++ .codex/skills/new-issue/SKILL.md | 651 ++++++++++++++++++++++++ .codex/skills/new-release/SKILL.md | 168 ++++++ .codex/skills/new-skill/SKILL.md | 514 +++++++++++++++++++ .codex/skills/qa-start/SKILL.md | 318 ++++++++++++ .codex/skills/refine-spec/SKILL.md | 356 +++++++++++++ .codex/skills/release-shipped/SKILL.md | 278 ++++++++++ .codex/skills/respect-figma/SKILL.md | 148 ++++++ .codex/skills/update-skill/SKILL.md | 588 +++++++++++++++++++++ .codex/skills/user-interview/SKILL.md | 628 +++++++++++++++++++++++ .cursor/skills/generate-assets/SKILL.md | 173 +++++++ .cursor/skills/idea-generator/SKILL.md | 494 ++++++++++++++++++ .cursor/skills/new-issue/SKILL.md | 651 ++++++++++++++++++++++++ .cursor/skills/new-release/SKILL.md | 168 ++++++ .cursor/skills/new-skill/SKILL.md | 514 +++++++++++++++++++ .cursor/skills/qa-start/SKILL.md | 318 ++++++++++++ .cursor/skills/refine-spec/SKILL.md | 356 +++++++++++++ .cursor/skills/release-shipped/SKILL.md | 278 ++++++++++ .cursor/skills/respect-figma/SKILL.md | 148 ++++++ .cursor/skills/update-skill/SKILL.md | 588 +++++++++++++++++++++ .cursor/skills/user-interview/SKILL.md | 628 +++++++++++++++++++++++ 22 files changed, 8632 insertions(+) create mode 100644 .codex/skills/generate-assets/SKILL.md create mode 100644 .codex/skills/idea-generator/SKILL.md create mode 100644 .codex/skills/new-issue/SKILL.md create mode 100644 .codex/skills/new-release/SKILL.md create mode 100644 .codex/skills/new-skill/SKILL.md create mode 100644 .codex/skills/qa-start/SKILL.md create mode 100644 .codex/skills/refine-spec/SKILL.md create mode 100644 .codex/skills/release-shipped/SKILL.md create mode 100644 .codex/skills/respect-figma/SKILL.md create mode 100644 .codex/skills/update-skill/SKILL.md create mode 100644 .codex/skills/user-interview/SKILL.md create mode 100644 .cursor/skills/generate-assets/SKILL.md create mode 100644 .cursor/skills/idea-generator/SKILL.md create mode 100644 .cursor/skills/new-issue/SKILL.md create mode 100644 .cursor/skills/new-release/SKILL.md create mode 100644 .cursor/skills/new-skill/SKILL.md create mode 100644 .cursor/skills/qa-start/SKILL.md create mode 100644 .cursor/skills/refine-spec/SKILL.md create mode 100644 .cursor/skills/release-shipped/SKILL.md create mode 100644 .cursor/skills/respect-figma/SKILL.md create mode 100644 .cursor/skills/update-skill/SKILL.md create mode 100644 .cursor/skills/user-interview/SKILL.md diff --git a/.codex/skills/generate-assets/SKILL.md b/.codex/skills/generate-assets/SKILL.md new file mode 100644 index 0000000..e282cbd --- /dev/null +++ b/.codex/skills/generate-assets/SKILL.md @@ -0,0 +1,173 @@ +--- +name: "generate-assets" +description: "Use when the user asks to generate or edit images via the OpenAI Image API (for example: generate image, edit/inpaint/mask, background removal or replacement, transparent background, product shots, concept art, covers, or batch variants); run the bundled CLI (`scripts/image_gen.py`) and require `OPENAI_API_KEY` for live calls." +--- + +# Image Generation Skill + +Generates or edits images for the current project (e.g., website assets, game assets, UI mockups, product mockups, wireframes, logo design, photorealistic images, infographics). Defaults to `gpt-image-1.5` and the OpenAI Image API, and prefers the bundled CLI for deterministic, reproducible runs. + +## When to use +- Generate a new image (concept art, product shot, cover, website hero) +- Edit an existing image (inpainting, masked edits, lighting or weather transformations, background replacement, object removal, compositing, transparent background) +- Batch runs (many prompts, or many variants across prompts) + +## Decision tree (generate vs edit vs batch) +- If the user explicitly asks to modify an image (e.g., "edit/retouch/inpaint/mask/translate/localize/change only X") -> **edit** (providing an input image alone does not imply edit; it may be a style reference) +- Else if the user needs many different prompts/assets -> **generate-batch** +- Else -> **generate** + +## Workflow +1. Decide intent: generate vs edit vs batch (see decision tree above). +2. Collect inputs up front: prompt(s), exact text (verbatim), constraints/avoid list, and any input image(s)/mask(s). For multi-image edits, label each input by index and role; for edits, list invariants explicitly. +3. If batch: write a temporary JSONL under tmp/ (one job per line), run once, then delete the JSONL. +4. Augment prompt into a short labeled spec (structure + constraints) without inventing new creative requirements. +5. Run the bundled CLI (`scripts/image_gen.py`) with sensible defaults (see references/cli.md). +6. For complex edits/generations, inspect outputs (open/view images) and validate: subject, style, composition, text accuracy, and invariants/avoid items. +7. Iterate: make a single targeted change (prompt or mask), re-run, re-check. +8. Save/return final outputs and note the final prompt + flags used. + +## Temp and output conventions +- Use `tmp/imagegen/` for intermediate files (for example JSONL batches); delete when done. +- Write final artifacts under `output/imagegen/` when working in this repo. +- Use `--out` or `--out-dir` to control output paths; keep filenames stable and descriptive. + +## Dependencies (install if missing) +Prefer `uv` for dependency management. + +Python packages: +``` +uv pip install openai pillow +``` +If `uv` is unavailable: +``` +python3 -m pip install openai pillow +``` + +## Environment +- `OPENAI_API_KEY` must be set for live API calls. + +If the key is missing, give the user these steps: +1. Create an API key in the OpenAI platform UI: https://platform.openai.com/api-keys +2. Set `OPENAI_API_KEY` as an environment variable in their system. +3. Offer to guide them through setting the environment variable for their OS/shell if needed. +- Never ask the user to paste the full key in chat. Ask them to set it locally and confirm when ready. + +If installation isn't possible in this environment, tell the user which dependency is missing and how to install it locally. + +## Defaults & rules +- Use `gpt-image-1.5` unless the user explicitly asks for `gpt-image-1-mini` or explicitly prefers a cheaper/faster model. +- Assume the user wants a new image unless they explicitly ask for an edit. +- Require `OPENAI_API_KEY` before any live API call. +- Use the OpenAI Python SDK (`openai` package) for all API calls; do not use raw HTTP. +- If the user requests edits, use `client.images.edit(...)` and include input images (and mask if provided). +- Prefer the bundled CLI (`scripts/image_gen.py`) over writing new one-off scripts. +- Never modify `scripts/image_gen.py`. If something is missing, ask the user before doing anything else. +- If the result isn’t clearly relevant or doesn’t satisfy constraints, iterate with small targeted prompt changes; only ask a question if a missing detail blocks success. + +## Prompt augmentation +Reformat user prompts into a structured, production-oriented spec. Only make implicit details explicit; do not invent new requirements. + +## Use-case taxonomy (exact slugs) +Classify each request into one of these buckets and keep the slug consistent across prompts and references. + +Generate: +- photorealistic-natural — candid/editorial lifestyle scenes with real texture and natural lighting. +- product-mockup — product/packaging shots, catalog imagery, merch concepts. +- ui-mockup — app/web interface mockups that look shippable. +- infographic-diagram — diagrams/infographics with structured layout and text. +- logo-brand — logo/mark exploration, vector-friendly. +- illustration-story — comics, children’s book art, narrative scenes. +- stylized-concept — style-driven concept art, 3D/stylized renders. +- historical-scene — period-accurate/world-knowledge scenes. + +Edit: +- text-localization — translate/replace in-image text, preserve layout. +- identity-preserve — try-on, person-in-scene; lock face/body/pose. +- precise-object-edit — remove/replace a specific element (incl. interior swaps). +- lighting-weather — time-of-day/season/atmosphere changes only. +- background-extraction — transparent background / clean cutout. +- style-transfer — apply reference style while changing subject/scene. +- compositing — multi-image insert/merge with matched lighting/perspective. +- sketch-to-render — drawing/line art to photoreal render. + +Quick clarification (augmentation vs invention): +- If the user says “a hero image for a landing page”, you may add *layout/composition constraints* that are implied by that use (e.g., “generous negative space on the right for headline text”). +- Do not introduce new creative elements the user didn’t ask for (e.g., adding a mascot, changing the subject, inventing brand names/logos). + +Template (include only relevant lines): +``` +Use case: +Asset type: +Primary request: +Scene/background: +Subject:
+Style/medium: +Composition/framing: +Lighting/mood: +Color palette: +Materials/textures: +Quality: +Input fidelity (edits): +Text (verbatim): "" +Constraints: +Avoid: +``` + +Augmentation rules: +- Keep it short; add only details the user already implied or provided elsewhere. +- Always classify the request into a taxonomy slug above and tailor constraints/composition/quality to that bucket. Use the slug to find the matching example in `references/sample-prompts.md`. +- If the user gives a broad request (e.g., "Generate images for this website"), use judgment to propose tasteful, context-appropriate assets and map each to a taxonomy slug. +- For edits, explicitly list invariants ("change only X; keep Y unchanged"). +- If any critical detail is missing and blocks success, ask a question; otherwise proceed. + +## Examples + +### Generation example (hero image) +``` +Use case: stylized-concept +Asset type: landing page hero +Primary request: a minimal hero image of a ceramic coffee mug +Style/medium: clean product photography +Composition/framing: centered product, generous negative space on the right +Lighting/mood: soft studio lighting +Constraints: no logos, no text, no watermark +``` + +### Edit example (invariants) +``` +Use case: precise-object-edit +Asset type: product photo background replacement +Primary request: replace the background with a warm sunset gradient +Constraints: change only the background; keep the product and its edges unchanged; no text; no watermark +``` + +## Prompting best practices (short list) +- Structure prompt as scene -> subject -> details -> constraints. +- Include intended use (ad, UI mock, infographic) to set the mode and polish level. +- Use camera/composition language for photorealism. +- Quote exact text and specify typography + placement. +- For tricky words, spell them letter-by-letter and require verbatim rendering. +- For multi-image inputs, reference images by index and describe how to combine them. +- For edits, repeat invariants every iteration to reduce drift. +- Iterate with single-change follow-ups. +- For latency-sensitive runs, start with quality=low; use quality=high for text-heavy or detail-critical outputs. +- For strict edits (identity/layout lock), consider input_fidelity=high. +- If results feel “tacky”, add a brief “Avoid:” line (stock-photo vibe; cheesy lens flare; oversaturated neon; harsh bloom; oversharpening; clutter) and specify restraint (“editorial”, “premium”, “subtle”). + +More principles: `references/prompting.md`. Copy/paste specs: `references/sample-prompts.md`. + +## Guidance by asset type +Asset-type templates (website assets, game assets, wireframes, logo) are consolidated in `references/sample-prompts.md`. + +## CLI + environment notes +- CLI commands + examples: `references/cli.md` +- API parameter quick reference: `references/image-api.md` +- If network approvals / sandbox settings are getting in the way: `references/codex-network.md` + +## Reference map +- **`references/cli.md`**: how to *run* image generation/edits/batches via `scripts/image_gen.py` (commands, flags, recipes). +- **`references/image-api.md`**: what knobs exist at the API level (parameters, sizes, quality, background, edit-only fields). +- **`references/prompting.md`**: prompting principles (structure, constraints/invariants, iteration patterns). +- **`references/sample-prompts.md`**: copy/paste prompt recipes (generate + edit workflows; examples only). +- **`references/codex-network.md`**: environment/sandbox/network-approval troubleshooting. diff --git a/.codex/skills/idea-generator/SKILL.md b/.codex/skills/idea-generator/SKILL.md new file mode 100644 index 0000000..6828f07 --- /dev/null +++ b/.codex/skills/idea-generator/SKILL.md @@ -0,0 +1,494 @@ +--- +name: idea-generator +description: Brainstorm bold, data-informed product feature ideas with competitor research, visual demos, and metric alignment. Use when exploring new feature concepts or looking for creative ways to move key metrics. Don't use for creating issues (/new-issue) or refining existing specs (/refine-spec). +disable-model-invocation: true +--- +# idea-generator + +## When to Use + +- You want creative feature ideas to move a specific metric (activation, retention, conversion, etc.) +- You're exploring "what could we build?" before committing to a spec +- You want competitor-informed brainstorming with visual mockups + +## When NOT to Use + +- You've already decided what to build and need an issue → use `/new-issue` +- You have an existing spec that needs refinement → use `/refine-spec` +- You need user research to validate an idea → use `/user-interview` +- You're doing QA or deployment work → wrong skill entirely + +--- + +You are a **wildly creative** product brainstorm partner for davidsulitzer.com — a places discovery app that helps users find amazing spots (coffee shops, bars, restaurants, etc.) through community curation. + +## Your Mission + +Generate **bold, original, unexpected** feature ideas that move our metrics. Don't play it safe — the best ideas often sound crazy at first. Be inspired by what competitors do well, but don't copy — **innovate**. + +Think like a mix of: a product visionary, a behavioral psychologist, and a user who's slightly obsessed with finding the perfect coffee shop. + +--- + +## Q1 2026 Goals + +### 🎯 North Star +**Define your current north-star metric and date.** + +### 📊 Top-Down Metrics (Funnel Conversion) +| Stage | Goal | +|-------|------| +| **💰 Pricing Screen → Trial Start** | 20-25% | +| **✅ Trial Start → Trial Converted** | 35-40% | + +### 💡 Leading Indicators + +| Metric | Goal | Definition | Why It Matters | +|--------|------|------------|----------------| +| **🔥 Activation Rate** | 60% | % of new users who perform at least ONE of: save, like, search result click, or place profile view in their first session | First session is make-or-break. If they don't engage on day 1, they're ghosts. | +| **🔄 Early Retention** | 40% | % of trial users with 3+ sessions in first 3 days | Habit formation window. Users who return within 3 days are building davidsulitzer.com into their routine. | +| **💾 Investment Rate** | 10% | % of trial users who save at least 1 place in their first 7 days | Saving = "I found value I want to keep." Creates switching costs and signals product-market fit. | + +--- + +## Brainstorm Process + +### Step 1: Pick Your Target +Ask me which metric or screen I want to focus on, or suggest one based on the biggest opportunity gap. + +### Step 2: Understand Current State +Before ideating, briefly explore the codebase to understand: +- What's the current UX for this metric area? +- What screens/flows are involved? +- What analytics events exist? + +Use semantic search and grep to find relevant code in both `davidsulitzer.com` and `davidsulitzer.com`. + +### Step 3: Research Competitors +Before generating ideas, use the **WebSearch tool** to research what competitors are doing well in this space. Don't rely on outdated knowledge — find recent features and updates! + +**Search queries to run:** +- `"[competitor] new features [current year]"` — e.g., "Google Maps new features 2026" +- `"[competitor] [metric area] improvements"` — e.g., "Yelp user engagement improvements" +- `"best [category] app features [current year]"` — e.g., "best restaurant discovery app features 2026" + +**Competitors to research:** +- **Maps & Discovery:** Google Maps, Yelp, Foursquare, TripAdvisor +- **Food & Booking:** Uber Eats, DoorDash, OpenTable +- **Travel:** Airbnb, Booking.com +- **Engagement patterns:** Instagram, TikTok, BeReal, Duolingo + +Note what's working for them and why — then think about how davidsulitzer.com can do it **better or differently**. + +### Step 4: Review User Interview Notes + +Before ideating, check the **User Interview Notes** Slack channel for real user feedback, pain points, and feature requests. This is gold — actual users telling us what they want! + +**Slack Channel:** `#user-interview-notes` (ID: `C09PTLMHKLP`) + +Use the Slack MCP to search for relevant insights: +``` +Search the Slack channel C09PTLMHKLP for messages related to [TOPIC/METRIC AREA] +``` + +**What to look for:** +- Recurring pain points or frustrations +- Feature requests that align with the target metric +- Emotional language (excitement, frustration, confusion) +- Workarounds users have invented (signals unmet needs!) +- Quotes that could inform your hypothesis + +**Pro tip:** Real user words make the best hypothesis justifications. Quote them in your ideas! + +### Step 5: Mine Crisp Support Conversations + +**Real users complaining = real product insights.** Before ideating, pull recent support conversations from Crisp to find recurring pain points, feature requests, and confusion signals. + +> #### Cloud Agent? Use Environment Secrets +> +> If running as a **cloud agent**, use `CRISP_IDENTIFIER`, `CRISP_KEY`, and `CRISP_WEBSITE_ID` from environment secrets instead of sourcing `.env`. + +#### 5a. Load Credentials + +```bash +source .env +``` + +#### 5b. Search Conversations by Topic + +Search for conversations related to the metric area or feature you're brainstorming on: + +```bash +# Search conversations matching a keyword (returns up to 20 per page) +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?search_query=KEYWORD&search_type=text" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +**Search tips:** +- Search for the feature area: `search`, `save`, `map`, `subscription`, `trial`, `cancel`, `bug`, `crash`, `payment` +- Search for emotional signals: `frustrated`, `broken`, `love`, `wish`, `want`, `missing`, `need` +- Search for competitor mentions: `google maps`, `yelp`, `tripadvisor` +- Page through results by changing the page number in the URL (`.../conversations/2`, `.../conversations/3`, etc.) + +#### 5c. Read Messages from Interesting Conversations + +When you find a conversation that looks relevant, pull the full message thread: + +```bash +# Get all messages in a specific conversation +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversation/SESSION_ID/messages" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +Replace `SESSION_ID` with the `session_id` from the conversation list (e.g., `session_fa5663c8-6f5c-473c-b4e9-44c3db8ef2e0`). + +#### 5d. Browse Recent Conversations (No Search) + +Sometimes the best insights come from just scanning what people are writing about lately: + +```bash +# List most recent conversations (page 1, most recent first) +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +Each conversation includes a `topic`, `last_message`, `meta.nickname`, `meta.email`, and `meta.device.geolocation` — useful for building persona context. + +#### 5e. Filter by Date Range + +Focus on recent feedback (e.g., last 30 days): + +```bash +# Conversations updated in the last 30 days +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_date_start=$(date -u -v-30d '+%Y-%m-%dT00:00:00.000Z')&filter_date_end=$(date -u '+%Y-%m-%dT23:59:59.999Z')" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +#### 5f. Filter by Conversation State + +Focus on unresolved issues (things users are STILL frustrated about): + +```bash +# Only unresolved conversations +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_not_resolved=1" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +#### 5g. What to Extract + +From the conversations, look for: + +| Signal | What It Tells You | How to Use It | +|--------|-------------------|---------------| +| **Recurring complaints** | What's broken or frustrating right now | Direct problem to solve | +| **Feature requests** | What users wish existed | Idea fuel — validate with data | +| **Confusion patterns** | Where the UX fails to communicate | UX improvement opportunities | +| **Competitor mentions** | What users compare davidsulitzer.com to | Competitive positioning insights | +| **Emotional language** | How strongly users feel | Prioritization signal (strong = urgent) | +| **Workarounds** | Hacks users invent to get what they want | Unmet needs hiding in plain sight | +| **Churn signals** | "I'm canceling because..." | Retention idea triggers | +| **Praise / delight** | What users genuinely love | Double down on what works | + +**Pro tip:** Quote real user words from Crisp in your ideas! Nothing sells a hypothesis like an actual frustrated human saying exactly the thing your idea would fix. + +### Step 6: Find Related Backlog +Search GitHub for related existing issues: +```bash +gh issue list -R 8Gaston8/davidsulitzer.com --search "KEYWORDS" --limit 10 +``` + +This helps connect ideas to existing work and avoid duplicating efforts. + +### Step 7: Generate Ideas (BE CREATIVE!) + +Generate **5-10 ideas** that are: +- **Original** — not obvious, not what everyone else is doing +- **Bold** — willing to take risks, challenge assumptions +- **Grounded** — tied to user psychology and mobile UX best practices +- **Varied** — across different categories (quick wins to moonshots) + +--- + +## Output Format + +**IMPORTANT:** Present ALL ideas using this detailed format. Each idea should be a complete, self-contained brief. + +--- + +### 💡 Idea #[N]: [Creative, Catchy Name] + +**Category:** ⚡ Quick Win / 🚀 Feature / 🧪 Experiment / 🌙 Moonshot + +> *[One sentence elevator pitch — make it punchy and memorable!]* + +#### The Concept +[2-3 sentences describing what this is and how it works. Be specific enough that someone could understand and visualize it.] + +--- + +#### 🎯 Goals Alignment + +**Primary Target:** +| Goal | Expected Impact | Confidence | +|------|-----------------|------------| +| [🔥 Activation / 🔄 Retention / 💾 Investment / 💰 Pricing→Trial / ✅ Trial→Converted] | [+X-Y%] | [High/Medium/Low] | + +**Also Impacts:** [List secondary goals with emojis] + +**Hypothesis:** +> [2-3 sentences explaining WHY this will work. Be specific about the user psychology, behavioral principle, or proven pattern you're drawing from. This is the most important part — convince me!] + +**Rationale:** +[1-2 sentences on the underlying logic or evidence. Reference specific data, competitor success, or behavioral science if applicable.] + +--- + +#### 🔍 Competitor Inspiration + +| Competitor | What They Do | davidsulitzer.com's Twist | +|------------|--------------|--------------| +| [Company 1] | [Their implementation] | [How we do it better/differently] | +| [Company 2] | [Their implementation] | [How we do it better/differently] | + +**Key Insight:** [What's the learning here? Why does this pattern work?] + +--- + +#### 🔗 Related Backlog + +| Repo | Issue | How It Relates | +|------|-------|----------------| +| 📱 davidsulitzer.com | [#XXXX - Title](link) | [Connection] | +| ⚙️ davidsulitzer.com | [#XXXX - Title](link) | [Connection] | + +*If no related issues exist, note: "No existing issues — this is net new!"* + +--- + +#### 📊 Evaluation + +| Dimension | Rating | Notes | +|-----------|--------|-------| +| **Impact** | 🟢 High / 🟡 Medium / 🔴 Low | [Brief justification] | +| **Effort** | S / M / L / XL | [Key complexity drivers] | +| **Confidence** | 🟢 High / 🟡 Medium / 🔴 Low | [What gives us confidence or uncertainty?] | +| **Dependencies** | [List] | Backend? Design? Data? External? | + +--- + +#### 💭 Open Questions +- [Question 1 that needs answering before implementation] +- [Question 2 about user behavior or technical feasibility] + +--- + +## Creativity Prompts + +When generating ideas, challenge yourself with these questions: + +**Flip the script:** +- What if we did the opposite of what's expected? +- What would a gaming app do here? +- What would feel magical to users? + +**Steal from other domains:** +- How does Duolingo make learning addictive? +- How does Tinder make swiping fun? +- How does Spotify personalize discovery? +- How does BeReal create urgency? + +**Remove friction vs add delight:** +- What's the laziest path to value? +- What would make users smile? +- What would they screenshot and share? + +**Think about emotions:** +- What makes users feel smart/cool/in-the-know? +- What creates FOMO? +- What builds trust? +- What creates "unfinished business" that brings them back? + +--- + +--- + +## Visual Demo (Required!) + +**After generating ideas, create a visual demo page** that renders each idea inside realistic iPhone frames. This is NOT optional — seeing ideas visually makes them 10x more useful. + +### Setup + +Create a temporary React demo in a `demos/` folder: + +```bash +mkdir -p demos/[feature-name] +cd demos/[feature-name] +npm init -y +npm install react react-dom vite @vitejs/plugin-react +``` + +### Phone Frame Structure + +Every design must be rendered inside a phone mockup: + +```jsx +
+
+
+ {/* Or other app context */} + + +
+
+``` + +### Template Contexts + +Choose the right context based on what screen your idea lives on: + +| Context | When to Use | Key Elements | +|---------|-------------|--------------| +| `PlaceProfileContext` | Place details, reviews, actions | Gallery, title, rating, People Are Saying, Good To Know | +| `SearchContext` | Search results, filters | Search header, follow-up chips, map with pins | +| `HomeContext` | Discovery, categories | Search bar, category pills, map background | +| `ListContext` | Place lists, collections | List header, place cards, filters | + +### Phone Frame CSS + +Use **280×560px** for demos (fits more ideas on screen) or **375×812px** for pixel-perfect iPhone fidelity: + +```css +.phone-frame { + width: 280px; /* Or 375px for full size */ + height: 560px; /* Or 812px for full size */ + background: #1a1a1a; + border-radius: 32px; + padding: 10px; + position: relative; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); +} + +.phone-notch { + width: 80px; + height: 24px; + background: #1a1a1a; + border-radius: 0 0 14px 14px; + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + z-index: 10; +} + +.phone-screen { + width: 100%; + height: 100%; + background: #f8f8f8; + border-radius: 24px; + overflow: hidden; + overflow-y: auto; /* Important for scrollable content! */ +} + +/* Scale content to fit smaller frame */ +.phone-screen .template-context { + transform: scale(0.72); + transform-origin: top left; + width: 139%; + height: 139%; +} +``` + +### Color Contrast (Important!) + +| Background | Text Color | Example | +|------------|------------|---------| +| Light (#fff, #f8f8f8) | Dark (#333 or darker) | ✅ Readable | +| Dark (#1a1a1a, #333) | White or light | ✅ Readable | +| Gradients | Test at ALL points | ⚠️ Check edges | +| Light gray | Light text | ❌ NEVER | + +### Page Layout + +Create a unified view with: + +1. **Sticky header** showing North Star + metrics goals +2. **Filter bar** with toggles: + - Category: All | ⚡ Quick Wins | 🚀 Features | 🌙 Moonshots + - Goal: All | 🔥 Activation | 🔄 Retention | 💾 Investment + - Data panels: 🎯 Goals | 🔍 Competitors | 🔗 Backlog (toggle visibility) +3. **Idea cards** showing: + - Phone mockup on the left + - Idea info + data panels on the right (always visible, no expand/collapse!) + +### Data Panels (Compact, Always Visible) + +```jsx +{/* Goals Panel */} +
+
🎯 Goal Alignment
+
{emoji} {goalName}
+
{expectedImpact}
+

{hypothesis}

+
+ +{/* Competitors Panel */} +
+
🔍 Competitors ({count})
+
+ {competitors.map(c => {c.name}: {c.feature})} +
+
+ +{/* Backlog Panel */} +
+
🔗 Backlog ({count})
+
+ {issues.map(i => 📱 #{i.number})} +
+
+``` + +### Run the Demo + +```bash +npm run dev +# Opens at http://localhost:3000 +``` + +**Open the browser automatically** so I can see the ideas visualized! + +### Key Principles + +| Do ✅ | Don't ❌ | +|-------|---------| +| Render inside phone frames | Show standalone components | +| Use real app context (Place Profile, Search, etc.) | Use fake/simplified mockups | +| Show all data panels immediately | Hide behind expand buttons | +| Make it interactive (filters, toggles) | Static walls of text | +| Use realistic placeholder data | Lorem ipsum | + +--- + +## After Brainstorming + +Once we finish brainstorming, I can help with next steps **if you ask**: +- Deep dive on a specific idea +- Draft a mini PRD with success metrics +- Identify key code areas to modify + +**Note:** I will NOT automatically create GitHub issues without your explicit request. Brainstorming is just brainstorming! + +**Cleanup:** The demo folder is temporary — delete it when done unless you want to keep it. + +--- + +## Let's Brainstorm! 🧠✨ + +Which metric or screen do you want to focus on today? Or should I analyze the codebase first to identify where the biggest opportunities might be? + +Remember: **The best ideas often sound a little crazy at first.** Don't hold back! diff --git a/.codex/skills/new-issue/SKILL.md b/.codex/skills/new-issue/SKILL.md new file mode 100644 index 0000000..49ebe38 --- /dev/null +++ b/.codex/skills/new-issue/SKILL.md @@ -0,0 +1,651 @@ +--- +name: new-issue +description: Create well-researched GitHub issues with full project integration (issue type, davidsulitzer.com project, priority, cross-repo blocking). Use when the user wants to create a new issue from a bug report, feature request, or task. Don't use for refining existing issues (/refine-spec) or for creating issues as part of QA (/qa-start handles that internally). +disable-model-invocation: true +--- +# new-issue + +Create GitHub issues based on the mentioned requirements. **Always** perform pre-research (Step 0) before writing anything. + +--- + +## When to Use + +- The user describes a bug, feature request, or task that needs a GitHub issue +- A bug review (`/review-new-bugs`) or crash review (`/review-new-crashes`) identified something worth tracking +- The user explicitly says "create an issue" or "file a ticket" + +## When NOT to Use + +- An issue already exists and needs improvement → use `/refine-spec` +- You're in the middle of QA and found a bug → `/qa-start` handles issue creation internally +- The user is brainstorming ideas → use `/idea-generator` first, create issues only when asked +- You're unsure if an issue is needed → ask the user before creating anything + +--- + +## Step 0: Pre-Research (BEFORE Creating Any Issue) + +Before writing anything, you MUST investigate the codebase and existing issues. This step ensures the issue lands in the correct repo and arrives loaded with useful context for the implementer. + +### 0a: Search for Relevant Code + +Search **all three** repos for keywords related to the problem or feature — function names, screen names, model names, API endpoints, error messages: + +```bash +# Search the current repo first +rg -l "" . + +# If in the davidsulitzer.com with submodules checked out, also search: +rg -l "" davidsulitzer.com/ 2>/dev/null + +# If in a standalone repo checkout, use gh to search other repos: +gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 +``` + +> **Note:** The `rg` commands with `2>/dev/null` gracefully handle missing sibling directories in standalone checkouts. Use `gh search code` as a fallback when submodules aren't available. + +Take note of: +- **Which files are involved** — full paths and key line numbers +- **Which repo(s) contain the relevant code** — this determines where the issue goes (Step 0b) +- **Recent changes** — check git log for the relevant files: + ```bash + git log --oneline -10 -- + ``` + +### 0b: Determine the Correct Repo + +Based on your code search results from 0a: + +| Search Results | Repo Decision | +|---------------|---------------| +| Relevant code **only** in `davidsulitzer.com/` | Create issue in `8Gaston8/davidsulitzer.com` | +| Relevant code in **multiple** repos | Create one issue per repo (see Step 2) | +| **Can't find relevant code** | **ASK the user** — do NOT guess | + +**Heuristic helpers** (use alongside code search, not instead of it): + +| Signal | Likely Repo | +|--------|-------------| +| UI, screens, animations, SwiftUI views, layout | iOS | +| API endpoints, database, migrations, queries, cron jobs | Server | +| Map tiles, vector tiles, pin scoring, pin priority, category pins | Tileserver | +| Location filtering, atly score, tile rendering, map vectors | Tileserver | +| "Data is wrong", "results are incorrect" | Usually Server or Tileserver — verify with code search | +| "Crashes", "freezes", "layout broken", "animation janky" | Usually iOS | +| "Pins wrong", "map shows wrong places", "scoring issue" | Usually Tileserver — verify with code search | +| Model names, feature names | Could be any repo — always search first | + +> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. + +### 0c: Check for Duplicate / Related Issues + +Before creating a new issue, search for existing ones: + +```bash +# Search open issues in all repos +gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --limit 10 + +# Also check recently closed issues (could be a regression of a previous fix) +gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --state closed --limit 5 +``` + +- **Open duplicate found** → Tell the user and link to it instead of creating a new one +- **Closed related issue found** → Reference it in the new issue for context (possible regression?) + +### 0d: Bug-Specific Investigation + +When creating a **bug** issue, do this additional research: + +1. **Find the error source** — If the user mentions an error message, search for where it's thrown: + ```bash + rg "" davidsulitzer.com/ davidsulitzer.com/ davidsulitzer.com/ + ``` + Include the file path, line number, and surrounding context in the issue. + +2. **Check recent commits for regressions** — Look for changes that could have introduced the bug: + ```bash + # Recent commits touching relevant files + git log --oneline -10 -- + + # Recent PRs merged to the repo + gh pr list --repo 8Gaston8/davidsulitzer.com --state merged --limit 10 + ``` + If a relevant file was changed recently, note the PR in the issue as a potential regression source. + +3. **Check test coverage** — Look for existing tests in the affected area: + ```bash + # iOS tests (use davidsulitzer.com paths if available, otherwise search current repo) + rg -l "" davidsulitzer.com/AtlyTests/ davidsulitzer.com/AtlyUITests/ 2>/dev/null || \ + rg -l "" AtlyTests/ AtlyUITests/ 2>/dev/null + + # Server tests (few exist — check services) + rg -l "" davidsulitzer.com/services/ 2>/dev/null || \ + rg -l "" services/ api/test/ 2>/dev/null + + # Tileserver tests + rg -l "" davidsulitzer.com/app/__tests__/ davidsulitzer.com/app/businessLogic/__tests__/ 2>/dev/null || \ + rg -l "" app/__tests__/ app/businessLogic/__tests__/ 2>/dev/null + ``` + Note whether tests exist and whether they cover the reported scenario. + +4. **Identify dependencies** — What other systems does this code touch? + - Feature flags that gate the behavior + - API endpoints involved (grep for route definitions) + - Third-party services (Mixpanel, RevenueCat, Firebase, etc.) + - Caching layers (tileserver, CDN, local cache) + +5. **Gather reproduction context:** + - Which API endpoints are involved + - What user state or data triggers the bug + - Which app version or server deployment may have introduced it (if determinable from git log) + +### 0e: Include Research Findings in Issue + +All findings from Step 0 MUST be woven into the issue body. Add a **Pre-Research Findings** section: + +```markdown +## Pre-Research Findings + +**Relevant code:** +- `:` — [brief description of what this code does] +- `:` — [brief description] + +**Recent changes in this area:** +- PR # () — [what it changed] + +**Related issues:** +- # — [title] (open/closed) + +**Test coverage:** [Existing tests cover X / No existing test coverage for this path] + +**Dependencies:** [Feature flags, APIs, services involved] +``` + +> ⚠️ **Do NOT skip this step.** Issues created without pre-research waste the implementer's time re-discovering context you already had access to. + +--- + +## Step 1: Determine the Right Repo(s) + +By this point, Step 0 should have told you which repo(s) contain the relevant code. Confirm your decision: + +| Scope | Action | +|-------|--------| +| **iOS only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Server only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Tileserver only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Multiple repos** | Create one issue per repo (see Step 2) | +| **Uncertain** | Go back to Step 0a and search more, or **ask the user** | + +> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. + +--- + +## Step 2: For Cross-Repo Issues + +When an issue requires work in multiple repos (any combination of iOS, Server, Tileserver): + +1. **Create backend/data issues first** — start with `8Gaston8/davidsulitzer.com` and/or `8Gaston8/davidsulitzer.com` + - Focus on backend requirements: API changes, data model, endpoints, tile logic + - Reference the downstream issue(s) that will be created + - Set project status to **Todo** + +2. **Create the iOS issue** in `8Gaston8/davidsulitzer.com` (if applicable) + - Focus on client requirements: UI, models, integration + - Reference the backend issue(s): "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" or "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" + - Include a section documenting the expected API contract from server/tileserver + +3. **Set up blocking relationships** — THIS IS MANDATORY FOR CROSS-REPO ISSUES: + +> The general rule: **iOS is blocked by Server/Tileserver. Server may be blocked by Tileserver.** Create `addBlockedBy` for each dependency. + + **Step 3a: Get the issue node IDs:** + ```bash + # Get issue node ID (replace and for each issue) + gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' + ``` + + **Step 3b: Create the blocked-by relationship (repeat for each dependency):** + ```bash + # Downstream issue is BLOCKED BY upstream issue + # e.g., iOS blocked by Server, iOS blocked by Tileserver, Server blocked by Tileserver + gh api graphql -f query='mutation { addBlockedBy(input: { issueId: "", blockingIssueId: "" }) { clientMutationId } }' + ``` + + **Step 3c: VERIFY the relationship was created (repeat for each downstream issue):** + ```bash + gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { blockedBy(first: 5) { nodes { number title repository { name } } } } } }' + ``` + + > ⚠️ **CRITICAL:** Do NOT use `addSubIssue` — that creates a parent/child hierarchy, NOT a blocking dependency. ONLY use `addBlockedBy`. + > + > ⚠️ **CRITICAL:** Always run the verification query. The mutation can return success even if no relationship was created. You MUST confirm `blockedBy.nodes` contains the server issue. + +--- + +## Step 3: Issue Content Requirements + +For each issue, include: + +### Required Sections +- **Pre-Research Findings** — Output from Step 0 (relevant code, recent changes, related issues, test coverage, dependencies) +- **Goal** — Clear one-liner at the top +- **Context/Background** — Why this matters +- **Requirements** — What needs to be built (platform-specific) +- **Figma Design** — ⚠️ If a Figma URL exists, include it as-is. **Do NOT translate the Figma into text** — the design file IS the spec. See the Figma Design template below. +- **Out of Scope** — Explicitly state what's NOT part of this issue (prevents scope creep, helps AI agents stay focused) +- **Acceptance Criteria** — Checkboxes for definition of done +- **Testing Requirements** — Unit tests, regression tests, manual QA scope (see Step 6) +- **Related Issues/PRs** — Cross-references + +### Conditional Sections (include when applicable) + +| Section | When to Include | +|---------|-----------------| +| **Figma Design** | ⚠️ **REQUIRED** when a Figma URL or design exists — see template below | +| **Design Brief** | ⚠️ **REQUIRED** when issue has `design` label — see template below | +| **Technical Approach** | Features/refactors where you have a sense of the architecture | +| **Investigation Checklist** | Bugs where root cause is unknown | +| **Constraints** | When there are performance, compatibility, or deadline requirements | +| **Feature Flag** | New features or risky changes that should be gated | +| **Reference Implementation** | When similar patterns/PRs exist to follow | +| **Risk/Rollback** | Medium+ risk changes that could cause issues | +| **Analytics Requirements** | When new Mixpanel events are needed | +| **Documentation Updates** | When README, API docs, or agent rules need updating (see Step 7) | + +### Section Templates + +
+Out of Scope (always include) + +```markdown +## Out of Scope +- [What you're explicitly NOT doing] +- [Related work that's a separate issue] +- [Platform exclusions, e.g., "Android — separate issue"] +``` +
+ +
+⚠️ Figma Design (REQUIRED when a Figma design exists) + +When the user provides a Figma URL or the feature has an existing Figma design, you MUST include a Figma Design section. The critical rule here is: + +> **DO NOT translate the Figma design into text.** The Figma file IS the spec. Just link to it. + +Agents and humans can read Figma directly — there's no need to describe layouts, colors, spacing, or component structure in prose. Doing so creates a stale, lossy copy of the design that will drift from the source of truth. + +```markdown +## Figma Design + +> **The Figma design is the source of truth for all visual and interaction details.** +> The implementer MUST use the `/respect-figma` skill to read the design directly from Figma. +> Do NOT rely on text descriptions of the design — open the Figma link below. + +🎨 **Figma:** [paste full Figma URL here] + +**Scope of the design:** +- [Which screens/flows are covered in this Figma link] +- [Any specific frames or variants to focus on] + +**Notes (only if needed):** +- [Anything that's NOT in the Figma but is relevant — e.g., "ignore the old header in the design, we're keeping the current one"] +- [Behavior/logic that isn't captured visually — e.g., "tapping X triggers a confirmation dialog"] +``` + +**What to include in the issue body:** +- The Figma link(s) — full URL with the correct node selected +- Which screens/frames are in scope +- Any behavioral notes that Figma can't express (conditional logic, animations, error states) + +**What NOT to include:** +- ❌ Text descriptions of the layout ("there's a card with rounded corners and a blue header...") +- ❌ Extracted color values, font sizes, or spacing numbers +- ❌ Re-drawn diagrams or ASCII mockups of what Figma already shows +- ❌ Screenshots of the Figma — link to the actual file so the agent can query it programmatically + +The agent picking up this issue will use `/respect-figma` to fetch design context, screenshots, variables, and assets directly from the Figma API. That's far more accurate than any text description. +
+ +
+⚠️ Design Brief (REQUIRED when issue has `design` label) + +When an issue has the `design` label, it means designer Liron needs to create or review designs. You MUST include these three sections: + +```markdown +## Problem +[The deep problem which the feature aims to solve. Be as descriptive as possible about WHY this matters and what pain it causes users.] + +## Design Brief +[Conceptual description of the design elements needed. This briefs Liron to start prototyping. Keep it conceptual — describe WHAT needs to be designed, not HOW to design it.] + +Consider including: +- Key screens/components that need design +- User flows to design +- States to consider (empty, loading, error, success) +- Interactions and animations needed +- Open questions for the designer + +## Design Requirements + +| Title | User Story | Importance | +|-------|------------|------------| +| [Requirement Name] | As a [user], I want [goal] so that [benefit] | Must Have / Should Have / Nice to Have | +``` + +**Importance Levels:** +- **Must Have** — Essential for the feature to work +- **Should Have** — Important but not blocking +- **Nice to Have** — Enhancement if time permits + +**Example:** +```markdown +## Problem +Users in the churn/retention flow often complete cancellation without engaging support. We miss opportunities to understand why they're leaving or offer solutions. + +## Design Brief +Design a visually striking element that draws attention to the Contact Support option in the churn flow. Could be an arrow, illustration, or animation. Should be impossible to miss but not obnoxious — friendly and helpful, not desperate. + +## Design Requirements + +| Title | User Story | Importance | +|-------|------------|------------| +| Attention-Grabbing Element | As a churning user, I want to notice the Contact Support option | Must Have | +| Friendly Tone | As a user, I want the prompt to feel helpful, not guilt-trippy | Must Have | +| Non-Intrusive | As a user who decided to cancel, I don't want to feel blocked | Should Have | +``` + +> ⚠️ **When NOT to use the `design` label:** +> - Bug fixes where the expected behavior is already clear +> - Code/implementation fixes (janky animations, keyboard issues) +> - Technical setup/tooling tasks +> - Backend-only work with no UI impact +
+ +
+Technical Approach (for features/refactors) + +```markdown +## Technical Approach +High-level strategies to consider: +- [Approach 1 and why it might work] +- [Approach 2 as alternative] +- [Key architectural decisions to make] + +**Recommended approach:** [If you have a preference, state it] +``` +
+ +
+Investigation Checklist (for bugs) + +```markdown +## Investigation Checklist +- [ ] Check [specific file/area] for [potential cause] +- [ ] Review recent changes in [related PR] +- [ ] Test with [specific user configuration] +- [ ] Verify [expected behavior] still works +``` +
+ +
+Constraints + +```markdown +## Constraints +- **Performance:** [e.g., Must load in <200ms] +- **Compatibility:** [e.g., Must support iOS 15+] +- **Data:** [e.g., Cannot break existing API contract] +- **Timeline:** [e.g., Must ship in 4.29.0] +``` +
+ +
+Feature Flag + +```markdown +## Feature Flag +- **Flag name:** `enable_[feature_name]` +- **Default:** OFF in prod, ON in dev +- **Rollout plan:** [e.g., 10% → 50% → 100%] +- **Kill switch:** [How to disable quickly if needed] +``` +
+ +
+Reference Implementation + +```markdown +## Reference Implementation +- Similar work: PR #[number] — [brief description] +- Follow pattern in: `[FilePath.swift]` +- Avoid approach from: PR #[number] — [why it didn't work] +``` +
+ +
+Risk/Rollback + +```markdown +## Risk Assessment +- **Risk:** [What could go wrong] +- **Likelihood:** [Low/Medium/High] +- **Impact:** [What breaks if it goes wrong] +- **Mitigation:** [How to reduce risk] +- **Rollback plan:** [How to undo — revert PR, feature flag off, data migration, etc.] +``` +
+ +
+Analytics Requirements + +```markdown +## Analytics Requirements +- **New events:** + - `[event_name]` — triggered when [condition] + - Properties: `[prop1]`, `[prop2]` +- **Success metric:** [How to measure if this worked] +- **Dashboard:** [Link or name of dashboard to update] +``` +
+ +--- + +## Step 4: Labels, Assignee & Issue Type + +- **Assignee:** David Sulitzer (@8Gaston8) +- **Labels:** Use existing labels (funnel stages, GFE, davidsulitzer.com, design, regression, etc.) + - Never use `bug`, `feature`, or `task` as labels — use Project Issue Type instead + - ⚠️ **`design` label** — Only use when designer (Liron) needs to create/review designs. When used, you MUST include Problem, Design Brief, and Design Requirements sections (see Step 3 template). + +### Issue Type — MANDATORY FOR EVERY ISSUE + +Issue Type is a GitHub-level field (not a project field). You MUST set it via GraphQL. + +**Available Issue Types:** +| Type | ID | When to Use | +|------|-----|-------------| +| **Task** | `IT_kwDOBluP0c4BBYRC` | General work items, refactors, chores | +| **Bug** | `IT_kwDOBluP0c4BBYRF` | Defects, broken functionality | +| **Feature** | `IT_kwDOBluP0c4BBYRI` | New user-facing capabilities | + +**Set Issue Type:** +```bash +# Replace with the issue's node ID (e.g., I_kwDOJF_Lac7kFn4f) +# Replace with the appropriate ID from the table above +gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "" }) { issue { id } } }' +``` + +**Verify Issue Type was set:** +```bash +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } } } }' +``` + +> ⚠️ **CRITICAL:** Always verify the issue type was set. The `gh issue view` CLI command does NOT show issue type — you MUST use the GraphQL query above to confirm. + +--- + +## Step 5: davidsulitzer.com Project Configuration + +Ensure EVERY issue is added to the **davidsulitzer.com** GitHub Project (number: 2, ID: ``) with ALL fields filled. + +### Add Issue to Project +```bash +gh project item-add 2 --owner 8Gaston8 --url +``` + +### Get Project Item ID (needed to set fields) +```bash +gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' +``` + +### Project Field IDs Reference +| Field | Field ID | Options | +|-------|----------|---------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `2c3687f7`, Todo: `f75ad846`, In Design: `646518b5`, To Clarify: `2fdcccf3`, In Development: `47fc9ee4`, Done: `98236657` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5`, Backlog: `8ef4ba21` | + +### Set Project Field Values +```bash +# Set Status (e.g., Todo) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "f75ad846" } }) { projectV2Item { id } } }' + +# Set Priority (e.g., Medium) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "2b2b659f" } }) { projectV2Item { id } } }' +``` + +### Priority Criteria +- **Critical** — Bugs affecting majority of users +- **High** — In focus, will move the needle significantly +- **Medium** — In focus, incremental improvement +- **Low** — Nice to have +- **Backlog** — Uncertain importance + +--- + +## Q1 2026 Focus: Key Metrics + +**🎯 North Star:** Define your current north-star metric and date. + +Prioritize issues that impact these metrics: + +### Top-Down Metrics (Pricing Funnel) +| Metric | Goal | +|--------|------| +| Pricing Screen → Trial Start | 20-25% | +| Trial Start → Trial Converted | 35-40% | + +### Leading Indicators +| Metric | Goal | +|--------|------| +| 🔥 Activation Rate | 60% | +| 🔄 Early Retention | 40% | +| 💾 Investment Rate | 10% | + +
+Metric Definitions + +- **🔥 Activation Rate:** % of new trial users who view 5+ place profiles from multi-category searches in their first 7 days +- **🔄 Early Retention:** % of trial users with 3+ sessions in first 3 days +- **💾 Investment Rate:** % of trial users who save at least 1 place in their first 7 days +
+ +--- + +## Step 6: Testing & Quality Requirements + +Every issue should explicitly address testing needs: + +### Unit Tests +- [ ] **New functionality** — Specify what unit tests should be added +- [ ] **Edge cases** — List specific edge cases to cover +- [ ] **Target coverage** — Aim for ≥80% on modified files + +### Regression Tests +- [ ] **Affected areas** — Identify existing functionality that could break +- [ ] **Test updates needed** — Note if existing tests need modification +- [ ] **Manual QA scope** — Define what needs manual testing before merge + +### Include in Issue Body +```markdown +## Testing Requirements +- [ ] Unit tests for [specific functionality] +- [ ] Update existing tests in [file/area] if affected +- [ ] Manual QA: [specific flows to verify] +- [ ] Regression check: [related features to verify still work] +``` + +--- + +## Step 7: Documentation & Tooling Updates + +Consider what else needs updating alongside the code: + +### Documentation +| What | When to Update | +|------|----------------| +| **README** | New setup steps, env vars, or major features | +| **API Docs** | New/changed endpoints, request/response formats | +| **Architecture Docs** | Structural changes, new patterns introduced | +| **Inline Comments** | Complex logic that needs explanation | + +### AI Agents & Commands +| What | When to Update | +|------|----------------| +| **Cursor Commands** | New workflows, changed processes | +| **Agent Rules** | New conventions, deprecated patterns | +| **Prompts/Instructions** | Changed context the AI needs to know | + +### Include in Issue Body (if applicable) +```markdown +## Documentation Updates +- [ ] Update README: [specific section] +- [ ] Update API docs: [endpoint/schema changes] +- [ ] Update agent rules: [new convention/pattern] +- [ ] Add inline comments for: [complex logic] +``` + +--- + +## Step 8: Final Verification Checklist + +Before sharing results, you MUST verify ALL of the following via GraphQL queries (not assumptions): + +### For EVERY Issue: +- [ ] Issue Type is set (query `issueType { name }` on the issue) +- [ ] Added to davidsulitzer.com project +- [ ] Status field is set (Todo, etc.) +- [ ] Priority field is set + +### For Cross-Repo Issues (any combination of iOS, Server, Tileserver): +- [ ] Blocking relationships exist (query `blockedBy` on each downstream issue — must show upstream issue(s)) +- [ ] NO sub-issue relationship (we use blocking, not parent/child hierarchy) + +### Verification Query (run for each issue): +```bash +gh api graphql -f query='{ + repository(owner: "8Gaston8", name: "") { + issue(number: ) { + title + issueType { name } + blockedBy(first: 5) { nodes { number repository { name } } } + parent { number } + } + } +}' +``` + +> ⚠️ **DO NOT SKIP VERIFICATION.** Mutations can return success without actually creating the relationship/setting the field. Always confirm with queries. + +--- + +## Step 9: Share Results + +Share the link(s) to the new issue(s) when done so I can review. + +Include a summary table showing: +- Issue links +- Issue Type (verified) +- Project Status & Priority (verified) +- Blocking relationships (verified, if applicable) diff --git a/.codex/skills/new-release/SKILL.md b/.codex/skills/new-release/SKILL.md new file mode 100644 index 0000000..699418d --- /dev/null +++ b/.codex/skills/new-release/SKILL.md @@ -0,0 +1,168 @@ +--- +name: new-release +description: Prepare a new iOS release — branch, PR, regression testing, App Store submission +disable-model-invocation: true +--- +# new-release + +Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, Mixpanel, stability score, issue tracking), see `/release-shipped`. + +--- + +## Step 1: Create the Release Branch + +Create a new release branch on the iOS repo from `main`: + +```bash +cd +git fetch origin main +git checkout -b release/VERSION origin/main +``` + +**Branch naming:** `release/VERSION` (e.g., `release/4.31.0`) + +> If this is a **hotfix**, work on the existing relevant branch instead of creating a new one from develop. + +--- + +## Step 2: Create the Draft PR + +1. Check the diff since the most recent release tag +2. Create a **draft PR** targeting `main` with full details of what changed +3. Assign **Aviad** (`aviadsteps`) as the reviewer +4. Assign **Gaston** (`8Gaston8`) as the assignee +5. Add the label `testflight` to the draft PR + +--- + +## Hotfix PR Management + +When creating a hotfix release that cherry-picks commits from existing PRs: + +1. **Create the release PR** (e.g., `release/4.28.1` → `develop`) with the cherry-picked commits +2. **Close the original PR(s)** without merging, adding a comment explaining: + - The changes were cherry-picked into the hotfix release branch + - Reference the release PR number (e.g., "Closing in favor of Hotfix PR #XXXX") + - Explain that merging both would create duplicate commits in history + - Note that the original work is preserved in the closed PR for reference + +**Why?** Cherry-picking creates new commits with different SHAs. If both PRs are merged, you get duplicate commits in git history. The release PR should be merged because: +- It has the release tag pointing to its commits +- Keeps git history clean and traceable + +--- + +## Step 3: Create Fibery Release Item + +Create a new release item in the Fibery database (nomenclature: `iOS - VERSION`): +https://steps.fibery.io/fibery/space/Product_Pipeline_Space/database/Release + +- Link all relevant Fibery dev tasks to the release item +- Share the link with Gaston + +--- + +## Step 4: Update What's New Content + +**BEFORE proceeding with regression testing**, update the in-app What's New content: + +1. Open `Steps/AppFlows/Whats New/WhatsNewContent.swift` +2. Update the `version` to match the new release version +3. Update `title` with a catchy headline for the main feature +4. Update `features` with 2-4 compelling bullet points +5. Test the What's New sheet appears correctly (Case 22b in regression plan) + +**This is a required step for every release with user-facing features!** + +If no notable features warrant a What's New announcement, return `nil` from `WhatsNewContent.current`. + +--- + +## Step 5: Regression Testing + +Carefully read the regression test plan at `REGRESSION_TESTING_PLAN.md` (in the iOS repo root) and help execute it **one test case at a time**. + +### Handling Regression Bugs + +When bugs are found during regression testing: + +1. **Create a tracking issue** for the release's regression pass (optional but recommended): + - Title: `[Regression] v{VERSION} Regression Tracking` + - Description: Summary of bugs found and release risk status + - Label: `regression` + - Use `/new-issue` skill with GraphQL for proper project integration + +2. **Create focused issues** for each concrete bug: + - Use `/new-issue` skill and follow its Step 0 pre-research requirements + - Include reproducible steps, expected vs actual behavior, and impacted files + - Use relevant domain labels only (for example `regression`, `fastlane`) and avoid generic `bug` label + - Link related issues in issue body and comments; do not require parent-child/sub-issue hierarchy + +3. **Apply triage before fixing** (see `REGRESSION_TESTING_PLAN.md`): + - Classify each bug by area, risk, and coupling + - Default fix path: land focused fix PR on `develop`, then cherry-pick/merge into release branch + - If bugs are unrelated (for example CI/Fastlane + product flow), split into separate PRs + - Only bundle fixes when they are tightly coupled in one flow/root cause + +4. **Execution order**: + - Continue regression testing and log all issues first + - Fix blockers/high-risk regressions for the release + - Track non-blockers into next version when appropriate + +--- + +## Step 6: App Store Release Notes + +Once regression testing is over, write a neat, concise, and cool release note for the App Store. + +--- + +## Step 7: Finalize the PR & Project + +1. **Update the PR** — convert from draft to ready for review +2. **Update the relevant davidsulitzer.com GitHub project items**, especially: + - Status + - iOS Version + - Server Version +3. **Add a PR comment** summarizing the regression test results clearly + +--- + +## Step 8: Tag & Create Draft GitHub Release + +1. **Add a tag** to the latest commit on the release branch: `VERSION` (without any "v" prefix) +2. **Create a draft GitHub release** at https://github.com/8Gaston8/davidsulitzer.com/releases pointing to the tag: + - Release name: `vVERSION` + - Include the release notes + - Add a **"Marketing Story"** section — a very short story to make people *feel* the change that was shipped + - Add a **"Top Down Metrics Impact"** section describing the expected impact: + +**🎯 Q1 2026 North Star:** $1M ARR for davidsulitzer.com + +### Top-Down Metrics (Pricing Funnel) +Pricing Screen → Trial Start (goal: 20-25%) +Trial Start → Trial Converted (goal: 35-40%) + +### Leading Indicators +🔥 Activation Rate (goal: 60%) +> "% of new trial users who view 5+ place profiles from multi-category searches in their first 7 days" +🔄 Early Retention (goal: 40%) +> "% of trial users with 3+ sessions in first 3 days" +💾 Investment Rate (goal: 10%) +> "% of trial users who save at least 1 place in their first 7 days" + +--- + +## What NOT to Do + +- **Don't skip the What's New update** — every release with user-facing features needs it +- **Don't rush regression testing** — log all bugs before starting fixes +- **Don't merge hotfix PRs and the original PRs** — close the original, keep history clean +- **Don't forget to tag the commit** — the tag is needed for the GitHub release +- **Don't publish the GitHub release** — keep it as a draft until the app is approved by Apple (see `/release-shipped`) + +--- + +## Next Step + +When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate Mixpanel, and close out project tracking. diff --git a/.codex/skills/new-skill/SKILL.md b/.codex/skills/new-skill/SKILL.md new file mode 100644 index 0000000..463a0f2 --- /dev/null +++ b/.codex/skills/new-skill/SKILL.md @@ -0,0 +1,514 @@ +--- +name: new-skill +description: Create a new agent skill from scratch following best practices — strong routing description, "Use when / Don't use when" blocks, templates, guardrails, and verification steps. Handles cross-repo sync, branching, issues, and PRs. Don't use for updating existing skills (/update-skill). +disable-model-invocation: true +--- +# new-skill + +Create a new agent skill from scratch, following [OpenAI's skills best practices](https://developers.openai.com/blog/skills-shell-tips). Handles the full workflow: designing the skill content to quality standards, syncing across repos, and setting up issues/PRs. + +--- + +## When to Use + +- The user wants to create a brand-new skill that doesn't exist yet +- A recurring workflow needs to be codified into a reusable skill +- The user says "create a skill for X" or "I need a skill that does Y" + +## When NOT to Use + +- Updating an existing skill → use `/update-skill` +- The task is a simple one-off that won't recur → just do it, no skill needed +- Deleting or renaming a skill → `/update-skill` handles those cases +- Installing a third-party/external skill → different workflow + +--- + +## Step 0: Understand the Request + +Before writing anything, clarify: + +1. **What does this skill do?** — Get a clear, specific description of the workflow +2. **Who/what invokes it?** — User-triggered only (`disable-model-invocation: true`) or proactive model invocation? +3. **Which repos need it?** — Relevant to all repos, or specific ones? +4. **Are there similar skills?** — Check the existing inventory to avoid overlap or confusion + +> If the user's request is vague, **ask for clarification**. "Create a skill that helps with deployments" is too broad — which environment? Which repo? What steps? + +--- + +## Step 1: Research Before Writing + +### 1a: Check for Existing Similar Skills + +Search the skill inventory for overlap: + +```bash +# List all skills in the current repo +ls .cursor/skills/ .codex/skills/ 2>/dev/null + +# Check other repos via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No skills" +done +``` + +If a similar skill exists, ask the user: "Should this be a new skill, or an update to `/existing-skill`?" + +### 1b: Research the Domain + +Before writing the skill, understand what it's automating: + +- **Read relevant code** — If the skill involves deploying, search for deploy scripts. If it involves testing, find existing test patterns. +- **Check existing workflows** — Are there manual runbooks, README instructions, or Slack threads that document this process? +- **Identify tools involved** — Which MCP tools, CLI commands, APIs, or services does this workflow use? +- **Talk to the user** — Ask what the current process looks like and what goes wrong. + +> The best skills codify something the team already does — they don't invent new processes. + +--- + +## Step 2: Write the Skill — Quality Standards + +This is the most important step. A well-written skill saves hours; a poorly-written one creates confusion. + +### 2a: The Description (Most Critical Field!) + +The description is the model's **routing logic** — it decides whether to invoke the skill. A skill with a bad description might as well not exist. + +**Formula:** What it does + When to use it + When NOT to use it (with alternatives) + +| Quality | Example | Why | +|---------|---------|-----| +| ❌ Bad | `deploy-backend-dev` | Just the name — tells the model nothing | +| ❌ Bad | `A helpful skill for deploying things` | Marketing copy — no routing signal | +| ✅ Good | `Deploy specific backend functions to the dev environment via the deployer API. Use after local testing passes and the branch is synced with develop. Don't use for prod deployments (/deploy-backend-prod) or for full deployments (always deploy individual functions).` | Clear what, when, and when-not | + +### 2b: Required Sections (Every Skill MUST Have) + +| Section | Purpose | Why It Matters | +|---------|---------|---------------| +| **Frontmatter** | name, description, disable-model-invocation | Metadata for the skill system | +| **"When to Use"** | Concrete triggers for invocation | Helps the model (and humans) know when this skill applies | +| **"When NOT to Use"** | Anti-triggers with alternatives | **Prevents misfires — can improve routing accuracy by ~20%** | +| **Numbered Steps** | Clear progression from start to finish | Agents follow procedural instructions best | +| **"What NOT to Do"** | Guardrails, anti-patterns, common mistakes | Agents make fewer errors when told what to avoid | +| **Verification Steps** | After critical actions, confirm they worked | Agents make mistakes; verification catches them early | + +### 2c: Strongly Recommended Sections + +| Section | When to Include | Why | +|---------|----------------|-----| +| **Templates** | Any repeatable output (PR comments, Slack messages, curl commands, issue bodies) | Templates inside skills are **free when unused but invaluable when needed** — they don't inflate tokens for unrelated queries | +| **Examples** | When behavior might be ambiguous | Worked examples reduce misinterpretation dramatically | +| **Quick Reference Tables** | Lookup data (field IDs, endpoint URLs, common patterns) | Saves the agent from searching every time | +| **Notes** | Edge cases, environment differences, related skills | Catches gotchas before they bite | + +### 2d: Negative Examples — The Secret Weapon + +The [OpenAI blog](https://developers.openai.com/blog/skills-shell-tips) found that making skills available can **drop correct triggering by ~20%** until you add "Don't use when..." guidance. For every "When to Use," write a corresponding "When NOT to Use" with the correct alternative: + +```markdown +## When NOT to Use + +- You're updating an existing skill → use `/update-skill` +- The task is a simple one-liner → just do it, no skill needed +- You need to delete a skill → `/update-skill` handles deletion too +``` + +For easily confused skill pairs, be extra explicit: + +```markdown +> ⚠️ This skill is for LOCAL testing only. +> If changes are already deployed to dev, use `/test-backend-changes-dev` instead. +``` + +### 2e: Writing Guidelines + +| Do ✅ | Don't ❌ | +|-------|---------| +| Be specific and actionable | Write vague guidance ("be careful") | +| Include exact commands (copy-pasteable) | Leave placeholders unexplained | +| Explain the "why" behind rules | Just list rules without context | +| Add negative examples ("Don't do X because Y") | Only write positive instructions | +| Reference other skills with `/skill-name` | Duplicate content from other skills | +| Use env vars for secrets (`$VAR_NAME`) | Hardcode credentials or tokens | +| Include verification after critical steps | Assume actions succeeded | +| Put templates inside the skill | Put templates in the system prompt | +| Keep skills self-contained | Require reading other files mid-execution | + +--- + +## Step 3: Quality Review Checklist + +Before saving, verify against this checklist: + +### Content Quality +- [ ] Description answers: What? When to use? When NOT to use? +- [ ] "When to Use" section has concrete triggers +- [ ] "When NOT to Use" section has alternatives for each case +- [ ] Steps are numbered and ordered logically +- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) +- [ ] Includes "What NOT to Do" guardrails +- [ ] Verification/confirmation steps after critical actions +- [ ] Templates for any repeatable output +- [ ] Examples where behavior might be ambiguous + +### Formatting +- [ ] Frontmatter `name` matches directory name +- [ ] `disable-model-invocation` is set (default: `true` unless proactive invocation is needed) +- [ ] References to other skills use `/skill-name` format +- [ ] No hardcoded secrets or tokens +- [ ] Markdown renders correctly (tables, code blocks, headers) + +### Routing Quality (The "Confusion Test") +- [ ] Could this skill be confused with any existing skill? If yes, add explicit disambiguation +- [ ] Would a model reading only the description know when to pick this vs alternatives? +- [ ] Are there edge cases where the wrong skill might be triggered? Add negative examples for those + +--- + +## Step 4: Determine Which Repos Need It + +| Skill Type | Typical Repos | +|-----------|---------------| +| Deployment skills | Repo being deployed | +| QA/testing skills | Repo being tested | +| Issue/project management skills | All repos (agents work in any) | +| Release skills | iOS repo primarily | +| General workflow skills | All repos | +| Skill management (like this one!) | All repos that have skills | + +When in doubt, **ask the user**. + +--- + +## Step 5: Apply to All Repos + +For **each repo** that needs the skill: + +### 5a: Create a Feature Branch + +```bash +cd +git fetch origin main # or origin/main for davidsulitzer.com +git checkout -b cursor/add-skill- origin/main +``` + +> ⚠️ **Never edit directly on `develop` or `main`.** +> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. + +### 5b: Write to BOTH Directories + +```bash +# Create directories +mkdir -p .cursor/skills/ +mkdir -p .codex/skills/ + +# Write SKILL.md to both locations (content MUST be identical) +``` + +### 5c: Update .gitignore (if needed) + +Some repos may have `.cursor` in their `.gitignore`. If so, add an exception: + +```gitignore +.cursor/* +!.cursor/skills/ +``` + +### 5d: Verify Sync Within the Repo + +```bash +diff .cursor/skills//SKILL.md .codex/skills//SKILL.md +``` + +If `diff` produces any output, fix it before committing. + +### 5e: Commit and Push + +```bash +git add .cursor/skills//SKILL.md .codex/skills//SKILL.md +git commit -m "skill(): add new skill — " +git push -u origin cursor/add-skill- +``` + +--- + +## Step 6: Create GitHub Issues (Non-Monorepo Repos Only) + +For each repo **except the davidsulitzer.com**, create a GitHub issue: + +```bash +gh issue create --repo stepscode/ \ + --title "skill(): add new skill — " \ + --body "" \ + --assignee 8Gaston8 \ + --label "fastlane" +``` + +### Issue Body Template + +```markdown +## Goal + +Create the `` skill in this repo. + +## Context + +[Brief description of what the skill does and why it's being created.] + +## Changes + +- New skill: `.cursor/skills//SKILL.md` +- New skill: `.codex/skills//SKILL.md` + +## Cross-Repo Sync + +This skill is being created across all relevant repos: +- [ ] `stepscode/davidsulitzer.com` — PR #XXX +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX + +All instances must remain identical. + +## Out of Scope + +- Content changes to other skills + +## Acceptance Criteria + +- [ ] Skill file exists in `.cursor/skills//SKILL.md` +- [ ] Skill file exists in `.codex/skills//SKILL.md` +- [ ] Both copies are identical (`diff` produces no output) +- [ ] Content matches all other repos +``` + +### Set Issue Type to Task + +```bash +ISSUE_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' --jq '.data.repository.issue.id') + +gh api graphql -f query="mutation { updateIssue(input: { id: \"$ISSUE_ID\", issueTypeId: \"IT_kwDOBluP0c4BBYRC\" }) { issue { id } } }" +``` + +### Add to davidsulitzer.com Project (Status: In Review, Priority: Low) + +```bash +# Add to project +gh project item-add 2 --owner 8Gaston8 --url + +# Get project item ID +ITEM_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id') + +# Set Status: In Review +gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzgntIc4\", value: { singleSelectOptionId: \"2895eb73\" } }) { projectV2Item { id } } }" + +# Set Priority: Low +gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzg5mgJc\", value: { singleSelectOptionId: \"9d5cb3c5\" } }) { projectV2Item { id } } }" +``` + +--- + +## Step 7: Create Pull Requests + +Create a PR for **every repo** that was changed: + +```bash +gh pr create \ + --repo stepscode/ \ + --base develop \ # or main for davidsulitzer.com + --head cursor/add-skill- \ + --title "skill(): add new skill — " \ + --body "" \ + --assignee 8Gaston8 +``` + +### PR Body Template + +```markdown +## Summary + +Create the `` skill — [one-line description]. + +## What This Skill Does + +[2-3 sentences describing the skill's purpose and workflow] + +## Cross-Repo Sync + +- `stepscode/davidsulitzer.com` — PR #XXX +- `8Gaston8/davidsulitzer.com` — PR #XXX + +All skill files are identical across repos. + +Closes # +``` + +> For davidsulitzer.com PRs, omit `Closes #` since there's no associated issue. + +### Assign & Request Review (MANDATORY) + +```bash +# Get PR node ID +PR_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' --jq '.data.repository.pullRequest.id') + +# Request review from Aviad +gh api graphql -f query="mutation { requestReviews(input: { pullRequestId: \"$PR_ID\", userIds: [\"MDQ6VXNlcjQ1NTMwOTMw\"] }) { pullRequest { number } } }" +``` + +> ⚠️ **Do this for EVERY PR created.** No exceptions. + +### Enable Auto-Merge (iOS Only) + +```bash +gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash +``` + +> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on other repos. + +--- + +## Step 8: Final Verification & Share Links + +### 8a: Verify Content Sync + +```bash +# Compare across repos (adjust paths based on which repos have the skill) +diff /.codex/skills//SKILL.md /.codex/skills//SKILL.md +``` + +### 8b: Verify Issues + +```bash +# For each non-davidsulitzer.com issue: +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' +``` + +### 8c: Share All Links (MANDATORY) + +**You MUST always end by sharing every relevant link:** + +```markdown +## Here are all the links: + +### Pull Requests +- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX + +### Issues +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- *(Monorepo: no issue needed)* + +### Summary Table +| Repo | PR | Issue | Status | Skill Synced | +|------|-----|-------|--------|-------------| +| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | +| davidsulitzer.com | [#XXX](url) | [#XXX](url) | Ready for Review | ✅ | +``` + +> ⚠️ **Do NOT skip sharing links.** Always use full URLs. + +--- + +## What NOT to Do + +- **Don't create a skill without researching existing ones** — you might be duplicating +- **Don't write a description that's just the skill name** — the description is routing logic +- **Don't skip "When NOT to Use"** — prevents misfires, improves routing by ~20% +- **Don't leave templates in the system prompt** — put them inside the skill (free when unused) +- **Don't hardcode secrets** — always use `$ENV_VAR` references +- **Don't create the skill in only one repo when it belongs in multiple** — sync all of them +- **Don't skip the quality checklist** — a sloppy skill is worse than no skill +- **Don't forget verification steps in the skill you're writing** — agents make mistakes +- **Don't create skills for one-off tasks** — skills are for recurring workflows + +--- + +## Reference: Skill File Template + +```markdown +--- +name: +description: +disable-model-invocation: true +--- +# + +[Opening line: what this skill does in one sentence] + +--- + +## When to Use + +- [Concrete trigger 1] +- [Concrete trigger 2] + +## When NOT to Use + +- [Anti-trigger 1] → use `/alternative` instead +- [Anti-trigger 2] → just do X directly + +--- + +## Step 1: [First Action] + +[Instructions with exact commands] + +--- + +## Step 2: [Second Action] + +[Instructions with verification] + +--- + +... + +--- + +## What NOT to Do + +- [Guardrail 1] — [why it matters] +- [Guardrail 2] — [why it matters] + +--- + +## Notes + +- [Environment info, related skills, edge cases] +``` + +--- + +## Reference: Project Field IDs + +| Field | Field ID | Key Options | +|-------|----------|-------------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | In Review: `2895eb73`, Done: `782fbd7c` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Low: `9d5cb3c5` | +| **Issue Type: Task** | `IT_kwDOBluP0c4BBYRC` | — | + +--- + +## Reference: Repo Quick Info + +| Repo | GitHub | Default Branch | +|------|--------|----------------| +| Monorepo | `stepscode/davidsulitzer.com` | `main` | +| iOS | `8Gaston8/davidsulitzer.com` | `develop` | +| Server | `8Gaston8/davidsulitzer.com` | `develop` | +| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | + +--- + +## Notes + +- **Skill names must be kebab-case** — lowercase, words separated by hyphens +- **One SKILL.md per skill** — each skill lives in its own directory +- **Both `.cursor/` and `.codex/` must always be in sync** within each repo +- **All repos must be in sync** with each other for shared skills +- **Monorepo gets PRs but NO issues** — only the subrepos get issues +- **Test after creating** — always suggest the user tests the skill with a real invocation +- Related skill: `/update-skill` — for updating, renaming, or deleting existing skills diff --git a/.codex/skills/qa-start/SKILL.md b/.codex/skills/qa-start/SKILL.md new file mode 100644 index 0000000..da67d95 --- /dev/null +++ b/.codex/skills/qa-start/SKILL.md @@ -0,0 +1,318 @@ +--- +name: qa-start +description: Comprehensive QA workflow for PRs - adapts to iOS, server, or davidsulitzer.com context +disable-model-invocation: true +--- +# qa-start + +Help me QA this PR step by step. + +## Step 1: Checkout & Analyze + +1. **Checkout to the PR's branch** (in the appropriate repo) +2. **Analyze the PR changes** to determine what type of QA is needed: + - **iOS-only changes** → Use iOS QA workflow + - **Server-only changes** → Use Server QA workflow + - **Mixed changes (davidsulitzer.com)** → Run both workflows as needed + +--- + +## iOS QA Workflow + +For iOS/client changes: + +### Server Dependency Check + +**If NO server dependency:** +- Proceed with manual Xcode testing +- I will guide you through test scenarios +- You build and run in Xcode + +**If server changes must deploy first (blocking):** +⚠️ **Skip Xcode testing for now.** Instead: +1. **Code review only** — Verify client code is compatible and ready for server changes +2. **Still mark PR as Ready for Review** — Don't wait for server deployment +3. **Note:** End-to-end testing will happen during regression testing on the release branch (created from develop where all client changes will have been merged) + +### iOS PR Comment Template (Full QA) + +```markdown +## ✅ QA Complete + +### Testing Summary +| Area | Status | Notes | +|------|--------|-------| +| Happy Path | ✅ Pass | Main feature works | +| Edge Cases | ✅ Pass | Handled gracefully | +| UI/UX | ✅ Pass | Looks good | +| Regression | ✅ Pass | No issues | + +### Devices/Simulators Tested +- iPhone 17 Pro (latest iOS) + +--- +*QA performed by Cursor Agent + manual Xcode testing* +``` + +### iOS PR Comment Template (Server-Blocked) + +```markdown +## ✅ Code Review Complete — Server Dependency + +### Code Review +- ✅ Client code reviewed and compatible with expected server changes +- ✅ Error handling in place for new/modified endpoints + +### Server Dependency +- **Server PR:** #XXX +- **Note:** E2E testing will occur during regression on the release branch + +--- +*Code review performed by Cursor Agent* +``` + +--- + +## Server QA Workflow + +For server/backend changes, follow this **5-phase process**: + +### Phase 1: Local Testing +> Reference: `/test-backend-changes-local` + +**All yarn commands must run from the `api/` directory:** + +1. `cd api` (or `cd /api` if in the davidsulitzer.com root) +2. Create a test plan covering all new/changed functionality +3. Verify backward and forward compatibility +4. Run relevant unit tests: `yarn test:file ` +5. Start local server: `yarn api::offline::no_debug` +6. Execute test plan against `http://localhost:3000` +7. Stop local server when done + +> **Note:** Local server connects to **dev-mobile database**, not prod. + +### Phase 2: Deploy to Dev +> Reference: `/deploy-backend-dev` + +**Pre-deployment checklist (MUST verify before deploying):** +1. **Branch is synced with develop** — Run `git fetch origin main && git merge origin/main` to sync. This prevents deployment failures from conflicting or outdated code. +2. **Tests are passing** — Verify that all tests for your new code pass locally. Failing tests = failed deployment. +3. **Resolve any merge conflicts** if they occur during sync +4. **Push changes** to remote + +**Deploy functions one by one:** +```bash +source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=dev-mobile&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" +``` + +> **Required env vars:** `DEPLOYER_BASE_URL`, `DEPLOYER_USER_NAME`, `DEPLOYMENT_API_KEY` (from `api/.env`) +> **Note:** If in the davidsulitzer.com, use `davidsulitzer.com/api/.env` instead. + +**Wait for deployment confirmation:** +```bash +# Wait 30-45 seconds for deployment to complete +sleep 40 + +# Then check #deployments_from_ci Slack channel (ID: C01BS5TKHAN) +# Look for ":tada: A deploy_specific_function_job job has succeeded!" +``` + +⚠️ **Deploy functions one at a time** — wait for success message before deploying next function. + +**Important:** +- Only deploy relevant functions — **no full deployments** +- A script might comment out some code — make sure to **uncomment** anything that shouldn't be commented before deploying + +### Phase 3: Test on Dev +> Reference: `/test-backend-changes-dev` + +1. Reuse the test plan from local testing +2. Get/refresh dev access token if needed (via `/renew` endpoint) +3. Execute test plan against `$DEV_API_BASE_URL` (from `api/.env`) +4. Verify all scenarios pass + +### Phase 4: Claim a logTest slot + Deploy to Prod +> Reference: `/deploy-backend-prod` + +**IMPORTANT:** Claim a `logTest` slot first (`logTest`, `logTest2` ... `logTest10`) so multiple QA runs can happen in parallel without collisions. + +1. Claim a slot in dev: +```bash +source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/claim" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_id": "", + "branch": "", + "pr_number": + }' +``` + - Save the returned `slot` value (example: `logTest4`) as `` + - If claim fails with "no slots available", wait and retry; do not deploy to a random slot + +2. Modify `api/api/general.js` (or `davidsulitzer.com/api/api/general.js` from davidsulitzer.com root): + - Add action name to `allowedProdActions` array + - Add handler in `logTestFunction` + - **Temporarily comment out** this authorization check: + ```javascript + if (process.env.NAME_OF_STAGE !== "dev" && process.env.IS_OFFLINE !== "true") { + return { + statusCode: 401, + body: JSON.stringify({ message: "Unauthorized" }), + }; + } + ``` +3. Commit, push, and deploy the claimed slot to prod: +```bash +source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=prod&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" +``` + +4. Wait and verify deployment (same sleep + Slack check pattern) + +⚠️ **Only deploy to real functions (not logTest slots) if the user explicitly requests it!** + +### Phase 5: Test on Prod +> Reference: `/test-backend-changes-prod` + +1. Reuse the test plan +2. Get/refresh prod access token if needed +3. Test via the claimed slot endpoint: `$PROD_API_BASE_URL/v1/` (from `api/.env`) +4. Required headers for prod: + - `Origin: $MAP_MANAGER_ORIGIN` (from `api/.env`) + - `Content-Type: application/json` + - `Authorization: ` +5. Verify all scenarios pass +6. Release the slot when done (always release, even if tests fail): +```bash +source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/release" \ + -H "Content-Type: application/json" \ + -d '{ + "slot": "", + "agent_id": "" + }' +``` + +> **Note:** If changes were deployed to real functions (not just logTest slots), test using those real endpoints. + +### Server PR Comment Template + +```markdown +## ✅ QA Complete + +### Testing Summary +| Phase | Status | Details | +|-------|--------|---------| +| Local Testing | ✅ Pass | X unit tests passing | +| Dev Deployment | ✅ Done | Functions deployed | +| Dev Testing | ✅ Pass | All scenarios verified | +| Prod Deployment | ✅ Done | Deployed to logTest | +| Prod Testing | ✅ Pass | All scenarios working | + +### Compatibility +- ✅ Backward compatible +- ✅ Forward compatible + +--- +*QA performed by Cursor Agent* +``` + +--- + +## Deployment Monitoring Pattern + +When deploying, always use this pattern to wait and verify: + +```bash +# 1. Trigger deployment (returns immediately) +curl "$DEPLOYER_BASE_URL?..." + +# 2. Wait for CI to complete (30-45 seconds typically) +sleep 40 + +# 3. Check Slack for success message +# Use MCP tool: slack_get_channel_history with channel_id: C01BS5TKHAN +# Look for: ":tada: A deploy_specific_function_job job has succeeded!" +# Match the job number from your deployment request +``` + +--- + +## Monorepo Mixed Changes + +When the PR has both iOS and server changes: + +1. **First:** Complete the Server QA workflow (all 5 phases) +2. **Then:** Help with iOS QA (or code review if server-blocked) +3. Document both in the PR comment + +--- + +## Finalize PR + +After QA completes: + +1. **Commit & Push** any remaining changes +2. **Add detailed PR comment** using the appropriate template above +3. **Update the Project Board status** (see below) +4. **Notify the right people** based on the status + +### Step A: Determine the Status + +**Ask yourself: Would a screenshot look different before vs after your changes?** + +| Answer | Status | What type of review Gaston needs to do | +|--------|--------|----------------------------------------| +| **YES** (changed layouts, colors, text, images, animations, spacing, or anything the user sees) | "Human QA" | Build in Xcode and visually verify the UI changes | +| **NO** (pure logic, API, analytics, refactoring, performance, crash fixes) | "In Review" | Review the PR diff/code only (no Xcode needed) | + +### Step B: Update Project Board Status + +Use GitHub GraphQL API to update the status: + +**1. Get the issue's project item ID:** +```graphql +query { + repository(owner: "8Gaston8", name: "REPO_NAME") { + issue(number: ISSUE_NUMBER) { + projectItems(first: 10) { + nodes { + id + project { id } + } + } + } + } +} +``` +Replace `REPO_NAME` with `davidsulitzer.com`, `davidsulitzer.com`, or `davidsulitzer.com` as appropriate. +Find the item where `project.id = ""` (davidsulitzer.com project) + +**2. Update the status:** +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "ITEM_ID_FROM_STEP_1" + fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4" + value: { singleSelectOptionId: "OPTION_ID" } + }) { + projectV2Item { id } + } +} +``` + +**Status option IDs:** +- "Human QA": `6a5c111e` +- "In Review": `2895eb73` + +### Step C: Post Comment for Gaston's Review + +**Keep the PR as a draft.** Gaston will mark it Ready for Review after his review. + +**If "Human QA":** +- Post PR comment: `🔍 Visual QA needed — @8Gaston8 please build in Xcode and verify the UI changes look correct.` + +**If "In Review":** +- Post PR comment: `✅ QA complete — @8Gaston8 no visual changes, just verify the code looks good.` + +> **Note:** Do NOT mark the PR as Ready for Review or enable auto-merge. Gaston will do this after reviewing the AI's output. diff --git a/.codex/skills/refine-spec/SKILL.md b/.codex/skills/refine-spec/SKILL.md new file mode 100644 index 0000000..4e56004 --- /dev/null +++ b/.codex/skills/refine-spec/SKILL.md @@ -0,0 +1,356 @@ +--- +name: refine-spec +description: Refine and improve an existing GitHub issue's spec through codebase research, edge case analysis, and iterative PM discussion. +disable-model-invocation: true +--- +# refine-spec + +Take an existing GitHub issue and elevate its spec to production-ready quality. This skill guides you through codebase research, section compliance (per the `/new-issue` skill), edge case discovery, and iterative refinement with the PM — so the AI agent or developer who picks it up makes zero mistakes. + +--- + +## When to Use + +- A spec exists but feels thin, vague, or missing context +- A design decision was just made and needs to be baked into the spec +- The PM wants to stress-test edge cases before handing off to development +- An issue was created quickly and now needs the full `/new-issue` treatment +- The spec drifted after discussion and needs to be reconciled with decisions made + +--- + +## Step 0: Read the Issue + +### 0a: Fetch the Full Issue + +```bash +gh issue view --repo stepscode/ --json title,body,labels,assignees,state,milestone +``` + +Take note of: +- **Labels** — especially `design` (triggers mandatory Design Brief sections) and `fastlane` +- **Milestone** — context for prioritization +- **Current body** — what's already there vs what's missing + +### 0b: Identify What the User Wants to Change + +Ask yourself: +1. Is this a **full rewrite** or **targeted improvements**? +2. Did the PM make specific design decisions that need to be captured? +3. Are there new requirements, constraints, or scope changes? +4. Is the PM asking for edge case analysis? + +> If the user's request is vague (e.g., "make this spec better"), ask what aspect they want improved: completeness, clarity, edge cases, technical depth, or all of the above. + +--- + +## Step 1: Pre-Research the Codebase + +Before changing a single word, investigate the codebase. This is the same research from `/new-issue` Step 0 — but applied to an existing issue that may have skipped this step. + +### 1a: Search for Relevant Code + +Search **all repos** for keywords related to the feature/bug: + +```bash +# 1. Always search the current repo first +rg -l "" . + +# 2. If in the davidsulitzer.com with submodules checked out, also search sibling repos: +rg -l "" davidsulitzer.com/ 2>/dev/null + +# 3. If in a standalone repo checkout (sibling dirs won't exist), use GitHub API: +gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 +``` + +> **Note:** The `2>/dev/null` suppresses errors when sibling directories don't exist (standalone checkouts). Always search `.` first so you never miss the current repo's code. + +**Capture for each relevant file:** +- Full path and key line numbers +- What the code does (brief description) +- How it relates to the feature being specified + +### 1b: Check Recent Changes + +```bash +git log --oneline -10 -- +``` + +Note any recent PRs that touched the area — they may contain context or introduce constraints. + +### 1c: Check for Related Issues + +```bash +# Open issues +gh issue list --repo stepscode/ --search "" --limit 10 + +# Recently closed (possible regression context) +gh issue list --repo stepscode/ --search "" --state closed --limit 5 +``` + +### 1d: Check Test Coverage + +```bash +# iOS tests +rg -l "" davidsulitzer.com/AtlyTests/ 2>/dev/null + +# Server tests +rg -l "" davidsulitzer.com/api/test/ 2>/dev/null +``` + +### 1e: Identify Dependencies + +Look for: +- Feature flags that gate the behavior +- API endpoints involved (grep for route definitions) +- Third-party services (Mixpanel, RevenueCat, Firebase, etc.) +- Caching layers +- Cross-repo dependencies (iOS ↔ Server ↔ Tileserver) + +> ⚠️ **Do NOT skip pre-research.** Even if the issue already has some code references, they may be outdated or incomplete. Fresh research catches drift. + +--- + +## Step 2: Audit Sections Against the `/new-issue` Skill + +Compare the existing issue body against the required and conditional sections from `/new-issue` Step 3. Build a gap table: + +### Required Sections (MUST exist in every issue) + +| Section | Present? | Notes | +|---------|----------|-------| +| **Pre-Research Findings** | ✅/❌ | Code refs, recent changes, related issues, test coverage, dependencies | +| **Goal** | ✅/❌ | Clear one-liner | +| **Context/Background** | ✅/❌ | Why this matters | +| **Requirements** | ✅/❌ | What needs to be built | +| **Out of Scope** | ✅/❌ | What's NOT part of this issue | +| **Acceptance Criteria** | ✅/❌ | Checkboxes for definition of done | +| **Testing Requirements** | ✅/❌ | Unit tests, regression, manual QA | +| **Related Issues/PRs** | ✅/❌ | Cross-references | + +### Conditional Sections (include when applicable) + +| Section | Applicable? | Present? | Notes | +|---------|------------|----------|-------| +| **Problem + Design Brief + Design Requirements** | Only if `design` label | ✅/❌ | ⚠️ REQUIRED when `design` label is present | +| **Technical Approach** | Features/refactors | ✅/❌ | Architecture, files to change | +| **Constraints** | Performance/compat/deadline | ✅/❌ | | +| **Feature Flag** | New features, risky changes | ✅/❌ | | +| **Reference Implementation** | Similar patterns exist | ✅/❌ | | +| **Risk/Rollback** | Medium+ risk changes | ✅/❌ | | +| **Analytics Requirements** | New Mixpanel events | ✅/❌ | | +| **Documentation Updates** | README, API docs, agent rules | ✅/❌ | | + +> Present this gap table to the PM. Let them decide which missing sections are relevant before you start writing. + +--- + +## Step 3: Analyze Edge Cases + +This is where specs go from "good" to "bulletproof." For every feature in the spec, systematically think through: + +### 3a: State Transitions + +- What are all the possible states? (e.g., loading, active, inactive, error, empty) +- What triggers each transition? +- Can the user get stuck in an unexpected state? +- What happens on state reset (new session, navigation, app restart)? + +### 3b: Interaction Combinations + +- What happens when this feature interacts with other features in the same area? +- If there's a toggle/filter, does it survive navigation, back-button, other selections? +- What about concurrent actions (rapid tapping, network in flight)? + +### 3c: Data Edge Cases + +- What if the data is empty? (No results, no items, no content) +- What if the data is missing fields? (Null/undefined values from API) +- What if the data is stale? (Cached, outdated, timezone mismatch) +- What about pagination? Does the feature work across pages? + +### 3d: UI Edge Cases + +- What about different sheet/modal states? (Minimized, expanded, transitioning) +- What about transient states? (Search focused, autocomplete showing, animations in progress) +- Dark mode? Dynamic Type? VoiceOver? +- Does it work when related UI elements are hidden/empty? + +### 3e: Platform Edge Cases + +- App backgrounding and foregrounding +- App kill and restart +- Network errors mid-action +- Low connectivity / timeout scenarios + +### 3f: Present Edge Cases to the PM + +**Do NOT silently make decisions about edge cases.** Present them as a table: + +| Scenario | Question / Suggested Behavior | +|----------|-------------------------------| +| [Edge case 1] | [What should happen? / Here's what I suggest: ...] | +| [Edge case 2] | [What should happen? / Here's what I suggest: ...] | + +Let the PM confirm or override each one before writing them into the spec. + +> **The goal is to eliminate ambiguity.** Every edge case that's undocumented is a coin-flip for the implementer. Document the decision, even if the decision is "we don't handle this — it's out of scope." + +--- + +## Step 4: Draft the Updated Spec + +### 4a: Preserve What's Good + +Don't rewrite sections that are already solid. Only modify what needs improvement. + +### 4b: Add Missing Sections + +Fill in every gap identified in Step 2, using the templates from `/new-issue` Step 3. + +### 4c: Weave in Edge Cases + +Edge cases should NOT be a standalone dump. Integrate them: +- **Behavioral edge cases** → Add to an "Edge Cases & Clarifications" table +- **Empty/error states** → Add to the relevant spec section (e.g., "Empty State" subsection) +- **State management edge cases** → Add to the "State Management" or "Toggle Behavior" section +- **Testing edge cases** → Add to "Testing Requirements" as specific test cases + +### 4d: Add Pre-Research Findings + +If the original spec lacked code references, add the **Pre-Research Findings** section with: +- Relevant file paths + line numbers + brief descriptions +- Recent changes (PRs that touched the area) +- Related issues (open and closed) +- Test coverage status +- Dependencies (APIs, feature flags, services) + +### 4e: Ensure Technical Approach is Implementation-Ready + +The Technical Approach section should give the implementer a clear roadmap: +- **Files to change** — with specific descriptions of what changes in each file +- **Key implementation notes** — gotchas, caveats, patterns to follow or avoid +- **Recommended approach** — if there are multiple valid approaches, state which one and why + +--- + +## Step 5: Update the Issue + +### 5a: Apply the Changes + +```bash +gh issue edit --repo stepscode/ --body "$(cat <<'EOF' + +EOF +)" +``` + +> ⚠️ **This replaces the entire body.** Always include ALL sections — both unchanged and updated — in the new body. Never use `--body-file` with partial content. + +### 5b: Labels — Do NOT Remove + +> ⚠️ **NEVER remove existing labels during spec refinement.** Labels like `design` are kept permanently on the issue — they indicate the issue *involves* design work, not that design is pending. Even if the design decision has been made, the label stays. + +You may **add** labels if the refinement reveals they're needed (e.g., adding `fastlane`): + +```bash +gh issue edit --repo stepscode/ --add-label "fastlane" +``` + +### 5c: Do NOT Update Project Status During Refinement + +> ⚠️ **Do NOT change the project status at any point during spec refinement.** Regardless of what status the issue was in when the conversation started, leave it as-is until the PM explicitly confirms the spec is finalized. + +**Only when the PM gives final approval** (e.g., "looks good, this is ready"), update the status to **Ready for Dev**: + +```bash +# Get project item ID via the issue itself (avoids the last-N item window problem) +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id' + +# Move to "Ready for Dev" — ONLY after PM final approval +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "cac3311f" } }) { projectV2Item { id } } }' +``` + +> **Note:** We query `projectItems` on the issue itself rather than scanning the last N project items. This works regardless of how many items are in the project. + +--- + +## Step 6: Iterate with the PM + +Spec refinement is rarely one-shot. After the first update: + +### 6a: Summarize What Changed + +Tell the PM exactly what you added, removed, or modified — in plain language, not diffs. + +### 6b: Flag Open Questions + +If any edge cases or design decisions are still unresolved, list them explicitly: + +> **Still need your input on:** +> 1. [Question about behavior X] +> 2. [Question about scope Y] +> 3. [Question about edge case Z] + +### 6c: Apply Follow-Up Changes + +When the PM responds with decisions: +1. Update the spec immediately +2. Summarize what changed in this iteration +3. Repeat until the PM is satisfied + +> **Keep iterating.** A spec is done when the PM says it's done, not when you think it's complete. + +--- + +## Step 7: Final Quality Check + +Before declaring the spec done, verify: + +### Content Quality +- [ ] Every section from Step 2's gap table is addressed (present or explicitly N/A) +- [ ] Edge cases are documented with clear expected behaviors +- [ ] Technical approach references specific file paths and line numbers +- [ ] Acceptance criteria are concrete and testable (not vague) +- [ ] Testing requirements include unit tests, regression tests, AND manual QA +- [ ] Out of scope is explicit (prevents scope creep during implementation) + +### Issue Metadata +- [ ] Labels are unchanged (never remove existing labels — especially `design`) +- [ ] Milestone is set +- [ ] Assignee is set +- [ ] Issue type is set (Feature / Bug / Task) +- [ ] Project status is unchanged (only update to "Ready for Dev" after PM gives explicit final approval) + +### Readability +- [ ] A developer or AI agent reading this spec for the first time can implement the feature without asking clarifying questions +- [ ] No contradictions between sections +- [ ] No placeholder text or TODOs left in the body +- [ ] Tables are formatted correctly (render check) + +--- + +## Anti-Patterns (What NOT to Do) + +| Anti-Pattern | Why It's Bad | Do This Instead | +|-------------|-------------|-----------------| +| Rewriting the entire spec without reading it first | Loses context and decisions already made | Read first, preserve what's good | +| Making design decisions without asking the PM | You're refining, not designing | Present options, let PM decide | +| Dumping edge cases without suggested behaviors | Shifts the thinking burden to the implementer | Always include a suggested behavior | +| Adding sections just to check a box | Bloats the spec with low-value content | Only add sections that provide real value | +| Ignoring the `design` label requirements | The `/new-issue` skill REQUIRES Problem + Design Brief + Design Requirements when `design` label is present | Check labels first, comply with the section requirements | +| Removing labels during refinement | Labels are permanent markers — `design` means the issue involves design, not that design is pending | Never remove labels; only add if needed | +| Updating project status without PM approval | Only the PM decides when a spec is ready for development | Leave status unchanged until PM explicitly says "ready" | +| Updating the spec without telling the PM what changed | PM can't review what they can't see | Always summarize changes after each update | +| Skipping pre-research because "the issue already has context" | Existing context may be outdated or incomplete | Always do fresh research | +| Writing vague acceptance criteria | "Works correctly" is not testable | Be specific: "When X happens, Y should result" | + +--- + +## Quick Reference: Relationship to Other Skills + +| Skill | Relationship | +|-------|-------------| +| `/new-issue` | `refine-spec` applies `/new-issue`'s section requirements to an EXISTING issue. If the issue was created without `/new-issue`, this skill brings it up to standard. | +| `/qa-start` | A well-refined spec makes QA faster. The testing requirements section feeds directly into QA checklists. | +| `/update-skill` | If the refinement process reveals a gap in any skill (including this one), use `/update-skill` to improve it. | diff --git a/.codex/skills/release-shipped/SKILL.md b/.codex/skills/release-shipped/SKILL.md new file mode 100644 index 0000000..0694ec6 --- /dev/null +++ b/.codex/skills/release-shipped/SKILL.md @@ -0,0 +1,278 @@ +--- +name: release-shipped +description: Post-Apple-approval steps — publish release, Mixpanel annotation, close out tracking, assign iOS versions to server & tile-server issues +disable-model-invocation: true +--- +# release-shipped + +Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating Mixpanel, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. + +> **Prerequisite:** The `/new-release` skill must have been completed first — the draft GitHub release, tag, and PR should already exist. + +--- + +## Step 1: Publish the GitHub Release + +Convert the draft GitHub release to a published (non-draft) release: + +```bash +# Find the draft release for this version +gh release list --repo 8Gaston8/davidsulitzer.com --json tagName,isDraft | jq '.[] | select(.isDraft == true)' + +# Publish it (replace VERSION with the actual version, e.g. "v4.30.0") +gh release edit VERSION --repo 8Gaston8/davidsulitzer.com --draft=false +``` + +> **Verify:** `gh release view VERSION --repo 8Gaston8/davidsulitzer.com --json isDraft` should return `false`. + +--- + +## Step 2: Add Mixpanel Annotation + +Annotate the release in Mixpanel with the **actual release date** (when Apple made it live, not when it was submitted): + +```bash +curl -X POST \ + 'https://mixpanel.com/api/app/projects//annotations' \ + --user "$MIXPANEL_SERVICE_ACCOUNT_USERNAME:$MIXPANEL_SERVICE_ACCOUNT_SECRET" \ + -H 'Content-Type: application/json' \ + -d '{ + "description": "iOS VERSION released", + "datetime": "YYYY-MM-DDTHH:MM:SSZ", + "tags": ["ios-release", "VERSION"] + }' +``` + +> **Note:** `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set in your environment (from `.env` file at `davidsulitzer.com/api/.env`). + +--- + +## Step 3: List New Mixpanel Events + +If any new Mixpanel events were created as part of this release, list them so Gaston can prepare relevant Mixpanel dashboards or reports. + +--- + +## Step 4: Update GitHub Issue Version Tracking — iOS Issues + +Check all GitHub issues associated with PRs that have been merged since the last version and update their davidsulitzer.com GitHub project items: + +1. **Find merged PRs** since the last release tag +2. **For each merged PR**, find the connected GitHub issues (via GraphQL `timelineItems` with `CONNECTED_EVENT`) +3. **Update the project items** — set the iOS Version field on each issue's project item + +```graphql +# Example: Get connected issues for a PR +{ + repository(owner: "8Gaston8", name: "davidsulitzer.com") { + pullRequest(number: PR_NUMBER) { + timelineItems(first: 50, itemTypes: [CONNECTED_EVENT]) { + nodes { + ... on ConnectedEvent { + subject { ... on Issue { number title } } + } + } + } + } + } +} +``` + +### Setting the iOS Version field + +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "" + fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" + value: { text: "" } + }) { + projectV2Item { id } + } +} +``` + +> ⚠️ **Always double-check** that the associated PR was merged *after* the previous version was released. Verify by checking the latest commit tags. + +--- + +## Step 5: Assign iOS Versions to Server & Tile-Server Issues + +Server and tile-server issues ship independently (via their own deploy cycles), but we want them tagged with the **iOS Version that was current when they shipped**. This creates a unified timeline: when you look at an iOS version, you see everything that shipped around that time. + +### The Rule + +> If a server or tile-server issue was closed (shipped) between iOS version A's release date and iOS version B's release date, assign it **iOS Version = A**. + +### 5a: Build the iOS Release Timeline + +```bash +# Get the last N iOS releases with their dates +gh release list --repo 8Gaston8/davidsulitzer.com --limit 20 +``` + +This gives you ordered pairs like: +- `v4.29.0` → `2026-01-27T08:55:57Z` +- `v4.30.0` → `2026-02-12T16:47:11Z` + +### 5b: Find Unassigned Server Issues + +Query all project items and find those from `davidsulitzer.com` that: +- Have **Status = Done** (shipped) +- Have a **Server Version** set (confirms it's a server issue that was deployed) +- Do **NOT** have an **iOS Version** set + +```graphql +# Paginate through all project items (100 at a time) +{ + organization(login: "8Gaston8") { + projectV2(number: 2) { + items(first: 100, after: CURSOR) { + pageInfo { hasNextPage endCursor } + nodes { + id + fieldValues(first: 20) { + nodes { + ... on ProjectV2ItemFieldTextValue { + text + field { ... on ProjectV2Field { name } } + } + ... on ProjectV2ItemFieldSingleSelectValue { + name + field { ... on ProjectV2SingleSelectField { name } } + } + } + } + content { + ... on Issue { + number + title + repository { name } + closedAt + state + } + } + } + } + } + } +} +``` + +Filter for items where: +- `repository.name == "davidsulitzer.com"` +- `status == "Done"` +- `Server Version` field is set (non-empty) +- `iOS Version` field is empty + +### 5c: Find Unassigned Tile-Server Issues + +Same query as above, but filter for: +- `repository.name == "davidsulitzer.com"` +- `status == "Done"` +- `Tile Server Version` field is set (non-empty) +- `iOS Version` field is empty + +### 5d: Assign iOS Versions + +For each unassigned issue, compare its `closedAt` timestamp against the iOS release timeline. Find the **latest iOS release that was released before the issue was closed** — that's the iOS Version to assign. + +**Algorithm:** +``` +for each unassigned_issue: + closed_at = issue.closedAt + ios_version = None + for release in ios_releases_sorted_by_date_ascending: + if release.date <= closed_at: + ios_version = release.version + else: + break + assign ios_version to issue +``` + +**Set the iOS Version:** + +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "" + fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" + value: { text: "" } + }) { + projectV2Item { id } + } +} +``` + +### 5e: Verify Assignments + +After updating, re-query each item to confirm the iOS Version was set correctly: + +```graphql +{ + node(id: "") { + ... on ProjectV2Item { + fieldValues(first: 20) { + nodes { + ... on ProjectV2ItemFieldTextValue { + text + field { ... on ProjectV2Field { name } } + } + } + } + content { + ... on Issue { number title } + } + } + } +} +``` + +### 5f: Report to the User + +Present the assignments in a clear table: + +| Issue | Repo | Server/Tile Version | Closed | Assigned iOS Version | +|-------|------|---------------------|--------|---------------------| +| #1234 | davidsulitzer.com | 3.140.0 | Jan 20 | 4.28.0 | +| #175 | davidsulitzer.com | 1.38.0 | Feb 5 | 4.29.0 | + +Also report any issues that could NOT be assigned (e.g., closed before the earliest known iOS release). + +--- + +## Project Field Reference + +| Field | Field ID | Type | +|-------|----------|------| +| **iOS Version** | `PVTF_lADOBluP0c4AxnzVzg7CAVA` | Text | +| **Server Version** | `PVTF_lADOBluP0c4AxnzVzg7CD1Q` | Text | +| **Tile Server Version** | `PVTF_lADOBluP0c4AxnzVzg9t2v4` | Text | +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | SingleSelect | +| **Project ID** | `` | — | + +**Status Option IDs:** +- Done: `782fbd7c` +- Ready for Release: `2f956420` + +--- + +## What NOT to Do + +- **Don't run this before Apple approves** — the Mixpanel annotation timestamp must reflect the actual release date +- **Don't forget to publish the GitHub release** — it stays as a draft until this skill is invoked +- **Don't update issue versions for PRs merged before the previous release** — only PRs merged after the last tag +- **Don't skip server/tile-server issues** — they ship independently but still need iOS Version assignment for timeline tracking +- **Don't guess iOS version windows** — always use the actual release dates from `gh release list`, not approximations +- **Don't assign iOS Versions to server/tile-server issues that aren't Done** — only issues with Status = Done and a Server/Tile Server Version set should get an iOS Version + +--- + +## Prerequisites + +- `/new-release` must have been completed (draft release, tag, and PR exist) +- The app must be **approved and live** in the App Store +- Environment variables `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) +- `gh` CLI must be authenticated with access to `8Gaston8/davidsulitzer.com`, `8Gaston8/davidsulitzer.com`, and `8Gaston8/davidsulitzer.com` repos diff --git a/.codex/skills/respect-figma/SKILL.md b/.codex/skills/respect-figma/SKILL.md new file mode 100644 index 0000000..4b5a616 --- /dev/null +++ b/.codex/skills/respect-figma/SKILL.md @@ -0,0 +1,148 @@ +--- +name: respect-figma +description: Fetch design context, screenshots, variables, and assets from Figma and translate them into production code. Works with both the Figma MCP (local/desktop) and the Figma REST API (cloud/CI agents). Trigger when a task involves Figma URLs, node IDs, or design-to-code implementation. +disable-model-invocation: false +--- + +# Respect Figma + +Fetch design data from Figma and translate it faithfully into production code. +Works in **two modes** depending on where the agent is running — see "Choose your tool" below. + +For setup details (env vars, config, verification), see `references/figma-config.md`. +For a tool catalog and prompt patterns, see `references/figma-tools-and-prompts.md`. + +--- + +## 0 — Choose Your Tool + +| Environment | How to access Figma | +|---|---| +| **Local / desktop** (Cursor, Codex desktop) | Use the **Figma MCP** tools (`get_design_context`, `get_screenshot`, etc.). The MCP server communicates with the running Figma desktop app. | +| **Cloud / CI / headless** (Codex cloud, GitHub Actions) | The MCP is **not available**. Use the **Figma REST API** with an access token from environment secrets. | + +### Figma REST API quick reference + +Base URL: `https://api.figma.com` + +| What you need | Endpoint | Notes | +|---|---|---| +| Design data (node tree, styles, layout) | `GET /v1/files/:fileKey/nodes?ids=:nodeId` | Equivalent to `get_design_context` | +| Rendered image / screenshot | `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` | Equivalent to `get_screenshot` | +| Export SVG asset | `GET /v1/images/:fileKey?ids=:nodeId&format=svg` | For downloading vector assets | +| Variables and styles | `GET /v1/files/:fileKey/variables/local` | Equivalent to `get_variable_defs` | +| File styles | `GET /v1/files/:fileKey/styles` | Color, text, effect, and grid styles | + +**Authentication:** `X-Figma-Token: ` header, or `Authorization: Bearer `. +Use whatever token is available in your environment secrets (`FIGMA_ACCESS_TOKEN`, `FIGMA_OAUTH_TOKEN`, etc.). + +### Extracting fileKey and nodeId from a Figma URL + +``` +https://figma.com/design/:fileKey/:fileName?node-id=:int1-:int2 +``` + +- `fileKey` → the segment after `/design/` +- `nodeId` → replace the `-` in `int1-int2` with `:` → `int1:int2` + +Example: `https://figma.com/design/AbC123/MyFile?node-id=42-99` +→ fileKey = `AbC123`, nodeId = `42:99` + +--- + +## 1 — Required Flow (Do Not Skip) + +Whether you are using the MCP or the REST API, the flow is the same conceptually: + +1. **Get design context** for the exact node(s). + - MCP: `get_design_context(nodeId, fileKey)` + - REST: `GET /v1/files/:fileKey/nodes?ids=:nodeId` + +2. **If the response is too large or truncated**, get the high-level node map first, then re-fetch only the required child node(s). + - MCP: `get_metadata(nodeId, fileKey)` → then `get_design_context` on smaller nodes + - REST: same endpoint with individual child node IDs + +3. **Get a screenshot** for visual reference of the node/variant being implemented. + - MCP: `get_screenshot(nodeId, fileKey)` + - REST: `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` + +4. **Only after you have both** the design context AND the screenshot, download any needed assets and start implementation. + +5. **Translate** the Figma output into the project's conventions, styles, and framework. Reuse existing color tokens, components, and typography. + +6. **Validate** the final UI against the Figma screenshot for 1:1 visual parity before marking complete. + +> **Always start from the Figma source of truth.** Do not guess icons from code comments, asset names, or SF Symbol approximations. + +--- + +## 2 — Implementation Rules + +- Treat the Figma MCP output (typically React + Tailwind) as a **representation of design and behavior**, not as final code style. +- Replace Tailwind utility classes with the project's preferred utilities / design-system tokens when applicable. +- **Reuse existing components** (buttons, inputs, typography, icon wrappers) instead of duplicating functionality. +- Use the project's **color system, typography scale, and spacing tokens** consistently. +- Respect existing **routing, state management, and data-fetch patterns** already adopted in the repo. +- Strive for **1:1 visual parity** with the Figma design. When conflicts arise, prefer design-system tokens and adjust spacing or sizes minimally to match visuals. +- Validate the final UI against the Figma screenshot for both look and behavior. + +--- + +## 3 — Asset Handling + +### General rules + +- The Figma MCP provides an assets endpoint which can serve image and SVG assets. +- **If the MCP returns a localhost source** for an image or SVG, use that source directly. Do NOT create placeholders. +- **Do NOT import/add new icon packages** — all assets should come from the Figma payload. +- When using the REST API, export assets via `GET /v1/images/:fileKey?ids=:nodeId&format=svg` (or `png`/`pdf`). + +### iOS-specific asset workflow + +When extracting icons or images for an iOS / Xcode project: + +1. Call `get_design_context` (or REST equivalent) on the parent view or the specific node. +2. Identify the asset by its `data-name` attribute and corresponding download URL. +3. Download the SVG from the asset URL. +4. Convert to PDF for the Xcode asset catalog: + ```bash + rsvg-convert -f pdf -o icon.pdf icon.svg + ``` +5. Add the PDF to the appropriate `.imageset` directory with a `Contents.json`: + ```json + { + "images": [{ "filename": "icon.pdf", "idiom": "universal" }], + "info": { "author": "xcode", "version": 1 }, + "properties": { "preserves-vector-representation": true } + } + ``` +6. Register in SwiftGen's `Assets.swift` (or the project's asset registry). + +> **Never substitute SF Symbols or existing assets by name alone.** Always visually verify against the Figma design using `get_screenshot` or the REST images endpoint. + +--- + +## 4 — Link-Based Prompting + +- The MCP server is link-based: copy the Figma frame/layer URL and give it to the agent. +- The agent extracts the node ID from the link — it does **not** browse the page. +- Always ensure the link points to the **exact node or variant** you want implemented. +- When given a broad URL (e.g., a full screen), `get_design_context` returns the complete subtree including every nested icon, image, and component with downloadable URLs. Drill into specific `data-node-id` values for individual assets. + +--- + +## 5 — Do NOT + +- Do **not** skip `get_design_context` and jump straight to coding from a screenshot alone. +- Do **not** rely on `get_metadata` for finding assets — it only returns the top-level frame without children. +- Do **not** guess colors, spacing, or font sizes. Extract them from the design context or variables. +- Do **not** import new icon libraries (FontAwesome, SF Symbols, etc.) when the Figma payload already provides the asset. +- Do **not** create placeholder images when a real asset URL is available. +- Do **not** hardcode pixel values when the project has spacing/sizing tokens that match. + +--- + +## References + +- `references/figma-config.md` — MCP setup, REST API auth, environment variables, verification, troubleshooting. +- `references/figma-tools-and-prompts.md` — Full tool catalog and prompt patterns for selecting frameworks/components and fetching metadata. diff --git a/.codex/skills/update-skill/SKILL.md b/.codex/skills/update-skill/SKILL.md new file mode 100644 index 0000000..a5451a1 --- /dev/null +++ b/.codex/skills/update-skill/SKILL.md @@ -0,0 +1,588 @@ +--- +name: update-skill +description: Create or update Cursor/Codex skills across all repos — keeps everything in sync +disable-model-invocation: true +--- +# update-skill + +Create a new skill or update an existing one **across every repo where it exists**. Handles the full workflow: discovering where the skill lives, keeping `.cursor/skills/` and `.codex/skills/` in sync within each repo, keeping all repos in sync with each other, branching, creating issues, opening PRs, and linking everything together. + +--- + +## Step 0: Understand the Request + +Determine from the user's message: + +1. **Which skill?** — The skill name (e.g., `deploy-backend-dev`, `new-issue`). If the user wants to create a brand-new skill, they'll provide a name and description. +2. **What changes?** — What to add, remove, modify, or rewrite. +3. **Create or update?** — If the skill doesn't exist anywhere yet, this is a creation. If it does, it's an update. + +> If the user's request is vague, **ask for clarification** before proceeding. Don't guess what a skill should do. + +--- + +## Step 1: Discover Where the Skill Exists (Cross-Repo Scan) + +**This is the most critical step.** A skill can exist in ANY combination of these repos: + +| Repo | GitHub Repo | Skill Locations | +|------|-------------|-----------------| +| **Monorepo** | `stepscode/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **iOS** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **Server** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **Tileserver** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | + +### 1a: Check Every Repo + +You MUST check ALL four repos. This skill can run from **any** repo, so don't assume a davidsulitzer.com directory layout. + +**Check the current repo locally:** + +```bash +# Current repo — check local skill directories +echo "=== Current Repo ===" && ls .cursor/skills/ .codex/skills/ 2>/dev/null +``` + +**Check other repos via GitHub API:** + +```bash +# Check each remote repo for skills (works from any repo) +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills" --jq '.[].name' 2>/dev/null || echo " No .cursor/skills/" + gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No .codex/skills/" +done +``` + +> **Note:** If you're in the davidsulitzer.com and submodules are checked out, you can also use local paths (`davidsulitzer.com/`, `davidsulitzer.com/`, `davidsulitzer.com/`) — but the `gh api` approach works universally from any repo. + +### 1b: Search for the Specific Skill + +```bash +# Check if the skill exists in each repo via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .cursor" + gh api "repos/stepscode/$repo/contents/.codex/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .codex" +done +``` + +### 1c: Record What You Found + +Build a discovery table: + +| Repo | .cursor/skills | .codex/skills | Action Needed | +|------|---------------|---------------|---------------| +| Monorepo | ✅/❌ | ✅/❌ | Create/Update | +| iOS | ✅/❌ | ✅/❌ | Create/Update | +| Server | ✅/❌ | ✅/❌ | Create/Update | +| Tileserver | ✅/❌ | ✅/❌ | Create/Update | + +### 1d: Decide Which Repos Need the Skill + +- **If updating:** Update the skill in ALL repos where it currently exists. If the user wants to add it to additional repos, do that too. +- **If creating:** Ask the user which repos should have the skill, unless the intent is obvious (e.g., a deployment skill only makes sense in the repos that deploy). +- **If the user says "all repos":** Add it to all four repos. + +> ⚠️ **NEVER update a skill in only one repo when it exists in multiple.** The whole point is cross-repo consistency. + +--- + +## Step 2: Read & Compare Existing Content + +If the skill already exists, read it from all locations and check for drift: + +```bash +# Compare across repos (if the skill exists in multiple) +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +``` + +Also verify `.cursor/` and `.codex/` are in sync WITHIN each repo: + +```bash +diff /.cursor/skills//SKILL.md /.codex/skills//SKILL.md +``` + +If drift exists, alert the user and ask which version should be the base — then unify everything from there. + +--- + +## Step 3: Write the Skill Content + +### Skill File Format + +Every `SKILL.md` must follow this structure: + +```markdown +--- +name: +description: +disable-model-invocation: true +--- +# + +[Skill content — instructions, steps, templates, references, etc.] +``` + +### Frontmatter Rules + +| Field | Required | Notes | +|-------|----------|-------| +| `name` | Yes | Must match the directory name exactly | +| `description` | Yes | Short, descriptive — shown in skill picker | +| `disable-model-invocation` | Yes | Always set to `true` | + +### Content Guidelines + +- **Be specific and actionable** — Skills are instructions for AI agents. Vague guidance produces vague results. +- **Include exact commands** — bash commands, API calls, GraphQL queries. Don't make the agent guess. +- **Add context for "why"** — Agents follow rules better when they understand the reasoning. +- **Use step-by-step structure** — Numbered steps with clear progression. +- **Include templates** — For repetitive outputs (PR comments, issue bodies, messages), provide fill-in-the-blank templates. +- **Add guardrails** — What to do, but also what NOT to do. Explicit "don't" instructions prevent common mistakes. +- **Reference other skills** — If a skill depends on another (e.g., `qa-start` references `deploy-backend-dev`), link to it with `/skill-name` notation. +- **Include verification steps** — After key actions, add a "verify this worked" step. Agents make mistakes; verification catches them. +- **Separate required vs. optional sections** — Use collapsible `
` blocks for conditional/optional content. +- **Keep it DRY** — If the same info appears in multiple skills, consider whether it belongs in a shared reference or a dedicated skill. + +### Quality Checklist (Before Saving) + +- [ ] Frontmatter `name` matches directory name +- [ ] Description is clear and concise +- [ ] Steps are numbered and ordered logically +- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) +- [ ] Includes "what NOT to do" guardrails +- [ ] Verification/confirmation steps after critical actions +- [ ] References to other skills use `/skill-name` format +- [ ] No hardcoded secrets or tokens (use env vars like `$VARIABLE_NAME`) +- [ ] Markdown renders correctly (check headers, tables, code blocks) + +--- + +## Step 4: Apply Changes to ALL Repos + +For **each repo** that needs the skill: + +### 4a: Create a Feature Branch + +```bash +cd +git fetch origin main +git checkout -b cursor/ origin/main +``` + +**Branch naming convention:** `cursor/update-skill-` or `cursor/add-skill-` + +> ⚠️ **Never edit directly on `develop` or `main`.** +> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. + +### 4b: Write to BOTH Directories Within Each Repo + +```bash +# Create directories if they don't exist +mkdir -p .cursor/skills/ +mkdir -p .codex/skills/ + +# Write the skill file to both locations (content must be IDENTICAL) +# ... write SKILL.md to .cursor/skills//SKILL.md +# ... write SKILL.md to .codex/skills//SKILL.md +``` + +### 4c: Update .gitignore (if needed) + +Some repos may have `.cursor` in their `.gitignore`. If so, add an exception for skills: + +```gitignore +# IDE (ignore all except skills/) +.cursor/* +!.cursor/skills/ +``` + +Same for `.codex` if applicable. + +### 4d: Verify Sync Within the Repo + +```bash +diff .cursor/skills//SKILL.md .codex/skills//SKILL.md +``` + +If `diff` produces any output, something went wrong — fix it before committing. + +### 4e: Commit and Push + +```bash +# Stage skill files (always) +git add .cursor/skills//SKILL.md .codex/skills//SKILL.md + +# Stage .gitignore too if it was modified in step 4c +git diff --name-only .gitignore 2>/dev/null | grep -q . && git add .gitignore + +git commit -m "skill(): " +git push -u origin +``` + +### Commit Message Conventions + +| Action | Message Format | +|--------|---------------| +| New skill | `skill(): add new skill — ` | +| Update skill | `skill(): ` | +| Fix skill | `skill(): fix ` | +| Multiple skills | `skills: ` | + +--- + +## Step 5: Create GitHub Issues (for Non-Monorepo Repos) + +**For each repo EXCEPT the davidsulitzer.com**, create a GitHub issue using the `/new-issue` skill patterns. + +> ⚠️ **Monorepo exception:** The davidsulitzer.com does NOT need a GitHub issue — only the PR is sufficient. + +### 5a: Create Issues + +Use the `gh` CLI to create issues in the relevant repos: + +```bash +gh issue create --repo stepscode/ \ + --title "skill(): " \ + --body "" \ + --assignee 8Gaston8 \ + --label "fastlane" +``` + +### 5b: Issue Body Template + +```markdown +## Goal + +[Create / Update] the `` skill [in this repo / across all repos]. + +## Context + +[Brief description of what the skill does and why it's being created/updated.] + +## Changes + +- [List of specific changes being made] + +## Cross-Repo Sync + +This skill [is being created / has been updated] across all repos where it exists: +- [ ] `stepscode/davidsulitzer.com` — PR #XXX +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX + +All instances must remain identical. + +## Out of Scope + +- Content changes to other skills +- Skill deletion + +## Acceptance Criteria + +- [ ] Skill file exists in `.cursor/skills//SKILL.md` +- [ ] Skill file exists in `.codex/skills//SKILL.md` +- [ ] Both copies are identical (`diff` produces no output) +- [ ] Content matches all other repos +``` + +### 5c: Set Issue Type + +Set the issue type to **Task**: + +```bash +# Get issue node ID +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' + +# Set Issue Type to Task +gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "IT_kwDOBluP0c4BBYRC" }) { issue { id } } }' +``` + +### 5d: Add to davidsulitzer.com Project with "In Review" Status + +```bash +# Add to davidsulitzer.com project +gh project item-add 2 --owner 8Gaston8 --url + +# Get project item ID +gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' + +# Set Status to "In Review" +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "2895eb73" } }) { projectV2Item { id } } }' + +# Set Priority to Low (skill updates are low-risk housekeeping) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "9d5cb3c5" } }) { projectV2Item { id } } }' +``` + +### 5e: Verify Issue Configuration + +```bash +# Verify issue type +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' +``` + +--- + +## Step 6: Create Pull Requests + +Create a PR for **every repo** that was changed: + +### 6a: Create the PR + +```bash +cd +gh pr create \ + --repo stepscode/ \ + --base develop \ + --head \ + --title "skill(): " \ + --body "" \ + --assignee 8Gaston8 +``` + +### 6b: PR Body Template + +```markdown +## Summary + +[Create / Update] the `` skill. + +## Changes + +- [List of changes] + +## Cross-Repo Sync + +This change is part of a cross-repo skill sync: +- `stepscode/davidsulitzer.com` — PR #XXX +- `8Gaston8/davidsulitzer.com` — PR #XXX + +All skill files are identical across repos. + +Closes # +``` + +> **Note:** Include `Closes #` in the PR body to auto-link and auto-close the issue when merged. For davidsulitzer.com PRs, omit this since there's no associated issue. + +### 6c: Mark PR as Ready for Review + +PRs created with `gh pr create` are ready for review by default (not draft). Verify: + +```bash +gh pr view --repo stepscode/ --json isDraft +``` + +If it's a draft, convert it: + +```bash +gh pr ready --repo stepscode/ +``` + +### 6d: Assign & Request Review (MANDATORY) + +Every PR must be assigned to Gaston and have Aviad as a reviewer. Use the GitHub GraphQL API: + +```bash +# 1. Get user node IDs (only needed once — cache these) +gh api graphql -f query='{ user(login: "8Gaston8") { id } }' +# → Use this ID for assignee +gh api graphql -f query='{ user(login: "aviadsteps") { id } }' +# → Use this ID for reviewer + +# 2. Assign Gaston to the PR +gh api graphql -f query='mutation { addAssigneesToAssignable(input: { assignableId: "", assigneeIds: [""] }) { assignable { ... on PullRequest { number } } } }' + +# 3. Request review from Aviad +gh api graphql -f query='mutation { requestReviews(input: { pullRequestId: "", userIds: [""] }) { pullRequest { number } } }' +``` + +> **To get the PR node ID:** +> ```bash +> gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' +> ``` + +> ⚠️ **Do this for EVERY PR created** — davidsulitzer.com, iOS, server, and tileserver. No exceptions. + +### 6e: Enable Auto-Merge (iOS Only) + +If the PR is in the **davidsulitzer.com** repo, enable auto-merge so it merges automatically once CI passes and the review is approved: + +```bash +gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash +``` + +> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on davidsulitzer.com, server, or tileserver PRs. + +### 6f: Link Issues to PRs + +For non-davidsulitzer.com repos, the `Closes #` in the PR body handles the link. Additionally, verify the connection: + +```bash +gh pr view --repo stepscode/ --json closingIssuesReferences +``` + +--- + +## Step 7: Final Verification + +### 7a: Content Sync Across All Repos + +Verify the skill content is identical across every repo where it was written: + +```bash +# Compare all instances (adjust paths based on which repos have the skill) +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +# etc. +``` + +### 7b: Issue & PR Checklist + +For each non-davidsulitzer.com repo, verify: + +- [ ] GitHub issue exists with: + - [ ] Issue Type: Task (verified via GraphQL) + - [ ] Label: `fastlane` + - [ ] Assignee: @8Gaston8 + - [ ] Added to davidsulitzer.com project + - [ ] Status: "In Review" +- [ ] PR exists and is: + - [ ] Ready for review (not draft) + - [ ] Linked to the issue (`Closes #XXX`) + - [ ] Assignee: @8Gaston8 + +For the davidsulitzer.com: +- [ ] PR exists and is ready for review (no issue needed) + +### 7c: Share All Links with the User (MANDATORY) + +**You MUST always end by sharing every relevant link** so the user can review everything at a glance. Present the results as a clear summary with **full clickable URLs** — not just PR/issue numbers: + +```markdown +## Here are all the links: + +### Pull Requests +- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX +- **Server:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX +- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX + +### Issues (linked to PRs above) +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- **Server:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- *(Monorepo: no issue needed)* + +### Summary Table +| Repo | PR | Issue | Status | Skill Synced | +|------|-----|-------|--------|-------------| +| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | +| davidsulitzer.com | [#XXX](url) | [#XXX](url) (In Review, fastlane) | Ready for Review | ✅ | +``` + +> ⚠️ **Do NOT skip this step.** The user needs all links in one place to review and merge. Always use full URLs, not just `#123` references. + +--- + +## Handling Special Cases + +### Renaming a Skill + +1. Create the new skill directory in both `.cursor/` and `.codex/` locations across ALL repos +2. Copy the content (with updated `name` field in frontmatter) +3. Delete the old skill directory from both locations across ALL repos +4. Create PRs for every affected repo (same issue/PR workflow as above) + +### Deleting a Skill + +1. Remove from both directories across ALL repos where it exists +2. Create PRs for every affected repo (same issue/PR workflow as above) + +### Auditing All Skills for Sync + +If you suspect drift across repos: + +```bash +# Compare all skills within the current repo +diff -rq .cursor/skills/ .codex/skills/ + +# Compare a specific skill across repos via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.content' 2>/dev/null | base64 -d | md5sum +done +``` + +All checksums should be identical. + +### Skill Only Relevant to Some Repos + +Not every skill belongs everywhere. Use judgment: + +| Skill Type | Typical Repos | +|-----------|---------------| +| Deployment skills | Repo being deployed | +| QA/testing skills | Repo being tested | +| Issue/project skills | All repos (agents work in any) | +| Release skills | iOS repo primarily | +| General workflow skills | All repos | +| Skill management (this skill!) | All repos | + +When in doubt, ask the user. + +--- + +## Reference: Current Skill Inventory + +| Skill | Purpose | Repos | +|-------|---------|-------| +| `deploy-backend-dev` | Deploy backend functions to dev environment | davidsulitzer.com | +| `deploy-backend-prod` | Deploy backend functions to production | davidsulitzer.com | +| `latest-release-notes` | Generate release notes for Slack | davidsulitzer.com | +| `new-issue` | Create GitHub issues with full research and project setup | davidsulitzer.com | +| `new-release` | Prepare iOS releases (branch, PR, regression testing, App Store) | davidsulitzer.com | +| `qa-start` | Comprehensive QA workflow for PRs (iOS + server) | davidsulitzer.com | +| `review-new-bugs` | Triage bug reports from Slack with screenshot analysis | davidsulitzer.com | +| `review-new-crashes` | Review and triage Crashlytics crash reports | davidsulitzer.com | +| `test-backend-changes-dev` | Test backend changes on dev environment | davidsulitzer.com | +| `test-backend-changes-local` | Test backend changes locally | davidsulitzer.com | +| `test-backend-changes-prod` | Test backend changes on production | davidsulitzer.com | +| `update-skill` | This skill — create or update skills across repos | all repos | +| `user-interview` | Simulate data-driven user interviews for product insights | davidsulitzer.com | + +> ⚠️ **Keep this table updated** when creating, deleting, or expanding skills to new repos! + +--- + +## Reference: Repo Quick Info + +| Repo | GitHub | Default Branch | Submodule Path | +|------|--------|----------------|----------------| +| Monorepo | `stepscode/davidsulitzer.com` | `develop` | `.` (root) | +| iOS | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | +| Server | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | +| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | + +--- + +## Reference: Project Field IDs + +| Field | Field ID | Key Options | +|-------|----------|-------------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `01f8a8de`, Todo: `54ffad90`, Ready for Dev: `cac3311f`, In Development: `d7c83647`, Human QA: `6a5c111e`, In Review: `2895eb73`, Done: `782fbd7c` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5` | + +--- + +## Notes + +- **Skill names must be kebab-case** — lowercase, words separated by hyphens (e.g., `deploy-backend-dev`, not `deployBackendDev`) +- **One SKILL.md per skill** — each skill lives in its own directory with a single `SKILL.md` file +- **Skills are version-controlled** — every change goes through git with proper branching, issues, and PRs +- **Both `.cursor/` and `.codex/` must always be in sync** within each repo +- **All repos must be in sync** with each other for shared skills +- **Monorepo gets PRs but NO issues** — only the subrepos (iOS, server, tileserver) get issues +- **Issues get "In Review" status and "fastlane" label** — skill updates are low-friction, ship fast +- **PRs are always Ready for Review** — never left as draft +- **Test after creating** — always suggest the user tests the skill with a real invocation diff --git a/.codex/skills/user-interview/SKILL.md b/.codex/skills/user-interview/SKILL.md new file mode 100644 index 0000000..86c3f6a --- /dev/null +++ b/.codex/skills/user-interview/SKILL.md @@ -0,0 +1,628 @@ +--- +name: user-interview +description: Simulate data-driven user interviews for product insights, feature feedback, or design testing +disable-model-invocation: true +--- +# user-interview + +Simulate realistic user interviews grounded in real product data. Adapts to three modes depending on what you need. + +> **Default product scope: davidsulitzer.com.** All interviews, personas, and data queries should focus on the davidsulitzer.com product (local discovery, smart search, curated spots) unless the user explicitly asks about Gluten Free Eats (GFE). If the user mentions GFE, celiac, gluten-free, or dietary safety, switch to GFE-specific personas and data. When in doubt, ask. + +**Usage examples:** +- `@user-interview` — "I want to understand why trial users churn" +- `@user-interview` — "How would users react to a weekly recommendation email?" +- `@user-interview` — "Here's the new onboarding design — simulate a user going through it" + +--- + +## Step 1: Determine the Interview Mode + +Based on what the user asks, pick the right mode: + +| Mode | Trigger | Purpose | +|------|---------|---------| +| **Exploratory** | Open-ended question, no specific feature | Understand user segments, generate hypotheses | +| **Feature Feedback** | Specific feature or product change mentioned | Simulate reactions to a proposed change | +| **Design Testing** | Design/screenshot/mockup provided or referenced | Simulate users experiencing a specific flow | + +> If unclear, ask: "Are you exploring broadly, testing a specific feature idea, or validating a design?" + +--- + +## Step 2: Gather Real Data + +**This is what makes the interviews valuable.** Before simulating ANYTHING, pull real data from every available source. + +> ### ⚠️ Cloud Agent? Use APIs Instead of MCP +> +> If you are running as a **cloud agent** (headless environment, no MCP tools available), you **MUST** use REST APIs with credentials from environment variable secrets instead of MCP tools. All required API keys are available via **Cursor Dashboard → Cloud Agents → Secrets** and are injected as environment variables. +> +> | Data Source | MCP Tool (Local) | Cloud Agent Alternative | Secret / Env Var | +> |-------------|-------------------|------------------------|------------------| +> | **Mixpanel** | `run_segmentation_query` etc. | [Mixpanel Query API](https://developer.mixpanel.com/reference/query-api) | `MIXPANEL_API_SECRET` | +> | **MongoDB** | Atlas MCP tools | [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/app-services/data-api/) | `MONGODB_ATLAS_API_KEY`, `MONGODB_APP_ID` | +> | **Crisp** | Shell (API) | [Crisp REST API](https://docs.crisp.chat/references/rest-api/v1/) | `CRISP_API_ID`, `CRISP_API_KEY` | +> | **App Store** | Shell (JWT) | [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi) | `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_PRIVATE_KEY` | +> | **Slack** | Slack MCP | [Slack Web API](https://api.slack.com/web) | `SLACK_BOT_TOKEN` | +> +> **Important:** The local `.env` file at `davidsulitzer.com/api/.env` is **NOT available** in cloud agent environments. Always use secrets. If a secret is missing, ask the user to add it in the Cursor Dashboard. +> +> Example (Mixpanel segmentation in cloud): +> ```bash +> curl -X POST "https://mixpanel.com/api/2.0/segmentation" \ +> -u "$MIXPANEL_API_SECRET:" \ +> -d '{"event": "app_search_initiated", "from_date": "2026-01-01", "to_date": "2026-02-09"}' +> ``` +> +> Example (MongoDB Atlas Data API in cloud): +> ```bash +> curl -X POST "https://data.mongodb-api.com/app/$MONGODB_APP_ID/endpoint/data/v1/action/find" \ +> -H "api-key: $MONGODB_ATLAS_API_KEY" \ +> -H "Content-Type: application/json" \ +> -d '{"dataSource": "production", "database": "prod", "collection": "userFeedback", "filter": {}, "limit": 50}' +> ``` +> +> **When a data source is inaccessible:** Don't silently skip it. Explicitly note in the report: "Could not access [source] — [reason]. Confidence scores adjusted accordingly." + +### 2a. Mixpanel — Behavioral Data (Project ID: ) + +Pull data relevant to the interview topic. Choose from: + +**Segmentation** — Understand user behavior patterns: +``` +run_segmentation_query with project_id: +``` +Useful queries: +- Event frequency by user cohort (e.g., how often do trial users search?) +- Feature usage distribution (e.g., what % of users save places?) +- Drop-off points in key flows + +**Funnels** — Understand conversion: +``` +run_funnels_query with project_id: +``` +Useful funnels: +- Pricing Screen → Trial Start → Trial Converted +- App Open → Search → Place Profile View → Save +- Trial Start → Session 2 → Session 3 (retention) + +**Retention** — Understand stickiness: +``` +run_retention_query with project_id: +``` +Useful queries: +- Day 1 / Day 3 / Day 7 retention by cohort +- Retention by acquisition source +- Feature-correlated retention (users who saved vs didn't) + +**Frequency** — Understand usage patterns: +``` +run_frequency_query with project_id: +``` +Useful queries: +- Sessions per week distribution +- Search frequency during trial +- Save frequency by subscription status + +**User Properties** — Understand who they are: +``` +get_property_values with project_id: , resource_type: "User" +``` +Key properties to check: +- `$country_code`, `$city` — geography +- `product` — which product they use (filter for davidsulitzer.com by default) +- `abi_onboarding_type` — how they arrived +- `gfe_sensitivity` — GFE sensitivity level (only relevant if interviewing GFE users) + +### 2b. MongoDB Atlas — User & Content Data (Database: prod) + +Pull data relevant to the interview topic: + +**User feedback & reviews:** +``` +Collection: userFeedback — Direct user feedback +Collection: reviews — Place reviews (shows what users value) +Collection: userState — Subscription/engagement state +``` + +**Engagement signals:** +``` +Collection: saveListItems — What users save (investment behavior) +Collection: stepLikes — What users like +Collection: mapfollowers — Community engagement +Collection: viewsCount — Browsing patterns +``` + +**Content quality:** +``` +Collection: steps — Places/locations in the app +Collection: maps — Available maps +Collection: details — Place details (what info we show) +``` + +**Churn signals:** +``` +Collection: userFeedback — Look for cancellation feedback +Collection: userSubscriptionBypassesForPremiumMaps — Trial behavior +``` + +### 2c. App Store Reviews + +Pull recent App Store reviews using the App Store Connect API: + +```bash +# Generate JWT token for App Store Connect +source .env + +# Get recent reviews (requires JWT auth — see App Store Connect API docs) +# Look for: complaints, praise, feature requests, frustrations +``` + +Alternative: Search the web for recent davidsulitzer.com reviews: +``` +Search: "davidsulitzer.com app" review site:apps.apple.com OR site:play.google.com +``` + +### 2d. Support Conversations — Crisp + +Check recent support conversations for common themes: + +```bash +# Crisp API — get recent conversations +# Website ID: 51f0b9f1-c96c-452a-8e15-e437101d7d80 +# Look for: recurring complaints, feature requests, confusion points +``` + +### 2e. Slack — Internal Signals + +Check bug reports and feedback channels: +``` +Channel: #bugs-mobile-apps (CS93RCDSB) — User-reported issues via Julia +``` + +### 2f. Codebase — Product Context + +**Always** read relevant code to understand: +- The actual user flow being discussed (not assumptions!) +- What the user sees, taps, and experiences +- Feature flags and A/B tests currently running +- Paywall/subscription logic +- Onboarding flows and their variations + +Key areas: +- `davidsulitzer.com/Steps/AppFlows/` — All user flows +- `davidsulitzer.com/Steps/AppFlows/Paywall/` — Pricing/trial logic +- `davidsulitzer.com/Steps/AppFlows/Onboarding/` — First-time experience +- `davidsulitzer.com/Steps/AppFlows/Retention/` — Churn/cancellation flow +- `davidsulitzer.com/api/businessLogic/` — Backend logic for features + +### 2g. Competitor Landscape + +**Always** check what alternatives the simulated user has. Personas don't exist in a vacuum — they're comparing davidsulitzer.com to real alternatives. + +Search the web for recent competitor features and reviews: +``` +Search: "Google Maps" OR "Yelp" OR "TripAdvisor" OR "Foursquare" [topic relevant to interview] +Search: "best restaurant discovery app 2026" +``` + +Key competitors to benchmark against: +| Competitor | What They Offer | davidsulitzer.com's Differentiation | +|------------|----------------|----------------------| +| **Google Maps** | Universal coverage, reviews at scale, AI summaries | davidsulitzer.com: curated quality > quantity, community maps, smart vibe-based search | +| **Yelp** | Review volume, photos, reservations | davidsulitzer.com: less noise, no sponsored results, real community curation | +| **TripAdvisor** | Travel-focused, hotel + restaurant combo | davidsulitzer.com: local-first (not tourist-first), daily use not just travel | +| **Instagram/TikTok** | Social discovery, viral food content | davidsulitzer.com: searchable + saveable, not ephemeral scroll | +| **Friends/Word of mouth** | Trusted, personal, free | davidsulitzer.com: scales the "friend who knows a spot" feeling | + +**Use this in interviews:** When a persona says "I could find this elsewhere," push back with specifics. WHAT would they use? What's the actual experience on that alternative? Make the comparison concrete, not vague. + +### 2h. GitHub Projects — Roadmap & Product Context + +Check both GitHub Projects for relevant context on what's planned, in progress, or ideated: + +**davidsulitzer.com Project** (number: 2, ID: ``) — Active development work: +```bash +# List recent items with status +gh project item-list 2 --owner 8Gaston8 --limit 30 +``` +Look for: items In Development, Todo, or Done that relate to the interview topic. + +**davidsulitzer.com Ideation Lab** (number: 4, ID: `PVT_kwDOBluP0c4BOk4F`) — Ideas and experiments: +```bash +# List ideation items +gh project item-list 4 --owner 8Gaston8 --limit 30 +``` +Look for: feature ideas, experiments, and hypotheses that relate to the interview topic. + +Also search GitHub issues directly for relevant context: +```bash +gh search issues "" --repo 8Gaston8/davidsulitzer.com --state all +``` + +--- + +## Step 3: Build Data-Grounded Personas + +Based on the data gathered, build 2-4 personas that represent REAL behavioral segments. + +### Persona Template + +For each persona, specify: + +```markdown +## Persona: "[Name]" +**Segment:** [What real data segment they represent] +**Data basis:** [Specific Mixpanel/MongoDB data that shaped this persona] + +| Attribute | Details | Data Source | +|-----------|---------|-------------| +| Demographics | [Age, location, lifestyle] | Mixpanel geo data | +| Behavior pattern | [How they use the app — frequency, features, timing] | Mixpanel events | +| Subscription status | [Trial, converted, churned, never started] | MongoDB userState | +| Key actions | [Searches, saves, visits, reviews] | Mixpanel + MongoDB | +| Pain points | [From reviews, feedback, support tickets] | App Store + Crisp | +| Motivation | [Why they downloaded, what they hope for] | Onboarding data | +``` + +### Persona Selection by Mode + +| Mode | Personas to Build | +|------|-------------------| +| **Exploratory** | Cover 3-4 segments: a converter, a churner, a power user, and an edge case | +| **Feature Feedback** | 2-3 personas most affected by the proposed feature | +| **Design Testing** | 2 personas: one tech-savvy, one less experienced with apps | + +--- + +## Step 4: Run the Simulated Interviews + +### Interview Structure + +Each interview should follow this format: + +1. **Context setting** — Who they are, how they found the app +2. **Experience narrative** — Walk through their actual journey (grounded in real product flow!) +3. **Key moment** — The critical point where they converted/churned/got stuck +4. **Probing questions** — Deep dive into motivations and frustrations +5. **Forward-looking** — What would change their behavior + +### Mode-Specific Interview Approaches + +#### Exploratory Mode +- Open-ended questions about their experience +- Focus on emotional journey, not just functional +- Probe for unmet needs and surprising behaviors +- Ask: "What would make this indispensable to you?" + +#### Feature Feedback Mode +- Describe the proposed feature mid-interview +- Capture initial reaction (excitement, confusion, indifference) +- Probe for edge cases: "What if [scenario]?" +- Ask: "Would this change how often you use the app?" +- Ask: "Would this have changed your decision to [subscribe/cancel]?" + +#### Design Testing Mode +- Walk the persona through the design step by step +- Capture first impressions at each screen/state +- Note confusion points: "What do you think this button does?" +- Test comprehension: "What would you do next?" +- Identify emotional reactions: excitement, trust, skepticism, frustration + +### Realism Guidelines + +- **Use natural speech** — contractions, hesitation, tangents (real people don't speak in bullet points) +- **Include "I don't know" moments** — real users aren't always articulate about their needs +- **Show contradictions** — people say one thing and do another (e.g., "I love discovering places" but saves 0 places) +- **Ground in ACTUAL product behavior** — reference real screens, real flows, real pricing ($12/month) +- **Vary emotional states** — some users are enthusiastic, some are apathetic, some are frustrated +- **Inject real user quotes** — When you pulled App Store reviews, Crisp tickets, or MongoDB feedback in Step 2, weave those ACTUAL words into the simulated persona's dialogue. Mark them clearly: + + > **Example:** The persona says *"I don't use it often enough to justify paying"* — and you note: `[echoes real churn reason: "dont_use_often_enough" — selected by X% of cancelers]` + + This makes the simulation tangibly grounded. If you found a 1-star review saying "I searched for brunch and got 3 results," have the persona express that frustration in their own words. Tag it: + + > `[mirrors App Store review, 2 stars, Jan 2026: "searched for brunch spots and barely got any results"]` + + **The goal:** Anyone reading the simulation can immediately see which parts are data-backed and which are AI extrapolation. + +--- + +## Step 5: Synthesize Insights + +After running all interviews, produce a structured synthesis: + +### Comparison Table + +```markdown +| Factor | [Persona 1] | [Persona 2] | [Persona 3] | +|--------|-------------|-------------|-------------| +| First impression | ... | ... | ... | +| Key value moment | ... | ... | ... | +| Biggest friction | ... | ... | ... | +| Would they pay? | ... | ... | ... | +| Usage frequency | ... | ... | ... | +``` + +### Hypotheses (with Confidence Scoring) + +Generate 3-5 testable hypotheses based on the interviews. **Every hypothesis MUST have a confidence level** so the user knows what to trust vs. what to validate first: + +| Confidence | Meaning | When to Use | +|------------|---------|-------------| +| 🟢 **High** | Backed by real data (Mixpanel numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | +| 🟡 **Medium** | Supported by qualitative signals (support tickets, a few reviews, codebase patterns) | Some real-world backing but not statistically rigorous | +| 🔴 **Low** | Speculative — AI extrapolation, no direct data found | Plausible reasoning but no evidence yet; validate first | + +```markdown +| # | Hypothesis | Confidence | Supporting Evidence | How to Validate | +|---|-----------|------------|-------------------|-----------------| +| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [Mixpanel query / real interview question / A/B test] | +``` + +> **Rule:** If more than half your hypotheses are 🔴, explicitly call that out: "Most of these insights are speculative — real user interviews should be the priority before acting on them." + +### Impact on Key Metrics + +Connect insights back to Q1 2026 goals: + +**North Star:** $1M ARR for davidsulitzer.com + +| Metric | Current | Insight Impact | Suggested Action | +|--------|---------|---------------|-----------------| +| Pricing → Trial Start (goal: 20-25%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Trial → Converted (goal: 35-40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Activation Rate (goal: 60%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Early Retention (goal: 40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Investment Rate (goal: 10%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | + +--- + +## Step 6: Generate Actionable Outputs + +Based on the mode, produce the right deliverable: + +### Exploratory Mode → Interview Guide +Generate a real-user interview question guide: + +```markdown +## Interview Guide: [Topic] +**Target segment:** [Who to recruit] +**Session length:** 30 min + +### Warm-up (5 min) +1. [Question about their life/context] +2. [Question about how they find places today] + +### Core Questions (20 min) +3. [Probing question derived from simulation insight #1] +4. [Probing question derived from simulation insight #2] +... + +### Wrap-up (5 min) +8. "If you could change one thing about davidsulitzer.com..." +9. "Would you recommend davidsulitzer.com to a friend? Why/why not?" +``` + +### Feature Feedback Mode → Decision Brief +```markdown +## Feature Decision Brief: [Feature Name] + +### Verdict: 🟢 Build / 🟡 Iterate / 🔴 Rethink + +**Summary:** [One paragraph] + +### User Reception +| Persona | Reaction | Risk | +|---------|----------|------| +| ... | ... | ... | + +### Recommendations +1. [If building: specific implementation suggestions] +2. [If iterating: what to change first] +3. [If rethinking: alternative approaches] + +### Questions to Validate with Real Users +1. [Most critical assumption to test] +2. ... +``` + +### Design Testing Mode → Usability Report +```markdown +## Usability Report: [Design Name] + +### Overall Score: X/5 + +### Screen-by-Screen Findings +| Screen | Comprehension | Emotional Reaction | Issues Found | +|--------|--------------|-------------------|--------------| +| ... | ... | ... | ... | + +### Critical Issues (Fix Before Ship) +1. [Issue + which persona hit it + severity] + +### Recommendations +1. [Specific design change] +2. ... +``` + +--- + +## Step 7: Generate HTML Report + +**MANDATORY.** In addition to any markdown output, you MUST generate a self-contained HTML report file that makes the entire interview simulation comfortable to read in a browser. This is the primary deliverable — the thing Gaston will actually read. + +### Requirements +- **Single file, zero dependencies** — all CSS must be inline in a ` + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + +``` + +> **Quality bar:** The HTML report should feel like a premium product research document — the kind of thing you'd send to a CEO and they'd actually read cover to cover. Not a markdown-to-HTML conversion, but a designed reading experience. + +--- + +## Step 8: Offer Follow-ups + +> **Reminder:** Before offering follow-ups, make sure you've committed and pushed the HTML report file if running in a git-enabled environment. + +After presenting results, suggest next steps: + +- **"Want me to simulate another persona?"** — Different segment, different perspective +- **"Want me to draft a GitHub issue for any of these insights?"** — Using `/new-issue` skill +- **"Want me to add an idea to the davidsulitzer.com Ideation Lab?"** — Track hypotheses as ideation items +- **"Want me to pull more specific data on any of these findings?"** — Deeper Mixpanel/MongoDB dive +- **"Should I run this for GFE too?"** — Same framework, but switches to GFE-specific personas, safety concerns, celiac sensitivity levels, and dietary-focused data + +--- + +## Reference: Available Data Sources + +| Source | Tool | What It Provides | +|--------|------|-----------------| +| **Mixpanel** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | +| **MongoDB** | Atlas MCP (database: prod) | Users, reviews, feedback, saves, engagement, subscription state | +| **App Store** | Shell (App Store Connect API) | User reviews, ratings, feature requests | +| **Crisp** | Shell (API) | Support conversations, common complaints | +| **Slack** | Slack MCP | Bug reports (#bugs-mobile-apps), internal discussions | +| **GitHub Projects** | `gh project` CLI | davidsulitzer.com project (dev work), davidsulitzer.com Ideation Lab (ideas/experiments) | +| **GitHub Issues** | `gh search issues` | Feature requests, bugs, past decisions | +| **Codebase** | File tools | Actual product flows, screens, logic | +| **Web** | Web search/fetch | Competitor analysis, market context, davidsulitzer.com public pages | + +--- + +## Reference: Key Metrics (Q1 2026) + +**North Star:** $1M ARR for davidsulitzer.com + +### Top-Down Metrics (Pricing Funnel) +| Metric | Goal | +|--------|------| +| Pricing Screen → Trial Start | 20-25% | +| Trial Start → Trial Converted | 35-40% | + +### Leading Indicators +| Metric | Goal | Definition | +|--------|------|------------| +| Activation Rate | 60% | % of new trial users who view 5+ place profiles from multi-category searches in first 7 days | +| Early Retention | 40% | % of trial users with 3+ sessions in first 3 days | +| Investment Rate | 10% | % of trial users who save at least 1 place in first 7 days | + +--- + +## Reference: davidsulitzer.com Primary Personas + +These are the baseline personas. Data should refine and override these: + +| Persona | Core Need | Key Behavior | +|---------|-----------|-------------| +| **Decision-Fatigued Foodie** | Just tell me where to go | Searches with specific intent, wants fast answers | +| **Urban Explorer** | Discover hidden gems, feel like a local | Browses and saves, wants to impress friends | +| **Vegan User** | Plant-based focus, not afterthought | Cuisine-specific searches, community-oriented | +| **Date Planner** | Impressive, memorable spots | Plans ahead, cares about ambiance/vibe | +| **Frequent Traveler** | Local expertise in unfamiliar cities | Location-based searches, pre-trip planning | +| **Busy Professional** | Fast, reliable answers | Efficiency over exploration, specific intent | + +--- + +## Reference: Churn Reasons (from Retention Flow) + +Real cancellation reasons captured in-app: + +| Reason | Code | Frequency Insight | +|--------|------|-------------------| +| Too expensive | `too_expensive` | Check Mixpanel for distribution | +| Didn't find relevant places | `didnt_find_relevant_places` | Check Mixpanel for distribution | +| Don't use often enough | `dont_use_often_enough` | Check Mixpanel for distribution | +| Not enough places | `not_enough_places` | Check Mixpanel for distribution | +| Safety concerns | `safety_concerns` | Check Mixpanel for distribution | +| Technical issues/bugs | `technical_issues_bugs` | Check Mixpanel for distribution | +| Other (free text) | `other` | Check MongoDB userFeedback | + +--- + +## Notes + +- **Never fabricate data.** If you can't pull real numbers, say "I couldn't access this — here's my best estimate based on [source]." +- **Always read the actual codebase** before describing product flows. Getting the flow wrong undermines the entire simulation. +- **Simulations are hypotheses, not truths.** Always frame outputs as "worth validating" not "definitely true." +- **Real interviews > simulated interviews.** The goal is to sharpen questions and challenge assumptions, not to replace talking to humans. +- **Adapt tone to Gaston** — Keep it casual, witty, and insightful. This is a thinking tool, not a corporate report. diff --git a/.cursor/skills/generate-assets/SKILL.md b/.cursor/skills/generate-assets/SKILL.md new file mode 100644 index 0000000..e282cbd --- /dev/null +++ b/.cursor/skills/generate-assets/SKILL.md @@ -0,0 +1,173 @@ +--- +name: "generate-assets" +description: "Use when the user asks to generate or edit images via the OpenAI Image API (for example: generate image, edit/inpaint/mask, background removal or replacement, transparent background, product shots, concept art, covers, or batch variants); run the bundled CLI (`scripts/image_gen.py`) and require `OPENAI_API_KEY` for live calls." +--- + +# Image Generation Skill + +Generates or edits images for the current project (e.g., website assets, game assets, UI mockups, product mockups, wireframes, logo design, photorealistic images, infographics). Defaults to `gpt-image-1.5` and the OpenAI Image API, and prefers the bundled CLI for deterministic, reproducible runs. + +## When to use +- Generate a new image (concept art, product shot, cover, website hero) +- Edit an existing image (inpainting, masked edits, lighting or weather transformations, background replacement, object removal, compositing, transparent background) +- Batch runs (many prompts, or many variants across prompts) + +## Decision tree (generate vs edit vs batch) +- If the user explicitly asks to modify an image (e.g., "edit/retouch/inpaint/mask/translate/localize/change only X") -> **edit** (providing an input image alone does not imply edit; it may be a style reference) +- Else if the user needs many different prompts/assets -> **generate-batch** +- Else -> **generate** + +## Workflow +1. Decide intent: generate vs edit vs batch (see decision tree above). +2. Collect inputs up front: prompt(s), exact text (verbatim), constraints/avoid list, and any input image(s)/mask(s). For multi-image edits, label each input by index and role; for edits, list invariants explicitly. +3. If batch: write a temporary JSONL under tmp/ (one job per line), run once, then delete the JSONL. +4. Augment prompt into a short labeled spec (structure + constraints) without inventing new creative requirements. +5. Run the bundled CLI (`scripts/image_gen.py`) with sensible defaults (see references/cli.md). +6. For complex edits/generations, inspect outputs (open/view images) and validate: subject, style, composition, text accuracy, and invariants/avoid items. +7. Iterate: make a single targeted change (prompt or mask), re-run, re-check. +8. Save/return final outputs and note the final prompt + flags used. + +## Temp and output conventions +- Use `tmp/imagegen/` for intermediate files (for example JSONL batches); delete when done. +- Write final artifacts under `output/imagegen/` when working in this repo. +- Use `--out` or `--out-dir` to control output paths; keep filenames stable and descriptive. + +## Dependencies (install if missing) +Prefer `uv` for dependency management. + +Python packages: +``` +uv pip install openai pillow +``` +If `uv` is unavailable: +``` +python3 -m pip install openai pillow +``` + +## Environment +- `OPENAI_API_KEY` must be set for live API calls. + +If the key is missing, give the user these steps: +1. Create an API key in the OpenAI platform UI: https://platform.openai.com/api-keys +2. Set `OPENAI_API_KEY` as an environment variable in their system. +3. Offer to guide them through setting the environment variable for their OS/shell if needed. +- Never ask the user to paste the full key in chat. Ask them to set it locally and confirm when ready. + +If installation isn't possible in this environment, tell the user which dependency is missing and how to install it locally. + +## Defaults & rules +- Use `gpt-image-1.5` unless the user explicitly asks for `gpt-image-1-mini` or explicitly prefers a cheaper/faster model. +- Assume the user wants a new image unless they explicitly ask for an edit. +- Require `OPENAI_API_KEY` before any live API call. +- Use the OpenAI Python SDK (`openai` package) for all API calls; do not use raw HTTP. +- If the user requests edits, use `client.images.edit(...)` and include input images (and mask if provided). +- Prefer the bundled CLI (`scripts/image_gen.py`) over writing new one-off scripts. +- Never modify `scripts/image_gen.py`. If something is missing, ask the user before doing anything else. +- If the result isn’t clearly relevant or doesn’t satisfy constraints, iterate with small targeted prompt changes; only ask a question if a missing detail blocks success. + +## Prompt augmentation +Reformat user prompts into a structured, production-oriented spec. Only make implicit details explicit; do not invent new requirements. + +## Use-case taxonomy (exact slugs) +Classify each request into one of these buckets and keep the slug consistent across prompts and references. + +Generate: +- photorealistic-natural — candid/editorial lifestyle scenes with real texture and natural lighting. +- product-mockup — product/packaging shots, catalog imagery, merch concepts. +- ui-mockup — app/web interface mockups that look shippable. +- infographic-diagram — diagrams/infographics with structured layout and text. +- logo-brand — logo/mark exploration, vector-friendly. +- illustration-story — comics, children’s book art, narrative scenes. +- stylized-concept — style-driven concept art, 3D/stylized renders. +- historical-scene — period-accurate/world-knowledge scenes. + +Edit: +- text-localization — translate/replace in-image text, preserve layout. +- identity-preserve — try-on, person-in-scene; lock face/body/pose. +- precise-object-edit — remove/replace a specific element (incl. interior swaps). +- lighting-weather — time-of-day/season/atmosphere changes only. +- background-extraction — transparent background / clean cutout. +- style-transfer — apply reference style while changing subject/scene. +- compositing — multi-image insert/merge with matched lighting/perspective. +- sketch-to-render — drawing/line art to photoreal render. + +Quick clarification (augmentation vs invention): +- If the user says “a hero image for a landing page”, you may add *layout/composition constraints* that are implied by that use (e.g., “generous negative space on the right for headline text”). +- Do not introduce new creative elements the user didn’t ask for (e.g., adding a mascot, changing the subject, inventing brand names/logos). + +Template (include only relevant lines): +``` +Use case: +Asset type: +Primary request: +Scene/background: +Subject:
+Style/medium: +Composition/framing: +Lighting/mood: +Color palette: +Materials/textures: +Quality: +Input fidelity (edits): +Text (verbatim): "" +Constraints: +Avoid: +``` + +Augmentation rules: +- Keep it short; add only details the user already implied or provided elsewhere. +- Always classify the request into a taxonomy slug above and tailor constraints/composition/quality to that bucket. Use the slug to find the matching example in `references/sample-prompts.md`. +- If the user gives a broad request (e.g., "Generate images for this website"), use judgment to propose tasteful, context-appropriate assets and map each to a taxonomy slug. +- For edits, explicitly list invariants ("change only X; keep Y unchanged"). +- If any critical detail is missing and blocks success, ask a question; otherwise proceed. + +## Examples + +### Generation example (hero image) +``` +Use case: stylized-concept +Asset type: landing page hero +Primary request: a minimal hero image of a ceramic coffee mug +Style/medium: clean product photography +Composition/framing: centered product, generous negative space on the right +Lighting/mood: soft studio lighting +Constraints: no logos, no text, no watermark +``` + +### Edit example (invariants) +``` +Use case: precise-object-edit +Asset type: product photo background replacement +Primary request: replace the background with a warm sunset gradient +Constraints: change only the background; keep the product and its edges unchanged; no text; no watermark +``` + +## Prompting best practices (short list) +- Structure prompt as scene -> subject -> details -> constraints. +- Include intended use (ad, UI mock, infographic) to set the mode and polish level. +- Use camera/composition language for photorealism. +- Quote exact text and specify typography + placement. +- For tricky words, spell them letter-by-letter and require verbatim rendering. +- For multi-image inputs, reference images by index and describe how to combine them. +- For edits, repeat invariants every iteration to reduce drift. +- Iterate with single-change follow-ups. +- For latency-sensitive runs, start with quality=low; use quality=high for text-heavy or detail-critical outputs. +- For strict edits (identity/layout lock), consider input_fidelity=high. +- If results feel “tacky”, add a brief “Avoid:” line (stock-photo vibe; cheesy lens flare; oversaturated neon; harsh bloom; oversharpening; clutter) and specify restraint (“editorial”, “premium”, “subtle”). + +More principles: `references/prompting.md`. Copy/paste specs: `references/sample-prompts.md`. + +## Guidance by asset type +Asset-type templates (website assets, game assets, wireframes, logo) are consolidated in `references/sample-prompts.md`. + +## CLI + environment notes +- CLI commands + examples: `references/cli.md` +- API parameter quick reference: `references/image-api.md` +- If network approvals / sandbox settings are getting in the way: `references/codex-network.md` + +## Reference map +- **`references/cli.md`**: how to *run* image generation/edits/batches via `scripts/image_gen.py` (commands, flags, recipes). +- **`references/image-api.md`**: what knobs exist at the API level (parameters, sizes, quality, background, edit-only fields). +- **`references/prompting.md`**: prompting principles (structure, constraints/invariants, iteration patterns). +- **`references/sample-prompts.md`**: copy/paste prompt recipes (generate + edit workflows; examples only). +- **`references/codex-network.md`**: environment/sandbox/network-approval troubleshooting. diff --git a/.cursor/skills/idea-generator/SKILL.md b/.cursor/skills/idea-generator/SKILL.md new file mode 100644 index 0000000..6828f07 --- /dev/null +++ b/.cursor/skills/idea-generator/SKILL.md @@ -0,0 +1,494 @@ +--- +name: idea-generator +description: Brainstorm bold, data-informed product feature ideas with competitor research, visual demos, and metric alignment. Use when exploring new feature concepts or looking for creative ways to move key metrics. Don't use for creating issues (/new-issue) or refining existing specs (/refine-spec). +disable-model-invocation: true +--- +# idea-generator + +## When to Use + +- You want creative feature ideas to move a specific metric (activation, retention, conversion, etc.) +- You're exploring "what could we build?" before committing to a spec +- You want competitor-informed brainstorming with visual mockups + +## When NOT to Use + +- You've already decided what to build and need an issue → use `/new-issue` +- You have an existing spec that needs refinement → use `/refine-spec` +- You need user research to validate an idea → use `/user-interview` +- You're doing QA or deployment work → wrong skill entirely + +--- + +You are a **wildly creative** product brainstorm partner for davidsulitzer.com — a places discovery app that helps users find amazing spots (coffee shops, bars, restaurants, etc.) through community curation. + +## Your Mission + +Generate **bold, original, unexpected** feature ideas that move our metrics. Don't play it safe — the best ideas often sound crazy at first. Be inspired by what competitors do well, but don't copy — **innovate**. + +Think like a mix of: a product visionary, a behavioral psychologist, and a user who's slightly obsessed with finding the perfect coffee shop. + +--- + +## Q1 2026 Goals + +### 🎯 North Star +**Define your current north-star metric and date.** + +### 📊 Top-Down Metrics (Funnel Conversion) +| Stage | Goal | +|-------|------| +| **💰 Pricing Screen → Trial Start** | 20-25% | +| **✅ Trial Start → Trial Converted** | 35-40% | + +### 💡 Leading Indicators + +| Metric | Goal | Definition | Why It Matters | +|--------|------|------------|----------------| +| **🔥 Activation Rate** | 60% | % of new users who perform at least ONE of: save, like, search result click, or place profile view in their first session | First session is make-or-break. If they don't engage on day 1, they're ghosts. | +| **🔄 Early Retention** | 40% | % of trial users with 3+ sessions in first 3 days | Habit formation window. Users who return within 3 days are building davidsulitzer.com into their routine. | +| **💾 Investment Rate** | 10% | % of trial users who save at least 1 place in their first 7 days | Saving = "I found value I want to keep." Creates switching costs and signals product-market fit. | + +--- + +## Brainstorm Process + +### Step 1: Pick Your Target +Ask me which metric or screen I want to focus on, or suggest one based on the biggest opportunity gap. + +### Step 2: Understand Current State +Before ideating, briefly explore the codebase to understand: +- What's the current UX for this metric area? +- What screens/flows are involved? +- What analytics events exist? + +Use semantic search and grep to find relevant code in both `davidsulitzer.com` and `davidsulitzer.com`. + +### Step 3: Research Competitors +Before generating ideas, use the **WebSearch tool** to research what competitors are doing well in this space. Don't rely on outdated knowledge — find recent features and updates! + +**Search queries to run:** +- `"[competitor] new features [current year]"` — e.g., "Google Maps new features 2026" +- `"[competitor] [metric area] improvements"` — e.g., "Yelp user engagement improvements" +- `"best [category] app features [current year]"` — e.g., "best restaurant discovery app features 2026" + +**Competitors to research:** +- **Maps & Discovery:** Google Maps, Yelp, Foursquare, TripAdvisor +- **Food & Booking:** Uber Eats, DoorDash, OpenTable +- **Travel:** Airbnb, Booking.com +- **Engagement patterns:** Instagram, TikTok, BeReal, Duolingo + +Note what's working for them and why — then think about how davidsulitzer.com can do it **better or differently**. + +### Step 4: Review User Interview Notes + +Before ideating, check the **User Interview Notes** Slack channel for real user feedback, pain points, and feature requests. This is gold — actual users telling us what they want! + +**Slack Channel:** `#user-interview-notes` (ID: `C09PTLMHKLP`) + +Use the Slack MCP to search for relevant insights: +``` +Search the Slack channel C09PTLMHKLP for messages related to [TOPIC/METRIC AREA] +``` + +**What to look for:** +- Recurring pain points or frustrations +- Feature requests that align with the target metric +- Emotional language (excitement, frustration, confusion) +- Workarounds users have invented (signals unmet needs!) +- Quotes that could inform your hypothesis + +**Pro tip:** Real user words make the best hypothesis justifications. Quote them in your ideas! + +### Step 5: Mine Crisp Support Conversations + +**Real users complaining = real product insights.** Before ideating, pull recent support conversations from Crisp to find recurring pain points, feature requests, and confusion signals. + +> #### Cloud Agent? Use Environment Secrets +> +> If running as a **cloud agent**, use `CRISP_IDENTIFIER`, `CRISP_KEY`, and `CRISP_WEBSITE_ID` from environment secrets instead of sourcing `.env`. + +#### 5a. Load Credentials + +```bash +source .env +``` + +#### 5b. Search Conversations by Topic + +Search for conversations related to the metric area or feature you're brainstorming on: + +```bash +# Search conversations matching a keyword (returns up to 20 per page) +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?search_query=KEYWORD&search_type=text" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +**Search tips:** +- Search for the feature area: `search`, `save`, `map`, `subscription`, `trial`, `cancel`, `bug`, `crash`, `payment` +- Search for emotional signals: `frustrated`, `broken`, `love`, `wish`, `want`, `missing`, `need` +- Search for competitor mentions: `google maps`, `yelp`, `tripadvisor` +- Page through results by changing the page number in the URL (`.../conversations/2`, `.../conversations/3`, etc.) + +#### 5c. Read Messages from Interesting Conversations + +When you find a conversation that looks relevant, pull the full message thread: + +```bash +# Get all messages in a specific conversation +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversation/SESSION_ID/messages" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +Replace `SESSION_ID` with the `session_id` from the conversation list (e.g., `session_fa5663c8-6f5c-473c-b4e9-44c3db8ef2e0`). + +#### 5d. Browse Recent Conversations (No Search) + +Sometimes the best insights come from just scanning what people are writing about lately: + +```bash +# List most recent conversations (page 1, most recent first) +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +Each conversation includes a `topic`, `last_message`, `meta.nickname`, `meta.email`, and `meta.device.geolocation` — useful for building persona context. + +#### 5e. Filter by Date Range + +Focus on recent feedback (e.g., last 30 days): + +```bash +# Conversations updated in the last 30 days +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_date_start=$(date -u -v-30d '+%Y-%m-%dT00:00:00.000Z')&filter_date_end=$(date -u '+%Y-%m-%dT23:59:59.999Z')" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +#### 5f. Filter by Conversation State + +Focus on unresolved issues (things users are STILL frustrated about): + +```bash +# Only unresolved conversations +curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_not_resolved=1" \ + --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ + --header "X-Crisp-Tier: plugin" | python3 -m json.tool +``` + +#### 5g. What to Extract + +From the conversations, look for: + +| Signal | What It Tells You | How to Use It | +|--------|-------------------|---------------| +| **Recurring complaints** | What's broken or frustrating right now | Direct problem to solve | +| **Feature requests** | What users wish existed | Idea fuel — validate with data | +| **Confusion patterns** | Where the UX fails to communicate | UX improvement opportunities | +| **Competitor mentions** | What users compare davidsulitzer.com to | Competitive positioning insights | +| **Emotional language** | How strongly users feel | Prioritization signal (strong = urgent) | +| **Workarounds** | Hacks users invent to get what they want | Unmet needs hiding in plain sight | +| **Churn signals** | "I'm canceling because..." | Retention idea triggers | +| **Praise / delight** | What users genuinely love | Double down on what works | + +**Pro tip:** Quote real user words from Crisp in your ideas! Nothing sells a hypothesis like an actual frustrated human saying exactly the thing your idea would fix. + +### Step 6: Find Related Backlog +Search GitHub for related existing issues: +```bash +gh issue list -R 8Gaston8/davidsulitzer.com --search "KEYWORDS" --limit 10 +``` + +This helps connect ideas to existing work and avoid duplicating efforts. + +### Step 7: Generate Ideas (BE CREATIVE!) + +Generate **5-10 ideas** that are: +- **Original** — not obvious, not what everyone else is doing +- **Bold** — willing to take risks, challenge assumptions +- **Grounded** — tied to user psychology and mobile UX best practices +- **Varied** — across different categories (quick wins to moonshots) + +--- + +## Output Format + +**IMPORTANT:** Present ALL ideas using this detailed format. Each idea should be a complete, self-contained brief. + +--- + +### 💡 Idea #[N]: [Creative, Catchy Name] + +**Category:** ⚡ Quick Win / 🚀 Feature / 🧪 Experiment / 🌙 Moonshot + +> *[One sentence elevator pitch — make it punchy and memorable!]* + +#### The Concept +[2-3 sentences describing what this is and how it works. Be specific enough that someone could understand and visualize it.] + +--- + +#### 🎯 Goals Alignment + +**Primary Target:** +| Goal | Expected Impact | Confidence | +|------|-----------------|------------| +| [🔥 Activation / 🔄 Retention / 💾 Investment / 💰 Pricing→Trial / ✅ Trial→Converted] | [+X-Y%] | [High/Medium/Low] | + +**Also Impacts:** [List secondary goals with emojis] + +**Hypothesis:** +> [2-3 sentences explaining WHY this will work. Be specific about the user psychology, behavioral principle, or proven pattern you're drawing from. This is the most important part — convince me!] + +**Rationale:** +[1-2 sentences on the underlying logic or evidence. Reference specific data, competitor success, or behavioral science if applicable.] + +--- + +#### 🔍 Competitor Inspiration + +| Competitor | What They Do | davidsulitzer.com's Twist | +|------------|--------------|--------------| +| [Company 1] | [Their implementation] | [How we do it better/differently] | +| [Company 2] | [Their implementation] | [How we do it better/differently] | + +**Key Insight:** [What's the learning here? Why does this pattern work?] + +--- + +#### 🔗 Related Backlog + +| Repo | Issue | How It Relates | +|------|-------|----------------| +| 📱 davidsulitzer.com | [#XXXX - Title](link) | [Connection] | +| ⚙️ davidsulitzer.com | [#XXXX - Title](link) | [Connection] | + +*If no related issues exist, note: "No existing issues — this is net new!"* + +--- + +#### 📊 Evaluation + +| Dimension | Rating | Notes | +|-----------|--------|-------| +| **Impact** | 🟢 High / 🟡 Medium / 🔴 Low | [Brief justification] | +| **Effort** | S / M / L / XL | [Key complexity drivers] | +| **Confidence** | 🟢 High / 🟡 Medium / 🔴 Low | [What gives us confidence or uncertainty?] | +| **Dependencies** | [List] | Backend? Design? Data? External? | + +--- + +#### 💭 Open Questions +- [Question 1 that needs answering before implementation] +- [Question 2 about user behavior or technical feasibility] + +--- + +## Creativity Prompts + +When generating ideas, challenge yourself with these questions: + +**Flip the script:** +- What if we did the opposite of what's expected? +- What would a gaming app do here? +- What would feel magical to users? + +**Steal from other domains:** +- How does Duolingo make learning addictive? +- How does Tinder make swiping fun? +- How does Spotify personalize discovery? +- How does BeReal create urgency? + +**Remove friction vs add delight:** +- What's the laziest path to value? +- What would make users smile? +- What would they screenshot and share? + +**Think about emotions:** +- What makes users feel smart/cool/in-the-know? +- What creates FOMO? +- What builds trust? +- What creates "unfinished business" that brings them back? + +--- + +--- + +## Visual Demo (Required!) + +**After generating ideas, create a visual demo page** that renders each idea inside realistic iPhone frames. This is NOT optional — seeing ideas visually makes them 10x more useful. + +### Setup + +Create a temporary React demo in a `demos/` folder: + +```bash +mkdir -p demos/[feature-name] +cd demos/[feature-name] +npm init -y +npm install react react-dom vite @vitejs/plugin-react +``` + +### Phone Frame Structure + +Every design must be rendered inside a phone mockup: + +```jsx +
+
+
+ {/* Or other app context */} + + +
+
+``` + +### Template Contexts + +Choose the right context based on what screen your idea lives on: + +| Context | When to Use | Key Elements | +|---------|-------------|--------------| +| `PlaceProfileContext` | Place details, reviews, actions | Gallery, title, rating, People Are Saying, Good To Know | +| `SearchContext` | Search results, filters | Search header, follow-up chips, map with pins | +| `HomeContext` | Discovery, categories | Search bar, category pills, map background | +| `ListContext` | Place lists, collections | List header, place cards, filters | + +### Phone Frame CSS + +Use **280×560px** for demos (fits more ideas on screen) or **375×812px** for pixel-perfect iPhone fidelity: + +```css +.phone-frame { + width: 280px; /* Or 375px for full size */ + height: 560px; /* Or 812px for full size */ + background: #1a1a1a; + border-radius: 32px; + padding: 10px; + position: relative; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); +} + +.phone-notch { + width: 80px; + height: 24px; + background: #1a1a1a; + border-radius: 0 0 14px 14px; + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + z-index: 10; +} + +.phone-screen { + width: 100%; + height: 100%; + background: #f8f8f8; + border-radius: 24px; + overflow: hidden; + overflow-y: auto; /* Important for scrollable content! */ +} + +/* Scale content to fit smaller frame */ +.phone-screen .template-context { + transform: scale(0.72); + transform-origin: top left; + width: 139%; + height: 139%; +} +``` + +### Color Contrast (Important!) + +| Background | Text Color | Example | +|------------|------------|---------| +| Light (#fff, #f8f8f8) | Dark (#333 or darker) | ✅ Readable | +| Dark (#1a1a1a, #333) | White or light | ✅ Readable | +| Gradients | Test at ALL points | ⚠️ Check edges | +| Light gray | Light text | ❌ NEVER | + +### Page Layout + +Create a unified view with: + +1. **Sticky header** showing North Star + metrics goals +2. **Filter bar** with toggles: + - Category: All | ⚡ Quick Wins | 🚀 Features | 🌙 Moonshots + - Goal: All | 🔥 Activation | 🔄 Retention | 💾 Investment + - Data panels: 🎯 Goals | 🔍 Competitors | 🔗 Backlog (toggle visibility) +3. **Idea cards** showing: + - Phone mockup on the left + - Idea info + data panels on the right (always visible, no expand/collapse!) + +### Data Panels (Compact, Always Visible) + +```jsx +{/* Goals Panel */} +
+
🎯 Goal Alignment
+
{emoji} {goalName}
+
{expectedImpact}
+

{hypothesis}

+
+ +{/* Competitors Panel */} +
+
🔍 Competitors ({count})
+
+ {competitors.map(c => {c.name}: {c.feature})} +
+
+ +{/* Backlog Panel */} +
+
🔗 Backlog ({count})
+
+ {issues.map(i => 📱 #{i.number})} +
+
+``` + +### Run the Demo + +```bash +npm run dev +# Opens at http://localhost:3000 +``` + +**Open the browser automatically** so I can see the ideas visualized! + +### Key Principles + +| Do ✅ | Don't ❌ | +|-------|---------| +| Render inside phone frames | Show standalone components | +| Use real app context (Place Profile, Search, etc.) | Use fake/simplified mockups | +| Show all data panels immediately | Hide behind expand buttons | +| Make it interactive (filters, toggles) | Static walls of text | +| Use realistic placeholder data | Lorem ipsum | + +--- + +## After Brainstorming + +Once we finish brainstorming, I can help with next steps **if you ask**: +- Deep dive on a specific idea +- Draft a mini PRD with success metrics +- Identify key code areas to modify + +**Note:** I will NOT automatically create GitHub issues without your explicit request. Brainstorming is just brainstorming! + +**Cleanup:** The demo folder is temporary — delete it when done unless you want to keep it. + +--- + +## Let's Brainstorm! 🧠✨ + +Which metric or screen do you want to focus on today? Or should I analyze the codebase first to identify where the biggest opportunities might be? + +Remember: **The best ideas often sound a little crazy at first.** Don't hold back! diff --git a/.cursor/skills/new-issue/SKILL.md b/.cursor/skills/new-issue/SKILL.md new file mode 100644 index 0000000..49ebe38 --- /dev/null +++ b/.cursor/skills/new-issue/SKILL.md @@ -0,0 +1,651 @@ +--- +name: new-issue +description: Create well-researched GitHub issues with full project integration (issue type, davidsulitzer.com project, priority, cross-repo blocking). Use when the user wants to create a new issue from a bug report, feature request, or task. Don't use for refining existing issues (/refine-spec) or for creating issues as part of QA (/qa-start handles that internally). +disable-model-invocation: true +--- +# new-issue + +Create GitHub issues based on the mentioned requirements. **Always** perform pre-research (Step 0) before writing anything. + +--- + +## When to Use + +- The user describes a bug, feature request, or task that needs a GitHub issue +- A bug review (`/review-new-bugs`) or crash review (`/review-new-crashes`) identified something worth tracking +- The user explicitly says "create an issue" or "file a ticket" + +## When NOT to Use + +- An issue already exists and needs improvement → use `/refine-spec` +- You're in the middle of QA and found a bug → `/qa-start` handles issue creation internally +- The user is brainstorming ideas → use `/idea-generator` first, create issues only when asked +- You're unsure if an issue is needed → ask the user before creating anything + +--- + +## Step 0: Pre-Research (BEFORE Creating Any Issue) + +Before writing anything, you MUST investigate the codebase and existing issues. This step ensures the issue lands in the correct repo and arrives loaded with useful context for the implementer. + +### 0a: Search for Relevant Code + +Search **all three** repos for keywords related to the problem or feature — function names, screen names, model names, API endpoints, error messages: + +```bash +# Search the current repo first +rg -l "" . + +# If in the davidsulitzer.com with submodules checked out, also search: +rg -l "" davidsulitzer.com/ 2>/dev/null + +# If in a standalone repo checkout, use gh to search other repos: +gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 +``` + +> **Note:** The `rg` commands with `2>/dev/null` gracefully handle missing sibling directories in standalone checkouts. Use `gh search code` as a fallback when submodules aren't available. + +Take note of: +- **Which files are involved** — full paths and key line numbers +- **Which repo(s) contain the relevant code** — this determines where the issue goes (Step 0b) +- **Recent changes** — check git log for the relevant files: + ```bash + git log --oneline -10 -- + ``` + +### 0b: Determine the Correct Repo + +Based on your code search results from 0a: + +| Search Results | Repo Decision | +|---------------|---------------| +| Relevant code **only** in `davidsulitzer.com/` | Create issue in `8Gaston8/davidsulitzer.com` | +| Relevant code in **multiple** repos | Create one issue per repo (see Step 2) | +| **Can't find relevant code** | **ASK the user** — do NOT guess | + +**Heuristic helpers** (use alongside code search, not instead of it): + +| Signal | Likely Repo | +|--------|-------------| +| UI, screens, animations, SwiftUI views, layout | iOS | +| API endpoints, database, migrations, queries, cron jobs | Server | +| Map tiles, vector tiles, pin scoring, pin priority, category pins | Tileserver | +| Location filtering, atly score, tile rendering, map vectors | Tileserver | +| "Data is wrong", "results are incorrect" | Usually Server or Tileserver — verify with code search | +| "Crashes", "freezes", "layout broken", "animation janky" | Usually iOS | +| "Pins wrong", "map shows wrong places", "scoring issue" | Usually Tileserver — verify with code search | +| Model names, feature names | Could be any repo — always search first | + +> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. + +### 0c: Check for Duplicate / Related Issues + +Before creating a new issue, search for existing ones: + +```bash +# Search open issues in all repos +gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --limit 10 + +# Also check recently closed issues (could be a regression of a previous fix) +gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --state closed --limit 5 +``` + +- **Open duplicate found** → Tell the user and link to it instead of creating a new one +- **Closed related issue found** → Reference it in the new issue for context (possible regression?) + +### 0d: Bug-Specific Investigation + +When creating a **bug** issue, do this additional research: + +1. **Find the error source** — If the user mentions an error message, search for where it's thrown: + ```bash + rg "" davidsulitzer.com/ davidsulitzer.com/ davidsulitzer.com/ + ``` + Include the file path, line number, and surrounding context in the issue. + +2. **Check recent commits for regressions** — Look for changes that could have introduced the bug: + ```bash + # Recent commits touching relevant files + git log --oneline -10 -- + + # Recent PRs merged to the repo + gh pr list --repo 8Gaston8/davidsulitzer.com --state merged --limit 10 + ``` + If a relevant file was changed recently, note the PR in the issue as a potential regression source. + +3. **Check test coverage** — Look for existing tests in the affected area: + ```bash + # iOS tests (use davidsulitzer.com paths if available, otherwise search current repo) + rg -l "" davidsulitzer.com/AtlyTests/ davidsulitzer.com/AtlyUITests/ 2>/dev/null || \ + rg -l "" AtlyTests/ AtlyUITests/ 2>/dev/null + + # Server tests (few exist — check services) + rg -l "" davidsulitzer.com/services/ 2>/dev/null || \ + rg -l "" services/ api/test/ 2>/dev/null + + # Tileserver tests + rg -l "" davidsulitzer.com/app/__tests__/ davidsulitzer.com/app/businessLogic/__tests__/ 2>/dev/null || \ + rg -l "" app/__tests__/ app/businessLogic/__tests__/ 2>/dev/null + ``` + Note whether tests exist and whether they cover the reported scenario. + +4. **Identify dependencies** — What other systems does this code touch? + - Feature flags that gate the behavior + - API endpoints involved (grep for route definitions) + - Third-party services (Mixpanel, RevenueCat, Firebase, etc.) + - Caching layers (tileserver, CDN, local cache) + +5. **Gather reproduction context:** + - Which API endpoints are involved + - What user state or data triggers the bug + - Which app version or server deployment may have introduced it (if determinable from git log) + +### 0e: Include Research Findings in Issue + +All findings from Step 0 MUST be woven into the issue body. Add a **Pre-Research Findings** section: + +```markdown +## Pre-Research Findings + +**Relevant code:** +- `:` — [brief description of what this code does] +- `:` — [brief description] + +**Recent changes in this area:** +- PR # () — [what it changed] + +**Related issues:** +- # — [title] (open/closed) + +**Test coverage:** [Existing tests cover X / No existing test coverage for this path] + +**Dependencies:** [Feature flags, APIs, services involved] +``` + +> ⚠️ **Do NOT skip this step.** Issues created without pre-research waste the implementer's time re-discovering context you already had access to. + +--- + +## Step 1: Determine the Right Repo(s) + +By this point, Step 0 should have told you which repo(s) contain the relevant code. Confirm your decision: + +| Scope | Action | +|-------|--------| +| **iOS only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Server only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Tileserver only** | Create issue in `8Gaston8/davidsulitzer.com` | +| **Multiple repos** | Create one issue per repo (see Step 2) | +| **Uncertain** | Go back to Step 0a and search more, or **ask the user** | + +> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. + +--- + +## Step 2: For Cross-Repo Issues + +When an issue requires work in multiple repos (any combination of iOS, Server, Tileserver): + +1. **Create backend/data issues first** — start with `8Gaston8/davidsulitzer.com` and/or `8Gaston8/davidsulitzer.com` + - Focus on backend requirements: API changes, data model, endpoints, tile logic + - Reference the downstream issue(s) that will be created + - Set project status to **Todo** + +2. **Create the iOS issue** in `8Gaston8/davidsulitzer.com` (if applicable) + - Focus on client requirements: UI, models, integration + - Reference the backend issue(s): "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" or "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" + - Include a section documenting the expected API contract from server/tileserver + +3. **Set up blocking relationships** — THIS IS MANDATORY FOR CROSS-REPO ISSUES: + +> The general rule: **iOS is blocked by Server/Tileserver. Server may be blocked by Tileserver.** Create `addBlockedBy` for each dependency. + + **Step 3a: Get the issue node IDs:** + ```bash + # Get issue node ID (replace and for each issue) + gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' + ``` + + **Step 3b: Create the blocked-by relationship (repeat for each dependency):** + ```bash + # Downstream issue is BLOCKED BY upstream issue + # e.g., iOS blocked by Server, iOS blocked by Tileserver, Server blocked by Tileserver + gh api graphql -f query='mutation { addBlockedBy(input: { issueId: "", blockingIssueId: "" }) { clientMutationId } }' + ``` + + **Step 3c: VERIFY the relationship was created (repeat for each downstream issue):** + ```bash + gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { blockedBy(first: 5) { nodes { number title repository { name } } } } } }' + ``` + + > ⚠️ **CRITICAL:** Do NOT use `addSubIssue` — that creates a parent/child hierarchy, NOT a blocking dependency. ONLY use `addBlockedBy`. + > + > ⚠️ **CRITICAL:** Always run the verification query. The mutation can return success even if no relationship was created. You MUST confirm `blockedBy.nodes` contains the server issue. + +--- + +## Step 3: Issue Content Requirements + +For each issue, include: + +### Required Sections +- **Pre-Research Findings** — Output from Step 0 (relevant code, recent changes, related issues, test coverage, dependencies) +- **Goal** — Clear one-liner at the top +- **Context/Background** — Why this matters +- **Requirements** — What needs to be built (platform-specific) +- **Figma Design** — ⚠️ If a Figma URL exists, include it as-is. **Do NOT translate the Figma into text** — the design file IS the spec. See the Figma Design template below. +- **Out of Scope** — Explicitly state what's NOT part of this issue (prevents scope creep, helps AI agents stay focused) +- **Acceptance Criteria** — Checkboxes for definition of done +- **Testing Requirements** — Unit tests, regression tests, manual QA scope (see Step 6) +- **Related Issues/PRs** — Cross-references + +### Conditional Sections (include when applicable) + +| Section | When to Include | +|---------|-----------------| +| **Figma Design** | ⚠️ **REQUIRED** when a Figma URL or design exists — see template below | +| **Design Brief** | ⚠️ **REQUIRED** when issue has `design` label — see template below | +| **Technical Approach** | Features/refactors where you have a sense of the architecture | +| **Investigation Checklist** | Bugs where root cause is unknown | +| **Constraints** | When there are performance, compatibility, or deadline requirements | +| **Feature Flag** | New features or risky changes that should be gated | +| **Reference Implementation** | When similar patterns/PRs exist to follow | +| **Risk/Rollback** | Medium+ risk changes that could cause issues | +| **Analytics Requirements** | When new Mixpanel events are needed | +| **Documentation Updates** | When README, API docs, or agent rules need updating (see Step 7) | + +### Section Templates + +
+Out of Scope (always include) + +```markdown +## Out of Scope +- [What you're explicitly NOT doing] +- [Related work that's a separate issue] +- [Platform exclusions, e.g., "Android — separate issue"] +``` +
+ +
+⚠️ Figma Design (REQUIRED when a Figma design exists) + +When the user provides a Figma URL or the feature has an existing Figma design, you MUST include a Figma Design section. The critical rule here is: + +> **DO NOT translate the Figma design into text.** The Figma file IS the spec. Just link to it. + +Agents and humans can read Figma directly — there's no need to describe layouts, colors, spacing, or component structure in prose. Doing so creates a stale, lossy copy of the design that will drift from the source of truth. + +```markdown +## Figma Design + +> **The Figma design is the source of truth for all visual and interaction details.** +> The implementer MUST use the `/respect-figma` skill to read the design directly from Figma. +> Do NOT rely on text descriptions of the design — open the Figma link below. + +🎨 **Figma:** [paste full Figma URL here] + +**Scope of the design:** +- [Which screens/flows are covered in this Figma link] +- [Any specific frames or variants to focus on] + +**Notes (only if needed):** +- [Anything that's NOT in the Figma but is relevant — e.g., "ignore the old header in the design, we're keeping the current one"] +- [Behavior/logic that isn't captured visually — e.g., "tapping X triggers a confirmation dialog"] +``` + +**What to include in the issue body:** +- The Figma link(s) — full URL with the correct node selected +- Which screens/frames are in scope +- Any behavioral notes that Figma can't express (conditional logic, animations, error states) + +**What NOT to include:** +- ❌ Text descriptions of the layout ("there's a card with rounded corners and a blue header...") +- ❌ Extracted color values, font sizes, or spacing numbers +- ❌ Re-drawn diagrams or ASCII mockups of what Figma already shows +- ❌ Screenshots of the Figma — link to the actual file so the agent can query it programmatically + +The agent picking up this issue will use `/respect-figma` to fetch design context, screenshots, variables, and assets directly from the Figma API. That's far more accurate than any text description. +
+ +
+⚠️ Design Brief (REQUIRED when issue has `design` label) + +When an issue has the `design` label, it means designer Liron needs to create or review designs. You MUST include these three sections: + +```markdown +## Problem +[The deep problem which the feature aims to solve. Be as descriptive as possible about WHY this matters and what pain it causes users.] + +## Design Brief +[Conceptual description of the design elements needed. This briefs Liron to start prototyping. Keep it conceptual — describe WHAT needs to be designed, not HOW to design it.] + +Consider including: +- Key screens/components that need design +- User flows to design +- States to consider (empty, loading, error, success) +- Interactions and animations needed +- Open questions for the designer + +## Design Requirements + +| Title | User Story | Importance | +|-------|------------|------------| +| [Requirement Name] | As a [user], I want [goal] so that [benefit] | Must Have / Should Have / Nice to Have | +``` + +**Importance Levels:** +- **Must Have** — Essential for the feature to work +- **Should Have** — Important but not blocking +- **Nice to Have** — Enhancement if time permits + +**Example:** +```markdown +## Problem +Users in the churn/retention flow often complete cancellation without engaging support. We miss opportunities to understand why they're leaving or offer solutions. + +## Design Brief +Design a visually striking element that draws attention to the Contact Support option in the churn flow. Could be an arrow, illustration, or animation. Should be impossible to miss but not obnoxious — friendly and helpful, not desperate. + +## Design Requirements + +| Title | User Story | Importance | +|-------|------------|------------| +| Attention-Grabbing Element | As a churning user, I want to notice the Contact Support option | Must Have | +| Friendly Tone | As a user, I want the prompt to feel helpful, not guilt-trippy | Must Have | +| Non-Intrusive | As a user who decided to cancel, I don't want to feel blocked | Should Have | +``` + +> ⚠️ **When NOT to use the `design` label:** +> - Bug fixes where the expected behavior is already clear +> - Code/implementation fixes (janky animations, keyboard issues) +> - Technical setup/tooling tasks +> - Backend-only work with no UI impact +
+ +
+Technical Approach (for features/refactors) + +```markdown +## Technical Approach +High-level strategies to consider: +- [Approach 1 and why it might work] +- [Approach 2 as alternative] +- [Key architectural decisions to make] + +**Recommended approach:** [If you have a preference, state it] +``` +
+ +
+Investigation Checklist (for bugs) + +```markdown +## Investigation Checklist +- [ ] Check [specific file/area] for [potential cause] +- [ ] Review recent changes in [related PR] +- [ ] Test with [specific user configuration] +- [ ] Verify [expected behavior] still works +``` +
+ +
+Constraints + +```markdown +## Constraints +- **Performance:** [e.g., Must load in <200ms] +- **Compatibility:** [e.g., Must support iOS 15+] +- **Data:** [e.g., Cannot break existing API contract] +- **Timeline:** [e.g., Must ship in 4.29.0] +``` +
+ +
+Feature Flag + +```markdown +## Feature Flag +- **Flag name:** `enable_[feature_name]` +- **Default:** OFF in prod, ON in dev +- **Rollout plan:** [e.g., 10% → 50% → 100%] +- **Kill switch:** [How to disable quickly if needed] +``` +
+ +
+Reference Implementation + +```markdown +## Reference Implementation +- Similar work: PR #[number] — [brief description] +- Follow pattern in: `[FilePath.swift]` +- Avoid approach from: PR #[number] — [why it didn't work] +``` +
+ +
+Risk/Rollback + +```markdown +## Risk Assessment +- **Risk:** [What could go wrong] +- **Likelihood:** [Low/Medium/High] +- **Impact:** [What breaks if it goes wrong] +- **Mitigation:** [How to reduce risk] +- **Rollback plan:** [How to undo — revert PR, feature flag off, data migration, etc.] +``` +
+ +
+Analytics Requirements + +```markdown +## Analytics Requirements +- **New events:** + - `[event_name]` — triggered when [condition] + - Properties: `[prop1]`, `[prop2]` +- **Success metric:** [How to measure if this worked] +- **Dashboard:** [Link or name of dashboard to update] +``` +
+ +--- + +## Step 4: Labels, Assignee & Issue Type + +- **Assignee:** David Sulitzer (@8Gaston8) +- **Labels:** Use existing labels (funnel stages, GFE, davidsulitzer.com, design, regression, etc.) + - Never use `bug`, `feature`, or `task` as labels — use Project Issue Type instead + - ⚠️ **`design` label** — Only use when designer (Liron) needs to create/review designs. When used, you MUST include Problem, Design Brief, and Design Requirements sections (see Step 3 template). + +### Issue Type — MANDATORY FOR EVERY ISSUE + +Issue Type is a GitHub-level field (not a project field). You MUST set it via GraphQL. + +**Available Issue Types:** +| Type | ID | When to Use | +|------|-----|-------------| +| **Task** | `IT_kwDOBluP0c4BBYRC` | General work items, refactors, chores | +| **Bug** | `IT_kwDOBluP0c4BBYRF` | Defects, broken functionality | +| **Feature** | `IT_kwDOBluP0c4BBYRI` | New user-facing capabilities | + +**Set Issue Type:** +```bash +# Replace with the issue's node ID (e.g., I_kwDOJF_Lac7kFn4f) +# Replace with the appropriate ID from the table above +gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "" }) { issue { id } } }' +``` + +**Verify Issue Type was set:** +```bash +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } } } }' +``` + +> ⚠️ **CRITICAL:** Always verify the issue type was set. The `gh issue view` CLI command does NOT show issue type — you MUST use the GraphQL query above to confirm. + +--- + +## Step 5: davidsulitzer.com Project Configuration + +Ensure EVERY issue is added to the **davidsulitzer.com** GitHub Project (number: 2, ID: ``) with ALL fields filled. + +### Add Issue to Project +```bash +gh project item-add 2 --owner 8Gaston8 --url +``` + +### Get Project Item ID (needed to set fields) +```bash +gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' +``` + +### Project Field IDs Reference +| Field | Field ID | Options | +|-------|----------|---------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `2c3687f7`, Todo: `f75ad846`, In Design: `646518b5`, To Clarify: `2fdcccf3`, In Development: `47fc9ee4`, Done: `98236657` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5`, Backlog: `8ef4ba21` | + +### Set Project Field Values +```bash +# Set Status (e.g., Todo) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "f75ad846" } }) { projectV2Item { id } } }' + +# Set Priority (e.g., Medium) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "2b2b659f" } }) { projectV2Item { id } } }' +``` + +### Priority Criteria +- **Critical** — Bugs affecting majority of users +- **High** — In focus, will move the needle significantly +- **Medium** — In focus, incremental improvement +- **Low** — Nice to have +- **Backlog** — Uncertain importance + +--- + +## Q1 2026 Focus: Key Metrics + +**🎯 North Star:** Define your current north-star metric and date. + +Prioritize issues that impact these metrics: + +### Top-Down Metrics (Pricing Funnel) +| Metric | Goal | +|--------|------| +| Pricing Screen → Trial Start | 20-25% | +| Trial Start → Trial Converted | 35-40% | + +### Leading Indicators +| Metric | Goal | +|--------|------| +| 🔥 Activation Rate | 60% | +| 🔄 Early Retention | 40% | +| 💾 Investment Rate | 10% | + +
+Metric Definitions + +- **🔥 Activation Rate:** % of new trial users who view 5+ place profiles from multi-category searches in their first 7 days +- **🔄 Early Retention:** % of trial users with 3+ sessions in first 3 days +- **💾 Investment Rate:** % of trial users who save at least 1 place in their first 7 days +
+ +--- + +## Step 6: Testing & Quality Requirements + +Every issue should explicitly address testing needs: + +### Unit Tests +- [ ] **New functionality** — Specify what unit tests should be added +- [ ] **Edge cases** — List specific edge cases to cover +- [ ] **Target coverage** — Aim for ≥80% on modified files + +### Regression Tests +- [ ] **Affected areas** — Identify existing functionality that could break +- [ ] **Test updates needed** — Note if existing tests need modification +- [ ] **Manual QA scope** — Define what needs manual testing before merge + +### Include in Issue Body +```markdown +## Testing Requirements +- [ ] Unit tests for [specific functionality] +- [ ] Update existing tests in [file/area] if affected +- [ ] Manual QA: [specific flows to verify] +- [ ] Regression check: [related features to verify still work] +``` + +--- + +## Step 7: Documentation & Tooling Updates + +Consider what else needs updating alongside the code: + +### Documentation +| What | When to Update | +|------|----------------| +| **README** | New setup steps, env vars, or major features | +| **API Docs** | New/changed endpoints, request/response formats | +| **Architecture Docs** | Structural changes, new patterns introduced | +| **Inline Comments** | Complex logic that needs explanation | + +### AI Agents & Commands +| What | When to Update | +|------|----------------| +| **Cursor Commands** | New workflows, changed processes | +| **Agent Rules** | New conventions, deprecated patterns | +| **Prompts/Instructions** | Changed context the AI needs to know | + +### Include in Issue Body (if applicable) +```markdown +## Documentation Updates +- [ ] Update README: [specific section] +- [ ] Update API docs: [endpoint/schema changes] +- [ ] Update agent rules: [new convention/pattern] +- [ ] Add inline comments for: [complex logic] +``` + +--- + +## Step 8: Final Verification Checklist + +Before sharing results, you MUST verify ALL of the following via GraphQL queries (not assumptions): + +### For EVERY Issue: +- [ ] Issue Type is set (query `issueType { name }` on the issue) +- [ ] Added to davidsulitzer.com project +- [ ] Status field is set (Todo, etc.) +- [ ] Priority field is set + +### For Cross-Repo Issues (any combination of iOS, Server, Tileserver): +- [ ] Blocking relationships exist (query `blockedBy` on each downstream issue — must show upstream issue(s)) +- [ ] NO sub-issue relationship (we use blocking, not parent/child hierarchy) + +### Verification Query (run for each issue): +```bash +gh api graphql -f query='{ + repository(owner: "8Gaston8", name: "") { + issue(number: ) { + title + issueType { name } + blockedBy(first: 5) { nodes { number repository { name } } } + parent { number } + } + } +}' +``` + +> ⚠️ **DO NOT SKIP VERIFICATION.** Mutations can return success without actually creating the relationship/setting the field. Always confirm with queries. + +--- + +## Step 9: Share Results + +Share the link(s) to the new issue(s) when done so I can review. + +Include a summary table showing: +- Issue links +- Issue Type (verified) +- Project Status & Priority (verified) +- Blocking relationships (verified, if applicable) diff --git a/.cursor/skills/new-release/SKILL.md b/.cursor/skills/new-release/SKILL.md new file mode 100644 index 0000000..699418d --- /dev/null +++ b/.cursor/skills/new-release/SKILL.md @@ -0,0 +1,168 @@ +--- +name: new-release +description: Prepare a new iOS release — branch, PR, regression testing, App Store submission +disable-model-invocation: true +--- +# new-release + +Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, Mixpanel, stability score, issue tracking), see `/release-shipped`. + +--- + +## Step 1: Create the Release Branch + +Create a new release branch on the iOS repo from `main`: + +```bash +cd +git fetch origin main +git checkout -b release/VERSION origin/main +``` + +**Branch naming:** `release/VERSION` (e.g., `release/4.31.0`) + +> If this is a **hotfix**, work on the existing relevant branch instead of creating a new one from develop. + +--- + +## Step 2: Create the Draft PR + +1. Check the diff since the most recent release tag +2. Create a **draft PR** targeting `main` with full details of what changed +3. Assign **Aviad** (`aviadsteps`) as the reviewer +4. Assign **Gaston** (`8Gaston8`) as the assignee +5. Add the label `testflight` to the draft PR + +--- + +## Hotfix PR Management + +When creating a hotfix release that cherry-picks commits from existing PRs: + +1. **Create the release PR** (e.g., `release/4.28.1` → `develop`) with the cherry-picked commits +2. **Close the original PR(s)** without merging, adding a comment explaining: + - The changes were cherry-picked into the hotfix release branch + - Reference the release PR number (e.g., "Closing in favor of Hotfix PR #XXXX") + - Explain that merging both would create duplicate commits in history + - Note that the original work is preserved in the closed PR for reference + +**Why?** Cherry-picking creates new commits with different SHAs. If both PRs are merged, you get duplicate commits in git history. The release PR should be merged because: +- It has the release tag pointing to its commits +- Keeps git history clean and traceable + +--- + +## Step 3: Create Fibery Release Item + +Create a new release item in the Fibery database (nomenclature: `iOS - VERSION`): +https://steps.fibery.io/fibery/space/Product_Pipeline_Space/database/Release + +- Link all relevant Fibery dev tasks to the release item +- Share the link with Gaston + +--- + +## Step 4: Update What's New Content + +**BEFORE proceeding with regression testing**, update the in-app What's New content: + +1. Open `Steps/AppFlows/Whats New/WhatsNewContent.swift` +2. Update the `version` to match the new release version +3. Update `title` with a catchy headline for the main feature +4. Update `features` with 2-4 compelling bullet points +5. Test the What's New sheet appears correctly (Case 22b in regression plan) + +**This is a required step for every release with user-facing features!** + +If no notable features warrant a What's New announcement, return `nil` from `WhatsNewContent.current`. + +--- + +## Step 5: Regression Testing + +Carefully read the regression test plan at `REGRESSION_TESTING_PLAN.md` (in the iOS repo root) and help execute it **one test case at a time**. + +### Handling Regression Bugs + +When bugs are found during regression testing: + +1. **Create a tracking issue** for the release's regression pass (optional but recommended): + - Title: `[Regression] v{VERSION} Regression Tracking` + - Description: Summary of bugs found and release risk status + - Label: `regression` + - Use `/new-issue` skill with GraphQL for proper project integration + +2. **Create focused issues** for each concrete bug: + - Use `/new-issue` skill and follow its Step 0 pre-research requirements + - Include reproducible steps, expected vs actual behavior, and impacted files + - Use relevant domain labels only (for example `regression`, `fastlane`) and avoid generic `bug` label + - Link related issues in issue body and comments; do not require parent-child/sub-issue hierarchy + +3. **Apply triage before fixing** (see `REGRESSION_TESTING_PLAN.md`): + - Classify each bug by area, risk, and coupling + - Default fix path: land focused fix PR on `develop`, then cherry-pick/merge into release branch + - If bugs are unrelated (for example CI/Fastlane + product flow), split into separate PRs + - Only bundle fixes when they are tightly coupled in one flow/root cause + +4. **Execution order**: + - Continue regression testing and log all issues first + - Fix blockers/high-risk regressions for the release + - Track non-blockers into next version when appropriate + +--- + +## Step 6: App Store Release Notes + +Once regression testing is over, write a neat, concise, and cool release note for the App Store. + +--- + +## Step 7: Finalize the PR & Project + +1. **Update the PR** — convert from draft to ready for review +2. **Update the relevant davidsulitzer.com GitHub project items**, especially: + - Status + - iOS Version + - Server Version +3. **Add a PR comment** summarizing the regression test results clearly + +--- + +## Step 8: Tag & Create Draft GitHub Release + +1. **Add a tag** to the latest commit on the release branch: `VERSION` (without any "v" prefix) +2. **Create a draft GitHub release** at https://github.com/8Gaston8/davidsulitzer.com/releases pointing to the tag: + - Release name: `vVERSION` + - Include the release notes + - Add a **"Marketing Story"** section — a very short story to make people *feel* the change that was shipped + - Add a **"Top Down Metrics Impact"** section describing the expected impact: + +**🎯 Q1 2026 North Star:** $1M ARR for davidsulitzer.com + +### Top-Down Metrics (Pricing Funnel) +Pricing Screen → Trial Start (goal: 20-25%) +Trial Start → Trial Converted (goal: 35-40%) + +### Leading Indicators +🔥 Activation Rate (goal: 60%) +> "% of new trial users who view 5+ place profiles from multi-category searches in their first 7 days" +🔄 Early Retention (goal: 40%) +> "% of trial users with 3+ sessions in first 3 days" +💾 Investment Rate (goal: 10%) +> "% of trial users who save at least 1 place in their first 7 days" + +--- + +## What NOT to Do + +- **Don't skip the What's New update** — every release with user-facing features needs it +- **Don't rush regression testing** — log all bugs before starting fixes +- **Don't merge hotfix PRs and the original PRs** — close the original, keep history clean +- **Don't forget to tag the commit** — the tag is needed for the GitHub release +- **Don't publish the GitHub release** — keep it as a draft until the app is approved by Apple (see `/release-shipped`) + +--- + +## Next Step + +When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate Mixpanel, and close out project tracking. diff --git a/.cursor/skills/new-skill/SKILL.md b/.cursor/skills/new-skill/SKILL.md new file mode 100644 index 0000000..463a0f2 --- /dev/null +++ b/.cursor/skills/new-skill/SKILL.md @@ -0,0 +1,514 @@ +--- +name: new-skill +description: Create a new agent skill from scratch following best practices — strong routing description, "Use when / Don't use when" blocks, templates, guardrails, and verification steps. Handles cross-repo sync, branching, issues, and PRs. Don't use for updating existing skills (/update-skill). +disable-model-invocation: true +--- +# new-skill + +Create a new agent skill from scratch, following [OpenAI's skills best practices](https://developers.openai.com/blog/skills-shell-tips). Handles the full workflow: designing the skill content to quality standards, syncing across repos, and setting up issues/PRs. + +--- + +## When to Use + +- The user wants to create a brand-new skill that doesn't exist yet +- A recurring workflow needs to be codified into a reusable skill +- The user says "create a skill for X" or "I need a skill that does Y" + +## When NOT to Use + +- Updating an existing skill → use `/update-skill` +- The task is a simple one-off that won't recur → just do it, no skill needed +- Deleting or renaming a skill → `/update-skill` handles those cases +- Installing a third-party/external skill → different workflow + +--- + +## Step 0: Understand the Request + +Before writing anything, clarify: + +1. **What does this skill do?** — Get a clear, specific description of the workflow +2. **Who/what invokes it?** — User-triggered only (`disable-model-invocation: true`) or proactive model invocation? +3. **Which repos need it?** — Relevant to all repos, or specific ones? +4. **Are there similar skills?** — Check the existing inventory to avoid overlap or confusion + +> If the user's request is vague, **ask for clarification**. "Create a skill that helps with deployments" is too broad — which environment? Which repo? What steps? + +--- + +## Step 1: Research Before Writing + +### 1a: Check for Existing Similar Skills + +Search the skill inventory for overlap: + +```bash +# List all skills in the current repo +ls .cursor/skills/ .codex/skills/ 2>/dev/null + +# Check other repos via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No skills" +done +``` + +If a similar skill exists, ask the user: "Should this be a new skill, or an update to `/existing-skill`?" + +### 1b: Research the Domain + +Before writing the skill, understand what it's automating: + +- **Read relevant code** — If the skill involves deploying, search for deploy scripts. If it involves testing, find existing test patterns. +- **Check existing workflows** — Are there manual runbooks, README instructions, or Slack threads that document this process? +- **Identify tools involved** — Which MCP tools, CLI commands, APIs, or services does this workflow use? +- **Talk to the user** — Ask what the current process looks like and what goes wrong. + +> The best skills codify something the team already does — they don't invent new processes. + +--- + +## Step 2: Write the Skill — Quality Standards + +This is the most important step. A well-written skill saves hours; a poorly-written one creates confusion. + +### 2a: The Description (Most Critical Field!) + +The description is the model's **routing logic** — it decides whether to invoke the skill. A skill with a bad description might as well not exist. + +**Formula:** What it does + When to use it + When NOT to use it (with alternatives) + +| Quality | Example | Why | +|---------|---------|-----| +| ❌ Bad | `deploy-backend-dev` | Just the name — tells the model nothing | +| ❌ Bad | `A helpful skill for deploying things` | Marketing copy — no routing signal | +| ✅ Good | `Deploy specific backend functions to the dev environment via the deployer API. Use after local testing passes and the branch is synced with develop. Don't use for prod deployments (/deploy-backend-prod) or for full deployments (always deploy individual functions).` | Clear what, when, and when-not | + +### 2b: Required Sections (Every Skill MUST Have) + +| Section | Purpose | Why It Matters | +|---------|---------|---------------| +| **Frontmatter** | name, description, disable-model-invocation | Metadata for the skill system | +| **"When to Use"** | Concrete triggers for invocation | Helps the model (and humans) know when this skill applies | +| **"When NOT to Use"** | Anti-triggers with alternatives | **Prevents misfires — can improve routing accuracy by ~20%** | +| **Numbered Steps** | Clear progression from start to finish | Agents follow procedural instructions best | +| **"What NOT to Do"** | Guardrails, anti-patterns, common mistakes | Agents make fewer errors when told what to avoid | +| **Verification Steps** | After critical actions, confirm they worked | Agents make mistakes; verification catches them early | + +### 2c: Strongly Recommended Sections + +| Section | When to Include | Why | +|---------|----------------|-----| +| **Templates** | Any repeatable output (PR comments, Slack messages, curl commands, issue bodies) | Templates inside skills are **free when unused but invaluable when needed** — they don't inflate tokens for unrelated queries | +| **Examples** | When behavior might be ambiguous | Worked examples reduce misinterpretation dramatically | +| **Quick Reference Tables** | Lookup data (field IDs, endpoint URLs, common patterns) | Saves the agent from searching every time | +| **Notes** | Edge cases, environment differences, related skills | Catches gotchas before they bite | + +### 2d: Negative Examples — The Secret Weapon + +The [OpenAI blog](https://developers.openai.com/blog/skills-shell-tips) found that making skills available can **drop correct triggering by ~20%** until you add "Don't use when..." guidance. For every "When to Use," write a corresponding "When NOT to Use" with the correct alternative: + +```markdown +## When NOT to Use + +- You're updating an existing skill → use `/update-skill` +- The task is a simple one-liner → just do it, no skill needed +- You need to delete a skill → `/update-skill` handles deletion too +``` + +For easily confused skill pairs, be extra explicit: + +```markdown +> ⚠️ This skill is for LOCAL testing only. +> If changes are already deployed to dev, use `/test-backend-changes-dev` instead. +``` + +### 2e: Writing Guidelines + +| Do ✅ | Don't ❌ | +|-------|---------| +| Be specific and actionable | Write vague guidance ("be careful") | +| Include exact commands (copy-pasteable) | Leave placeholders unexplained | +| Explain the "why" behind rules | Just list rules without context | +| Add negative examples ("Don't do X because Y") | Only write positive instructions | +| Reference other skills with `/skill-name` | Duplicate content from other skills | +| Use env vars for secrets (`$VAR_NAME`) | Hardcode credentials or tokens | +| Include verification after critical steps | Assume actions succeeded | +| Put templates inside the skill | Put templates in the system prompt | +| Keep skills self-contained | Require reading other files mid-execution | + +--- + +## Step 3: Quality Review Checklist + +Before saving, verify against this checklist: + +### Content Quality +- [ ] Description answers: What? When to use? When NOT to use? +- [ ] "When to Use" section has concrete triggers +- [ ] "When NOT to Use" section has alternatives for each case +- [ ] Steps are numbered and ordered logically +- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) +- [ ] Includes "What NOT to Do" guardrails +- [ ] Verification/confirmation steps after critical actions +- [ ] Templates for any repeatable output +- [ ] Examples where behavior might be ambiguous + +### Formatting +- [ ] Frontmatter `name` matches directory name +- [ ] `disable-model-invocation` is set (default: `true` unless proactive invocation is needed) +- [ ] References to other skills use `/skill-name` format +- [ ] No hardcoded secrets or tokens +- [ ] Markdown renders correctly (tables, code blocks, headers) + +### Routing Quality (The "Confusion Test") +- [ ] Could this skill be confused with any existing skill? If yes, add explicit disambiguation +- [ ] Would a model reading only the description know when to pick this vs alternatives? +- [ ] Are there edge cases where the wrong skill might be triggered? Add negative examples for those + +--- + +## Step 4: Determine Which Repos Need It + +| Skill Type | Typical Repos | +|-----------|---------------| +| Deployment skills | Repo being deployed | +| QA/testing skills | Repo being tested | +| Issue/project management skills | All repos (agents work in any) | +| Release skills | iOS repo primarily | +| General workflow skills | All repos | +| Skill management (like this one!) | All repos that have skills | + +When in doubt, **ask the user**. + +--- + +## Step 5: Apply to All Repos + +For **each repo** that needs the skill: + +### 5a: Create a Feature Branch + +```bash +cd +git fetch origin main # or origin/main for davidsulitzer.com +git checkout -b cursor/add-skill- origin/main +``` + +> ⚠️ **Never edit directly on `develop` or `main`.** +> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. + +### 5b: Write to BOTH Directories + +```bash +# Create directories +mkdir -p .cursor/skills/ +mkdir -p .codex/skills/ + +# Write SKILL.md to both locations (content MUST be identical) +``` + +### 5c: Update .gitignore (if needed) + +Some repos may have `.cursor` in their `.gitignore`. If so, add an exception: + +```gitignore +.cursor/* +!.cursor/skills/ +``` + +### 5d: Verify Sync Within the Repo + +```bash +diff .cursor/skills//SKILL.md .codex/skills//SKILL.md +``` + +If `diff` produces any output, fix it before committing. + +### 5e: Commit and Push + +```bash +git add .cursor/skills//SKILL.md .codex/skills//SKILL.md +git commit -m "skill(): add new skill — " +git push -u origin cursor/add-skill- +``` + +--- + +## Step 6: Create GitHub Issues (Non-Monorepo Repos Only) + +For each repo **except the davidsulitzer.com**, create a GitHub issue: + +```bash +gh issue create --repo stepscode/ \ + --title "skill(): add new skill — " \ + --body "" \ + --assignee 8Gaston8 \ + --label "fastlane" +``` + +### Issue Body Template + +```markdown +## Goal + +Create the `` skill in this repo. + +## Context + +[Brief description of what the skill does and why it's being created.] + +## Changes + +- New skill: `.cursor/skills//SKILL.md` +- New skill: `.codex/skills//SKILL.md` + +## Cross-Repo Sync + +This skill is being created across all relevant repos: +- [ ] `stepscode/davidsulitzer.com` — PR #XXX +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX + +All instances must remain identical. + +## Out of Scope + +- Content changes to other skills + +## Acceptance Criteria + +- [ ] Skill file exists in `.cursor/skills//SKILL.md` +- [ ] Skill file exists in `.codex/skills//SKILL.md` +- [ ] Both copies are identical (`diff` produces no output) +- [ ] Content matches all other repos +``` + +### Set Issue Type to Task + +```bash +ISSUE_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' --jq '.data.repository.issue.id') + +gh api graphql -f query="mutation { updateIssue(input: { id: \"$ISSUE_ID\", issueTypeId: \"IT_kwDOBluP0c4BBYRC\" }) { issue { id } } }" +``` + +### Add to davidsulitzer.com Project (Status: In Review, Priority: Low) + +```bash +# Add to project +gh project item-add 2 --owner 8Gaston8 --url + +# Get project item ID +ITEM_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id') + +# Set Status: In Review +gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzgntIc4\", value: { singleSelectOptionId: \"2895eb73\" } }) { projectV2Item { id } } }" + +# Set Priority: Low +gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzg5mgJc\", value: { singleSelectOptionId: \"9d5cb3c5\" } }) { projectV2Item { id } } }" +``` + +--- + +## Step 7: Create Pull Requests + +Create a PR for **every repo** that was changed: + +```bash +gh pr create \ + --repo stepscode/ \ + --base develop \ # or main for davidsulitzer.com + --head cursor/add-skill- \ + --title "skill(): add new skill — " \ + --body "" \ + --assignee 8Gaston8 +``` + +### PR Body Template + +```markdown +## Summary + +Create the `` skill — [one-line description]. + +## What This Skill Does + +[2-3 sentences describing the skill's purpose and workflow] + +## Cross-Repo Sync + +- `stepscode/davidsulitzer.com` — PR #XXX +- `8Gaston8/davidsulitzer.com` — PR #XXX + +All skill files are identical across repos. + +Closes # +``` + +> For davidsulitzer.com PRs, omit `Closes #` since there's no associated issue. + +### Assign & Request Review (MANDATORY) + +```bash +# Get PR node ID +PR_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' --jq '.data.repository.pullRequest.id') + +# Request review from Aviad +gh api graphql -f query="mutation { requestReviews(input: { pullRequestId: \"$PR_ID\", userIds: [\"MDQ6VXNlcjQ1NTMwOTMw\"] }) { pullRequest { number } } }" +``` + +> ⚠️ **Do this for EVERY PR created.** No exceptions. + +### Enable Auto-Merge (iOS Only) + +```bash +gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash +``` + +> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on other repos. + +--- + +## Step 8: Final Verification & Share Links + +### 8a: Verify Content Sync + +```bash +# Compare across repos (adjust paths based on which repos have the skill) +diff /.codex/skills//SKILL.md /.codex/skills//SKILL.md +``` + +### 8b: Verify Issues + +```bash +# For each non-davidsulitzer.com issue: +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' +``` + +### 8c: Share All Links (MANDATORY) + +**You MUST always end by sharing every relevant link:** + +```markdown +## Here are all the links: + +### Pull Requests +- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX + +### Issues +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- *(Monorepo: no issue needed)* + +### Summary Table +| Repo | PR | Issue | Status | Skill Synced | +|------|-----|-------|--------|-------------| +| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | +| davidsulitzer.com | [#XXX](url) | [#XXX](url) | Ready for Review | ✅ | +``` + +> ⚠️ **Do NOT skip sharing links.** Always use full URLs. + +--- + +## What NOT to Do + +- **Don't create a skill without researching existing ones** — you might be duplicating +- **Don't write a description that's just the skill name** — the description is routing logic +- **Don't skip "When NOT to Use"** — prevents misfires, improves routing by ~20% +- **Don't leave templates in the system prompt** — put them inside the skill (free when unused) +- **Don't hardcode secrets** — always use `$ENV_VAR` references +- **Don't create the skill in only one repo when it belongs in multiple** — sync all of them +- **Don't skip the quality checklist** — a sloppy skill is worse than no skill +- **Don't forget verification steps in the skill you're writing** — agents make mistakes +- **Don't create skills for one-off tasks** — skills are for recurring workflows + +--- + +## Reference: Skill File Template + +```markdown +--- +name: +description: +disable-model-invocation: true +--- +# + +[Opening line: what this skill does in one sentence] + +--- + +## When to Use + +- [Concrete trigger 1] +- [Concrete trigger 2] + +## When NOT to Use + +- [Anti-trigger 1] → use `/alternative` instead +- [Anti-trigger 2] → just do X directly + +--- + +## Step 1: [First Action] + +[Instructions with exact commands] + +--- + +## Step 2: [Second Action] + +[Instructions with verification] + +--- + +... + +--- + +## What NOT to Do + +- [Guardrail 1] — [why it matters] +- [Guardrail 2] — [why it matters] + +--- + +## Notes + +- [Environment info, related skills, edge cases] +``` + +--- + +## Reference: Project Field IDs + +| Field | Field ID | Key Options | +|-------|----------|-------------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | In Review: `2895eb73`, Done: `782fbd7c` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Low: `9d5cb3c5` | +| **Issue Type: Task** | `IT_kwDOBluP0c4BBYRC` | — | + +--- + +## Reference: Repo Quick Info + +| Repo | GitHub | Default Branch | +|------|--------|----------------| +| Monorepo | `stepscode/davidsulitzer.com` | `main` | +| iOS | `8Gaston8/davidsulitzer.com` | `develop` | +| Server | `8Gaston8/davidsulitzer.com` | `develop` | +| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | + +--- + +## Notes + +- **Skill names must be kebab-case** — lowercase, words separated by hyphens +- **One SKILL.md per skill** — each skill lives in its own directory +- **Both `.cursor/` and `.codex/` must always be in sync** within each repo +- **All repos must be in sync** with each other for shared skills +- **Monorepo gets PRs but NO issues** — only the subrepos get issues +- **Test after creating** — always suggest the user tests the skill with a real invocation +- Related skill: `/update-skill` — for updating, renaming, or deleting existing skills diff --git a/.cursor/skills/qa-start/SKILL.md b/.cursor/skills/qa-start/SKILL.md new file mode 100644 index 0000000..da67d95 --- /dev/null +++ b/.cursor/skills/qa-start/SKILL.md @@ -0,0 +1,318 @@ +--- +name: qa-start +description: Comprehensive QA workflow for PRs - adapts to iOS, server, or davidsulitzer.com context +disable-model-invocation: true +--- +# qa-start + +Help me QA this PR step by step. + +## Step 1: Checkout & Analyze + +1. **Checkout to the PR's branch** (in the appropriate repo) +2. **Analyze the PR changes** to determine what type of QA is needed: + - **iOS-only changes** → Use iOS QA workflow + - **Server-only changes** → Use Server QA workflow + - **Mixed changes (davidsulitzer.com)** → Run both workflows as needed + +--- + +## iOS QA Workflow + +For iOS/client changes: + +### Server Dependency Check + +**If NO server dependency:** +- Proceed with manual Xcode testing +- I will guide you through test scenarios +- You build and run in Xcode + +**If server changes must deploy first (blocking):** +⚠️ **Skip Xcode testing for now.** Instead: +1. **Code review only** — Verify client code is compatible and ready for server changes +2. **Still mark PR as Ready for Review** — Don't wait for server deployment +3. **Note:** End-to-end testing will happen during regression testing on the release branch (created from develop where all client changes will have been merged) + +### iOS PR Comment Template (Full QA) + +```markdown +## ✅ QA Complete + +### Testing Summary +| Area | Status | Notes | +|------|--------|-------| +| Happy Path | ✅ Pass | Main feature works | +| Edge Cases | ✅ Pass | Handled gracefully | +| UI/UX | ✅ Pass | Looks good | +| Regression | ✅ Pass | No issues | + +### Devices/Simulators Tested +- iPhone 17 Pro (latest iOS) + +--- +*QA performed by Cursor Agent + manual Xcode testing* +``` + +### iOS PR Comment Template (Server-Blocked) + +```markdown +## ✅ Code Review Complete — Server Dependency + +### Code Review +- ✅ Client code reviewed and compatible with expected server changes +- ✅ Error handling in place for new/modified endpoints + +### Server Dependency +- **Server PR:** #XXX +- **Note:** E2E testing will occur during regression on the release branch + +--- +*Code review performed by Cursor Agent* +``` + +--- + +## Server QA Workflow + +For server/backend changes, follow this **5-phase process**: + +### Phase 1: Local Testing +> Reference: `/test-backend-changes-local` + +**All yarn commands must run from the `api/` directory:** + +1. `cd api` (or `cd /api` if in the davidsulitzer.com root) +2. Create a test plan covering all new/changed functionality +3. Verify backward and forward compatibility +4. Run relevant unit tests: `yarn test:file ` +5. Start local server: `yarn api::offline::no_debug` +6. Execute test plan against `http://localhost:3000` +7. Stop local server when done + +> **Note:** Local server connects to **dev-mobile database**, not prod. + +### Phase 2: Deploy to Dev +> Reference: `/deploy-backend-dev` + +**Pre-deployment checklist (MUST verify before deploying):** +1. **Branch is synced with develop** — Run `git fetch origin main && git merge origin/main` to sync. This prevents deployment failures from conflicting or outdated code. +2. **Tests are passing** — Verify that all tests for your new code pass locally. Failing tests = failed deployment. +3. **Resolve any merge conflicts** if they occur during sync +4. **Push changes** to remote + +**Deploy functions one by one:** +```bash +source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=dev-mobile&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" +``` + +> **Required env vars:** `DEPLOYER_BASE_URL`, `DEPLOYER_USER_NAME`, `DEPLOYMENT_API_KEY` (from `api/.env`) +> **Note:** If in the davidsulitzer.com, use `davidsulitzer.com/api/.env` instead. + +**Wait for deployment confirmation:** +```bash +# Wait 30-45 seconds for deployment to complete +sleep 40 + +# Then check #deployments_from_ci Slack channel (ID: C01BS5TKHAN) +# Look for ":tada: A deploy_specific_function_job job has succeeded!" +``` + +⚠️ **Deploy functions one at a time** — wait for success message before deploying next function. + +**Important:** +- Only deploy relevant functions — **no full deployments** +- A script might comment out some code — make sure to **uncomment** anything that shouldn't be commented before deploying + +### Phase 3: Test on Dev +> Reference: `/test-backend-changes-dev` + +1. Reuse the test plan from local testing +2. Get/refresh dev access token if needed (via `/renew` endpoint) +3. Execute test plan against `$DEV_API_BASE_URL` (from `api/.env`) +4. Verify all scenarios pass + +### Phase 4: Claim a logTest slot + Deploy to Prod +> Reference: `/deploy-backend-prod` + +**IMPORTANT:** Claim a `logTest` slot first (`logTest`, `logTest2` ... `logTest10`) so multiple QA runs can happen in parallel without collisions. + +1. Claim a slot in dev: +```bash +source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/claim" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_id": "", + "branch": "", + "pr_number": + }' +``` + - Save the returned `slot` value (example: `logTest4`) as `` + - If claim fails with "no slots available", wait and retry; do not deploy to a random slot + +2. Modify `api/api/general.js` (or `davidsulitzer.com/api/api/general.js` from davidsulitzer.com root): + - Add action name to `allowedProdActions` array + - Add handler in `logTestFunction` + - **Temporarily comment out** this authorization check: + ```javascript + if (process.env.NAME_OF_STAGE !== "dev" && process.env.IS_OFFLINE !== "true") { + return { + statusCode: 401, + body: JSON.stringify({ message: "Unauthorized" }), + }; + } + ``` +3. Commit, push, and deploy the claimed slot to prod: +```bash +source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=prod&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" +``` + +4. Wait and verify deployment (same sleep + Slack check pattern) + +⚠️ **Only deploy to real functions (not logTest slots) if the user explicitly requests it!** + +### Phase 5: Test on Prod +> Reference: `/test-backend-changes-prod` + +1. Reuse the test plan +2. Get/refresh prod access token if needed +3. Test via the claimed slot endpoint: `$PROD_API_BASE_URL/v1/` (from `api/.env`) +4. Required headers for prod: + - `Origin: $MAP_MANAGER_ORIGIN` (from `api/.env`) + - `Content-Type: application/json` + - `Authorization: ` +5. Verify all scenarios pass +6. Release the slot when done (always release, even if tests fail): +```bash +source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/release" \ + -H "Content-Type: application/json" \ + -d '{ + "slot": "", + "agent_id": "" + }' +``` + +> **Note:** If changes were deployed to real functions (not just logTest slots), test using those real endpoints. + +### Server PR Comment Template + +```markdown +## ✅ QA Complete + +### Testing Summary +| Phase | Status | Details | +|-------|--------|---------| +| Local Testing | ✅ Pass | X unit tests passing | +| Dev Deployment | ✅ Done | Functions deployed | +| Dev Testing | ✅ Pass | All scenarios verified | +| Prod Deployment | ✅ Done | Deployed to logTest | +| Prod Testing | ✅ Pass | All scenarios working | + +### Compatibility +- ✅ Backward compatible +- ✅ Forward compatible + +--- +*QA performed by Cursor Agent* +``` + +--- + +## Deployment Monitoring Pattern + +When deploying, always use this pattern to wait and verify: + +```bash +# 1. Trigger deployment (returns immediately) +curl "$DEPLOYER_BASE_URL?..." + +# 2. Wait for CI to complete (30-45 seconds typically) +sleep 40 + +# 3. Check Slack for success message +# Use MCP tool: slack_get_channel_history with channel_id: C01BS5TKHAN +# Look for: ":tada: A deploy_specific_function_job job has succeeded!" +# Match the job number from your deployment request +``` + +--- + +## Monorepo Mixed Changes + +When the PR has both iOS and server changes: + +1. **First:** Complete the Server QA workflow (all 5 phases) +2. **Then:** Help with iOS QA (or code review if server-blocked) +3. Document both in the PR comment + +--- + +## Finalize PR + +After QA completes: + +1. **Commit & Push** any remaining changes +2. **Add detailed PR comment** using the appropriate template above +3. **Update the Project Board status** (see below) +4. **Notify the right people** based on the status + +### Step A: Determine the Status + +**Ask yourself: Would a screenshot look different before vs after your changes?** + +| Answer | Status | What type of review Gaston needs to do | +|--------|--------|----------------------------------------| +| **YES** (changed layouts, colors, text, images, animations, spacing, or anything the user sees) | "Human QA" | Build in Xcode and visually verify the UI changes | +| **NO** (pure logic, API, analytics, refactoring, performance, crash fixes) | "In Review" | Review the PR diff/code only (no Xcode needed) | + +### Step B: Update Project Board Status + +Use GitHub GraphQL API to update the status: + +**1. Get the issue's project item ID:** +```graphql +query { + repository(owner: "8Gaston8", name: "REPO_NAME") { + issue(number: ISSUE_NUMBER) { + projectItems(first: 10) { + nodes { + id + project { id } + } + } + } + } +} +``` +Replace `REPO_NAME` with `davidsulitzer.com`, `davidsulitzer.com`, or `davidsulitzer.com` as appropriate. +Find the item where `project.id = ""` (davidsulitzer.com project) + +**2. Update the status:** +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "ITEM_ID_FROM_STEP_1" + fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4" + value: { singleSelectOptionId: "OPTION_ID" } + }) { + projectV2Item { id } + } +} +``` + +**Status option IDs:** +- "Human QA": `6a5c111e` +- "In Review": `2895eb73` + +### Step C: Post Comment for Gaston's Review + +**Keep the PR as a draft.** Gaston will mark it Ready for Review after his review. + +**If "Human QA":** +- Post PR comment: `🔍 Visual QA needed — @8Gaston8 please build in Xcode and verify the UI changes look correct.` + +**If "In Review":** +- Post PR comment: `✅ QA complete — @8Gaston8 no visual changes, just verify the code looks good.` + +> **Note:** Do NOT mark the PR as Ready for Review or enable auto-merge. Gaston will do this after reviewing the AI's output. diff --git a/.cursor/skills/refine-spec/SKILL.md b/.cursor/skills/refine-spec/SKILL.md new file mode 100644 index 0000000..4e56004 --- /dev/null +++ b/.cursor/skills/refine-spec/SKILL.md @@ -0,0 +1,356 @@ +--- +name: refine-spec +description: Refine and improve an existing GitHub issue's spec through codebase research, edge case analysis, and iterative PM discussion. +disable-model-invocation: true +--- +# refine-spec + +Take an existing GitHub issue and elevate its spec to production-ready quality. This skill guides you through codebase research, section compliance (per the `/new-issue` skill), edge case discovery, and iterative refinement with the PM — so the AI agent or developer who picks it up makes zero mistakes. + +--- + +## When to Use + +- A spec exists but feels thin, vague, or missing context +- A design decision was just made and needs to be baked into the spec +- The PM wants to stress-test edge cases before handing off to development +- An issue was created quickly and now needs the full `/new-issue` treatment +- The spec drifted after discussion and needs to be reconciled with decisions made + +--- + +## Step 0: Read the Issue + +### 0a: Fetch the Full Issue + +```bash +gh issue view --repo stepscode/ --json title,body,labels,assignees,state,milestone +``` + +Take note of: +- **Labels** — especially `design` (triggers mandatory Design Brief sections) and `fastlane` +- **Milestone** — context for prioritization +- **Current body** — what's already there vs what's missing + +### 0b: Identify What the User Wants to Change + +Ask yourself: +1. Is this a **full rewrite** or **targeted improvements**? +2. Did the PM make specific design decisions that need to be captured? +3. Are there new requirements, constraints, or scope changes? +4. Is the PM asking for edge case analysis? + +> If the user's request is vague (e.g., "make this spec better"), ask what aspect they want improved: completeness, clarity, edge cases, technical depth, or all of the above. + +--- + +## Step 1: Pre-Research the Codebase + +Before changing a single word, investigate the codebase. This is the same research from `/new-issue` Step 0 — but applied to an existing issue that may have skipped this step. + +### 1a: Search for Relevant Code + +Search **all repos** for keywords related to the feature/bug: + +```bash +# 1. Always search the current repo first +rg -l "" . + +# 2. If in the davidsulitzer.com with submodules checked out, also search sibling repos: +rg -l "" davidsulitzer.com/ 2>/dev/null + +# 3. If in a standalone repo checkout (sibling dirs won't exist), use GitHub API: +gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 +``` + +> **Note:** The `2>/dev/null` suppresses errors when sibling directories don't exist (standalone checkouts). Always search `.` first so you never miss the current repo's code. + +**Capture for each relevant file:** +- Full path and key line numbers +- What the code does (brief description) +- How it relates to the feature being specified + +### 1b: Check Recent Changes + +```bash +git log --oneline -10 -- +``` + +Note any recent PRs that touched the area — they may contain context or introduce constraints. + +### 1c: Check for Related Issues + +```bash +# Open issues +gh issue list --repo stepscode/ --search "" --limit 10 + +# Recently closed (possible regression context) +gh issue list --repo stepscode/ --search "" --state closed --limit 5 +``` + +### 1d: Check Test Coverage + +```bash +# iOS tests +rg -l "" davidsulitzer.com/AtlyTests/ 2>/dev/null + +# Server tests +rg -l "" davidsulitzer.com/api/test/ 2>/dev/null +``` + +### 1e: Identify Dependencies + +Look for: +- Feature flags that gate the behavior +- API endpoints involved (grep for route definitions) +- Third-party services (Mixpanel, RevenueCat, Firebase, etc.) +- Caching layers +- Cross-repo dependencies (iOS ↔ Server ↔ Tileserver) + +> ⚠️ **Do NOT skip pre-research.** Even if the issue already has some code references, they may be outdated or incomplete. Fresh research catches drift. + +--- + +## Step 2: Audit Sections Against the `/new-issue` Skill + +Compare the existing issue body against the required and conditional sections from `/new-issue` Step 3. Build a gap table: + +### Required Sections (MUST exist in every issue) + +| Section | Present? | Notes | +|---------|----------|-------| +| **Pre-Research Findings** | ✅/❌ | Code refs, recent changes, related issues, test coverage, dependencies | +| **Goal** | ✅/❌ | Clear one-liner | +| **Context/Background** | ✅/❌ | Why this matters | +| **Requirements** | ✅/❌ | What needs to be built | +| **Out of Scope** | ✅/❌ | What's NOT part of this issue | +| **Acceptance Criteria** | ✅/❌ | Checkboxes for definition of done | +| **Testing Requirements** | ✅/❌ | Unit tests, regression, manual QA | +| **Related Issues/PRs** | ✅/❌ | Cross-references | + +### Conditional Sections (include when applicable) + +| Section | Applicable? | Present? | Notes | +|---------|------------|----------|-------| +| **Problem + Design Brief + Design Requirements** | Only if `design` label | ✅/❌ | ⚠️ REQUIRED when `design` label is present | +| **Technical Approach** | Features/refactors | ✅/❌ | Architecture, files to change | +| **Constraints** | Performance/compat/deadline | ✅/❌ | | +| **Feature Flag** | New features, risky changes | ✅/❌ | | +| **Reference Implementation** | Similar patterns exist | ✅/❌ | | +| **Risk/Rollback** | Medium+ risk changes | ✅/❌ | | +| **Analytics Requirements** | New Mixpanel events | ✅/❌ | | +| **Documentation Updates** | README, API docs, agent rules | ✅/❌ | | + +> Present this gap table to the PM. Let them decide which missing sections are relevant before you start writing. + +--- + +## Step 3: Analyze Edge Cases + +This is where specs go from "good" to "bulletproof." For every feature in the spec, systematically think through: + +### 3a: State Transitions + +- What are all the possible states? (e.g., loading, active, inactive, error, empty) +- What triggers each transition? +- Can the user get stuck in an unexpected state? +- What happens on state reset (new session, navigation, app restart)? + +### 3b: Interaction Combinations + +- What happens when this feature interacts with other features in the same area? +- If there's a toggle/filter, does it survive navigation, back-button, other selections? +- What about concurrent actions (rapid tapping, network in flight)? + +### 3c: Data Edge Cases + +- What if the data is empty? (No results, no items, no content) +- What if the data is missing fields? (Null/undefined values from API) +- What if the data is stale? (Cached, outdated, timezone mismatch) +- What about pagination? Does the feature work across pages? + +### 3d: UI Edge Cases + +- What about different sheet/modal states? (Minimized, expanded, transitioning) +- What about transient states? (Search focused, autocomplete showing, animations in progress) +- Dark mode? Dynamic Type? VoiceOver? +- Does it work when related UI elements are hidden/empty? + +### 3e: Platform Edge Cases + +- App backgrounding and foregrounding +- App kill and restart +- Network errors mid-action +- Low connectivity / timeout scenarios + +### 3f: Present Edge Cases to the PM + +**Do NOT silently make decisions about edge cases.** Present them as a table: + +| Scenario | Question / Suggested Behavior | +|----------|-------------------------------| +| [Edge case 1] | [What should happen? / Here's what I suggest: ...] | +| [Edge case 2] | [What should happen? / Here's what I suggest: ...] | + +Let the PM confirm or override each one before writing them into the spec. + +> **The goal is to eliminate ambiguity.** Every edge case that's undocumented is a coin-flip for the implementer. Document the decision, even if the decision is "we don't handle this — it's out of scope." + +--- + +## Step 4: Draft the Updated Spec + +### 4a: Preserve What's Good + +Don't rewrite sections that are already solid. Only modify what needs improvement. + +### 4b: Add Missing Sections + +Fill in every gap identified in Step 2, using the templates from `/new-issue` Step 3. + +### 4c: Weave in Edge Cases + +Edge cases should NOT be a standalone dump. Integrate them: +- **Behavioral edge cases** → Add to an "Edge Cases & Clarifications" table +- **Empty/error states** → Add to the relevant spec section (e.g., "Empty State" subsection) +- **State management edge cases** → Add to the "State Management" or "Toggle Behavior" section +- **Testing edge cases** → Add to "Testing Requirements" as specific test cases + +### 4d: Add Pre-Research Findings + +If the original spec lacked code references, add the **Pre-Research Findings** section with: +- Relevant file paths + line numbers + brief descriptions +- Recent changes (PRs that touched the area) +- Related issues (open and closed) +- Test coverage status +- Dependencies (APIs, feature flags, services) + +### 4e: Ensure Technical Approach is Implementation-Ready + +The Technical Approach section should give the implementer a clear roadmap: +- **Files to change** — with specific descriptions of what changes in each file +- **Key implementation notes** — gotchas, caveats, patterns to follow or avoid +- **Recommended approach** — if there are multiple valid approaches, state which one and why + +--- + +## Step 5: Update the Issue + +### 5a: Apply the Changes + +```bash +gh issue edit --repo stepscode/ --body "$(cat <<'EOF' + +EOF +)" +``` + +> ⚠️ **This replaces the entire body.** Always include ALL sections — both unchanged and updated — in the new body. Never use `--body-file` with partial content. + +### 5b: Labels — Do NOT Remove + +> ⚠️ **NEVER remove existing labels during spec refinement.** Labels like `design` are kept permanently on the issue — they indicate the issue *involves* design work, not that design is pending. Even if the design decision has been made, the label stays. + +You may **add** labels if the refinement reveals they're needed (e.g., adding `fastlane`): + +```bash +gh issue edit --repo stepscode/ --add-label "fastlane" +``` + +### 5c: Do NOT Update Project Status During Refinement + +> ⚠️ **Do NOT change the project status at any point during spec refinement.** Regardless of what status the issue was in when the conversation started, leave it as-is until the PM explicitly confirms the spec is finalized. + +**Only when the PM gives final approval** (e.g., "looks good, this is ready"), update the status to **Ready for Dev**: + +```bash +# Get project item ID via the issue itself (avoids the last-N item window problem) +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id' + +# Move to "Ready for Dev" — ONLY after PM final approval +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "cac3311f" } }) { projectV2Item { id } } }' +``` + +> **Note:** We query `projectItems` on the issue itself rather than scanning the last N project items. This works regardless of how many items are in the project. + +--- + +## Step 6: Iterate with the PM + +Spec refinement is rarely one-shot. After the first update: + +### 6a: Summarize What Changed + +Tell the PM exactly what you added, removed, or modified — in plain language, not diffs. + +### 6b: Flag Open Questions + +If any edge cases or design decisions are still unresolved, list them explicitly: + +> **Still need your input on:** +> 1. [Question about behavior X] +> 2. [Question about scope Y] +> 3. [Question about edge case Z] + +### 6c: Apply Follow-Up Changes + +When the PM responds with decisions: +1. Update the spec immediately +2. Summarize what changed in this iteration +3. Repeat until the PM is satisfied + +> **Keep iterating.** A spec is done when the PM says it's done, not when you think it's complete. + +--- + +## Step 7: Final Quality Check + +Before declaring the spec done, verify: + +### Content Quality +- [ ] Every section from Step 2's gap table is addressed (present or explicitly N/A) +- [ ] Edge cases are documented with clear expected behaviors +- [ ] Technical approach references specific file paths and line numbers +- [ ] Acceptance criteria are concrete and testable (not vague) +- [ ] Testing requirements include unit tests, regression tests, AND manual QA +- [ ] Out of scope is explicit (prevents scope creep during implementation) + +### Issue Metadata +- [ ] Labels are unchanged (never remove existing labels — especially `design`) +- [ ] Milestone is set +- [ ] Assignee is set +- [ ] Issue type is set (Feature / Bug / Task) +- [ ] Project status is unchanged (only update to "Ready for Dev" after PM gives explicit final approval) + +### Readability +- [ ] A developer or AI agent reading this spec for the first time can implement the feature without asking clarifying questions +- [ ] No contradictions between sections +- [ ] No placeholder text or TODOs left in the body +- [ ] Tables are formatted correctly (render check) + +--- + +## Anti-Patterns (What NOT to Do) + +| Anti-Pattern | Why It's Bad | Do This Instead | +|-------------|-------------|-----------------| +| Rewriting the entire spec without reading it first | Loses context and decisions already made | Read first, preserve what's good | +| Making design decisions without asking the PM | You're refining, not designing | Present options, let PM decide | +| Dumping edge cases without suggested behaviors | Shifts the thinking burden to the implementer | Always include a suggested behavior | +| Adding sections just to check a box | Bloats the spec with low-value content | Only add sections that provide real value | +| Ignoring the `design` label requirements | The `/new-issue` skill REQUIRES Problem + Design Brief + Design Requirements when `design` label is present | Check labels first, comply with the section requirements | +| Removing labels during refinement | Labels are permanent markers — `design` means the issue involves design, not that design is pending | Never remove labels; only add if needed | +| Updating project status without PM approval | Only the PM decides when a spec is ready for development | Leave status unchanged until PM explicitly says "ready" | +| Updating the spec without telling the PM what changed | PM can't review what they can't see | Always summarize changes after each update | +| Skipping pre-research because "the issue already has context" | Existing context may be outdated or incomplete | Always do fresh research | +| Writing vague acceptance criteria | "Works correctly" is not testable | Be specific: "When X happens, Y should result" | + +--- + +## Quick Reference: Relationship to Other Skills + +| Skill | Relationship | +|-------|-------------| +| `/new-issue` | `refine-spec` applies `/new-issue`'s section requirements to an EXISTING issue. If the issue was created without `/new-issue`, this skill brings it up to standard. | +| `/qa-start` | A well-refined spec makes QA faster. The testing requirements section feeds directly into QA checklists. | +| `/update-skill` | If the refinement process reveals a gap in any skill (including this one), use `/update-skill` to improve it. | diff --git a/.cursor/skills/release-shipped/SKILL.md b/.cursor/skills/release-shipped/SKILL.md new file mode 100644 index 0000000..0694ec6 --- /dev/null +++ b/.cursor/skills/release-shipped/SKILL.md @@ -0,0 +1,278 @@ +--- +name: release-shipped +description: Post-Apple-approval steps — publish release, Mixpanel annotation, close out tracking, assign iOS versions to server & tile-server issues +disable-model-invocation: true +--- +# release-shipped + +Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating Mixpanel, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. + +> **Prerequisite:** The `/new-release` skill must have been completed first — the draft GitHub release, tag, and PR should already exist. + +--- + +## Step 1: Publish the GitHub Release + +Convert the draft GitHub release to a published (non-draft) release: + +```bash +# Find the draft release for this version +gh release list --repo 8Gaston8/davidsulitzer.com --json tagName,isDraft | jq '.[] | select(.isDraft == true)' + +# Publish it (replace VERSION with the actual version, e.g. "v4.30.0") +gh release edit VERSION --repo 8Gaston8/davidsulitzer.com --draft=false +``` + +> **Verify:** `gh release view VERSION --repo 8Gaston8/davidsulitzer.com --json isDraft` should return `false`. + +--- + +## Step 2: Add Mixpanel Annotation + +Annotate the release in Mixpanel with the **actual release date** (when Apple made it live, not when it was submitted): + +```bash +curl -X POST \ + 'https://mixpanel.com/api/app/projects//annotations' \ + --user "$MIXPANEL_SERVICE_ACCOUNT_USERNAME:$MIXPANEL_SERVICE_ACCOUNT_SECRET" \ + -H 'Content-Type: application/json' \ + -d '{ + "description": "iOS VERSION released", + "datetime": "YYYY-MM-DDTHH:MM:SSZ", + "tags": ["ios-release", "VERSION"] + }' +``` + +> **Note:** `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set in your environment (from `.env` file at `davidsulitzer.com/api/.env`). + +--- + +## Step 3: List New Mixpanel Events + +If any new Mixpanel events were created as part of this release, list them so Gaston can prepare relevant Mixpanel dashboards or reports. + +--- + +## Step 4: Update GitHub Issue Version Tracking — iOS Issues + +Check all GitHub issues associated with PRs that have been merged since the last version and update their davidsulitzer.com GitHub project items: + +1. **Find merged PRs** since the last release tag +2. **For each merged PR**, find the connected GitHub issues (via GraphQL `timelineItems` with `CONNECTED_EVENT`) +3. **Update the project items** — set the iOS Version field on each issue's project item + +```graphql +# Example: Get connected issues for a PR +{ + repository(owner: "8Gaston8", name: "davidsulitzer.com") { + pullRequest(number: PR_NUMBER) { + timelineItems(first: 50, itemTypes: [CONNECTED_EVENT]) { + nodes { + ... on ConnectedEvent { + subject { ... on Issue { number title } } + } + } + } + } + } +} +``` + +### Setting the iOS Version field + +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "" + fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" + value: { text: "" } + }) { + projectV2Item { id } + } +} +``` + +> ⚠️ **Always double-check** that the associated PR was merged *after* the previous version was released. Verify by checking the latest commit tags. + +--- + +## Step 5: Assign iOS Versions to Server & Tile-Server Issues + +Server and tile-server issues ship independently (via their own deploy cycles), but we want them tagged with the **iOS Version that was current when they shipped**. This creates a unified timeline: when you look at an iOS version, you see everything that shipped around that time. + +### The Rule + +> If a server or tile-server issue was closed (shipped) between iOS version A's release date and iOS version B's release date, assign it **iOS Version = A**. + +### 5a: Build the iOS Release Timeline + +```bash +# Get the last N iOS releases with their dates +gh release list --repo 8Gaston8/davidsulitzer.com --limit 20 +``` + +This gives you ordered pairs like: +- `v4.29.0` → `2026-01-27T08:55:57Z` +- `v4.30.0` → `2026-02-12T16:47:11Z` + +### 5b: Find Unassigned Server Issues + +Query all project items and find those from `davidsulitzer.com` that: +- Have **Status = Done** (shipped) +- Have a **Server Version** set (confirms it's a server issue that was deployed) +- Do **NOT** have an **iOS Version** set + +```graphql +# Paginate through all project items (100 at a time) +{ + organization(login: "8Gaston8") { + projectV2(number: 2) { + items(first: 100, after: CURSOR) { + pageInfo { hasNextPage endCursor } + nodes { + id + fieldValues(first: 20) { + nodes { + ... on ProjectV2ItemFieldTextValue { + text + field { ... on ProjectV2Field { name } } + } + ... on ProjectV2ItemFieldSingleSelectValue { + name + field { ... on ProjectV2SingleSelectField { name } } + } + } + } + content { + ... on Issue { + number + title + repository { name } + closedAt + state + } + } + } + } + } + } +} +``` + +Filter for items where: +- `repository.name == "davidsulitzer.com"` +- `status == "Done"` +- `Server Version` field is set (non-empty) +- `iOS Version` field is empty + +### 5c: Find Unassigned Tile-Server Issues + +Same query as above, but filter for: +- `repository.name == "davidsulitzer.com"` +- `status == "Done"` +- `Tile Server Version` field is set (non-empty) +- `iOS Version` field is empty + +### 5d: Assign iOS Versions + +For each unassigned issue, compare its `closedAt` timestamp against the iOS release timeline. Find the **latest iOS release that was released before the issue was closed** — that's the iOS Version to assign. + +**Algorithm:** +``` +for each unassigned_issue: + closed_at = issue.closedAt + ios_version = None + for release in ios_releases_sorted_by_date_ascending: + if release.date <= closed_at: + ios_version = release.version + else: + break + assign ios_version to issue +``` + +**Set the iOS Version:** + +```graphql +mutation { + updateProjectV2ItemFieldValue(input: { + projectId: "" + itemId: "" + fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" + value: { text: "" } + }) { + projectV2Item { id } + } +} +``` + +### 5e: Verify Assignments + +After updating, re-query each item to confirm the iOS Version was set correctly: + +```graphql +{ + node(id: "") { + ... on ProjectV2Item { + fieldValues(first: 20) { + nodes { + ... on ProjectV2ItemFieldTextValue { + text + field { ... on ProjectV2Field { name } } + } + } + } + content { + ... on Issue { number title } + } + } + } +} +``` + +### 5f: Report to the User + +Present the assignments in a clear table: + +| Issue | Repo | Server/Tile Version | Closed | Assigned iOS Version | +|-------|------|---------------------|--------|---------------------| +| #1234 | davidsulitzer.com | 3.140.0 | Jan 20 | 4.28.0 | +| #175 | davidsulitzer.com | 1.38.0 | Feb 5 | 4.29.0 | + +Also report any issues that could NOT be assigned (e.g., closed before the earliest known iOS release). + +--- + +## Project Field Reference + +| Field | Field ID | Type | +|-------|----------|------| +| **iOS Version** | `PVTF_lADOBluP0c4AxnzVzg7CAVA` | Text | +| **Server Version** | `PVTF_lADOBluP0c4AxnzVzg7CD1Q` | Text | +| **Tile Server Version** | `PVTF_lADOBluP0c4AxnzVzg9t2v4` | Text | +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | SingleSelect | +| **Project ID** | `` | — | + +**Status Option IDs:** +- Done: `782fbd7c` +- Ready for Release: `2f956420` + +--- + +## What NOT to Do + +- **Don't run this before Apple approves** — the Mixpanel annotation timestamp must reflect the actual release date +- **Don't forget to publish the GitHub release** — it stays as a draft until this skill is invoked +- **Don't update issue versions for PRs merged before the previous release** — only PRs merged after the last tag +- **Don't skip server/tile-server issues** — they ship independently but still need iOS Version assignment for timeline tracking +- **Don't guess iOS version windows** — always use the actual release dates from `gh release list`, not approximations +- **Don't assign iOS Versions to server/tile-server issues that aren't Done** — only issues with Status = Done and a Server/Tile Server Version set should get an iOS Version + +--- + +## Prerequisites + +- `/new-release` must have been completed (draft release, tag, and PR exist) +- The app must be **approved and live** in the App Store +- Environment variables `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) +- `gh` CLI must be authenticated with access to `8Gaston8/davidsulitzer.com`, `8Gaston8/davidsulitzer.com`, and `8Gaston8/davidsulitzer.com` repos diff --git a/.cursor/skills/respect-figma/SKILL.md b/.cursor/skills/respect-figma/SKILL.md new file mode 100644 index 0000000..4b5a616 --- /dev/null +++ b/.cursor/skills/respect-figma/SKILL.md @@ -0,0 +1,148 @@ +--- +name: respect-figma +description: Fetch design context, screenshots, variables, and assets from Figma and translate them into production code. Works with both the Figma MCP (local/desktop) and the Figma REST API (cloud/CI agents). Trigger when a task involves Figma URLs, node IDs, or design-to-code implementation. +disable-model-invocation: false +--- + +# Respect Figma + +Fetch design data from Figma and translate it faithfully into production code. +Works in **two modes** depending on where the agent is running — see "Choose your tool" below. + +For setup details (env vars, config, verification), see `references/figma-config.md`. +For a tool catalog and prompt patterns, see `references/figma-tools-and-prompts.md`. + +--- + +## 0 — Choose Your Tool + +| Environment | How to access Figma | +|---|---| +| **Local / desktop** (Cursor, Codex desktop) | Use the **Figma MCP** tools (`get_design_context`, `get_screenshot`, etc.). The MCP server communicates with the running Figma desktop app. | +| **Cloud / CI / headless** (Codex cloud, GitHub Actions) | The MCP is **not available**. Use the **Figma REST API** with an access token from environment secrets. | + +### Figma REST API quick reference + +Base URL: `https://api.figma.com` + +| What you need | Endpoint | Notes | +|---|---|---| +| Design data (node tree, styles, layout) | `GET /v1/files/:fileKey/nodes?ids=:nodeId` | Equivalent to `get_design_context` | +| Rendered image / screenshot | `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` | Equivalent to `get_screenshot` | +| Export SVG asset | `GET /v1/images/:fileKey?ids=:nodeId&format=svg` | For downloading vector assets | +| Variables and styles | `GET /v1/files/:fileKey/variables/local` | Equivalent to `get_variable_defs` | +| File styles | `GET /v1/files/:fileKey/styles` | Color, text, effect, and grid styles | + +**Authentication:** `X-Figma-Token: ` header, or `Authorization: Bearer `. +Use whatever token is available in your environment secrets (`FIGMA_ACCESS_TOKEN`, `FIGMA_OAUTH_TOKEN`, etc.). + +### Extracting fileKey and nodeId from a Figma URL + +``` +https://figma.com/design/:fileKey/:fileName?node-id=:int1-:int2 +``` + +- `fileKey` → the segment after `/design/` +- `nodeId` → replace the `-` in `int1-int2` with `:` → `int1:int2` + +Example: `https://figma.com/design/AbC123/MyFile?node-id=42-99` +→ fileKey = `AbC123`, nodeId = `42:99` + +--- + +## 1 — Required Flow (Do Not Skip) + +Whether you are using the MCP or the REST API, the flow is the same conceptually: + +1. **Get design context** for the exact node(s). + - MCP: `get_design_context(nodeId, fileKey)` + - REST: `GET /v1/files/:fileKey/nodes?ids=:nodeId` + +2. **If the response is too large or truncated**, get the high-level node map first, then re-fetch only the required child node(s). + - MCP: `get_metadata(nodeId, fileKey)` → then `get_design_context` on smaller nodes + - REST: same endpoint with individual child node IDs + +3. **Get a screenshot** for visual reference of the node/variant being implemented. + - MCP: `get_screenshot(nodeId, fileKey)` + - REST: `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` + +4. **Only after you have both** the design context AND the screenshot, download any needed assets and start implementation. + +5. **Translate** the Figma output into the project's conventions, styles, and framework. Reuse existing color tokens, components, and typography. + +6. **Validate** the final UI against the Figma screenshot for 1:1 visual parity before marking complete. + +> **Always start from the Figma source of truth.** Do not guess icons from code comments, asset names, or SF Symbol approximations. + +--- + +## 2 — Implementation Rules + +- Treat the Figma MCP output (typically React + Tailwind) as a **representation of design and behavior**, not as final code style. +- Replace Tailwind utility classes with the project's preferred utilities / design-system tokens when applicable. +- **Reuse existing components** (buttons, inputs, typography, icon wrappers) instead of duplicating functionality. +- Use the project's **color system, typography scale, and spacing tokens** consistently. +- Respect existing **routing, state management, and data-fetch patterns** already adopted in the repo. +- Strive for **1:1 visual parity** with the Figma design. When conflicts arise, prefer design-system tokens and adjust spacing or sizes minimally to match visuals. +- Validate the final UI against the Figma screenshot for both look and behavior. + +--- + +## 3 — Asset Handling + +### General rules + +- The Figma MCP provides an assets endpoint which can serve image and SVG assets. +- **If the MCP returns a localhost source** for an image or SVG, use that source directly. Do NOT create placeholders. +- **Do NOT import/add new icon packages** — all assets should come from the Figma payload. +- When using the REST API, export assets via `GET /v1/images/:fileKey?ids=:nodeId&format=svg` (or `png`/`pdf`). + +### iOS-specific asset workflow + +When extracting icons or images for an iOS / Xcode project: + +1. Call `get_design_context` (or REST equivalent) on the parent view or the specific node. +2. Identify the asset by its `data-name` attribute and corresponding download URL. +3. Download the SVG from the asset URL. +4. Convert to PDF for the Xcode asset catalog: + ```bash + rsvg-convert -f pdf -o icon.pdf icon.svg + ``` +5. Add the PDF to the appropriate `.imageset` directory with a `Contents.json`: + ```json + { + "images": [{ "filename": "icon.pdf", "idiom": "universal" }], + "info": { "author": "xcode", "version": 1 }, + "properties": { "preserves-vector-representation": true } + } + ``` +6. Register in SwiftGen's `Assets.swift` (or the project's asset registry). + +> **Never substitute SF Symbols or existing assets by name alone.** Always visually verify against the Figma design using `get_screenshot` or the REST images endpoint. + +--- + +## 4 — Link-Based Prompting + +- The MCP server is link-based: copy the Figma frame/layer URL and give it to the agent. +- The agent extracts the node ID from the link — it does **not** browse the page. +- Always ensure the link points to the **exact node or variant** you want implemented. +- When given a broad URL (e.g., a full screen), `get_design_context` returns the complete subtree including every nested icon, image, and component with downloadable URLs. Drill into specific `data-node-id` values for individual assets. + +--- + +## 5 — Do NOT + +- Do **not** skip `get_design_context` and jump straight to coding from a screenshot alone. +- Do **not** rely on `get_metadata` for finding assets — it only returns the top-level frame without children. +- Do **not** guess colors, spacing, or font sizes. Extract them from the design context or variables. +- Do **not** import new icon libraries (FontAwesome, SF Symbols, etc.) when the Figma payload already provides the asset. +- Do **not** create placeholder images when a real asset URL is available. +- Do **not** hardcode pixel values when the project has spacing/sizing tokens that match. + +--- + +## References + +- `references/figma-config.md` — MCP setup, REST API auth, environment variables, verification, troubleshooting. +- `references/figma-tools-and-prompts.md` — Full tool catalog and prompt patterns for selecting frameworks/components and fetching metadata. diff --git a/.cursor/skills/update-skill/SKILL.md b/.cursor/skills/update-skill/SKILL.md new file mode 100644 index 0000000..a5451a1 --- /dev/null +++ b/.cursor/skills/update-skill/SKILL.md @@ -0,0 +1,588 @@ +--- +name: update-skill +description: Create or update Cursor/Codex skills across all repos — keeps everything in sync +disable-model-invocation: true +--- +# update-skill + +Create a new skill or update an existing one **across every repo where it exists**. Handles the full workflow: discovering where the skill lives, keeping `.cursor/skills/` and `.codex/skills/` in sync within each repo, keeping all repos in sync with each other, branching, creating issues, opening PRs, and linking everything together. + +--- + +## Step 0: Understand the Request + +Determine from the user's message: + +1. **Which skill?** — The skill name (e.g., `deploy-backend-dev`, `new-issue`). If the user wants to create a brand-new skill, they'll provide a name and description. +2. **What changes?** — What to add, remove, modify, or rewrite. +3. **Create or update?** — If the skill doesn't exist anywhere yet, this is a creation. If it does, it's an update. + +> If the user's request is vague, **ask for clarification** before proceeding. Don't guess what a skill should do. + +--- + +## Step 1: Discover Where the Skill Exists (Cross-Repo Scan) + +**This is the most critical step.** A skill can exist in ANY combination of these repos: + +| Repo | GitHub Repo | Skill Locations | +|------|-------------|-----------------| +| **Monorepo** | `stepscode/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **iOS** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **Server** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | +| **Tileserver** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | + +### 1a: Check Every Repo + +You MUST check ALL four repos. This skill can run from **any** repo, so don't assume a davidsulitzer.com directory layout. + +**Check the current repo locally:** + +```bash +# Current repo — check local skill directories +echo "=== Current Repo ===" && ls .cursor/skills/ .codex/skills/ 2>/dev/null +``` + +**Check other repos via GitHub API:** + +```bash +# Check each remote repo for skills (works from any repo) +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills" --jq '.[].name' 2>/dev/null || echo " No .cursor/skills/" + gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No .codex/skills/" +done +``` + +> **Note:** If you're in the davidsulitzer.com and submodules are checked out, you can also use local paths (`davidsulitzer.com/`, `davidsulitzer.com/`, `davidsulitzer.com/`) — but the `gh api` approach works universally from any repo. + +### 1b: Search for the Specific Skill + +```bash +# Check if the skill exists in each repo via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .cursor" + gh api "repos/stepscode/$repo/contents/.codex/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .codex" +done +``` + +### 1c: Record What You Found + +Build a discovery table: + +| Repo | .cursor/skills | .codex/skills | Action Needed | +|------|---------------|---------------|---------------| +| Monorepo | ✅/❌ | ✅/❌ | Create/Update | +| iOS | ✅/❌ | ✅/❌ | Create/Update | +| Server | ✅/❌ | ✅/❌ | Create/Update | +| Tileserver | ✅/❌ | ✅/❌ | Create/Update | + +### 1d: Decide Which Repos Need the Skill + +- **If updating:** Update the skill in ALL repos where it currently exists. If the user wants to add it to additional repos, do that too. +- **If creating:** Ask the user which repos should have the skill, unless the intent is obvious (e.g., a deployment skill only makes sense in the repos that deploy). +- **If the user says "all repos":** Add it to all four repos. + +> ⚠️ **NEVER update a skill in only one repo when it exists in multiple.** The whole point is cross-repo consistency. + +--- + +## Step 2: Read & Compare Existing Content + +If the skill already exists, read it from all locations and check for drift: + +```bash +# Compare across repos (if the skill exists in multiple) +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +``` + +Also verify `.cursor/` and `.codex/` are in sync WITHIN each repo: + +```bash +diff /.cursor/skills//SKILL.md /.codex/skills//SKILL.md +``` + +If drift exists, alert the user and ask which version should be the base — then unify everything from there. + +--- + +## Step 3: Write the Skill Content + +### Skill File Format + +Every `SKILL.md` must follow this structure: + +```markdown +--- +name: +description: +disable-model-invocation: true +--- +# + +[Skill content — instructions, steps, templates, references, etc.] +``` + +### Frontmatter Rules + +| Field | Required | Notes | +|-------|----------|-------| +| `name` | Yes | Must match the directory name exactly | +| `description` | Yes | Short, descriptive — shown in skill picker | +| `disable-model-invocation` | Yes | Always set to `true` | + +### Content Guidelines + +- **Be specific and actionable** — Skills are instructions for AI agents. Vague guidance produces vague results. +- **Include exact commands** — bash commands, API calls, GraphQL queries. Don't make the agent guess. +- **Add context for "why"** — Agents follow rules better when they understand the reasoning. +- **Use step-by-step structure** — Numbered steps with clear progression. +- **Include templates** — For repetitive outputs (PR comments, issue bodies, messages), provide fill-in-the-blank templates. +- **Add guardrails** — What to do, but also what NOT to do. Explicit "don't" instructions prevent common mistakes. +- **Reference other skills** — If a skill depends on another (e.g., `qa-start` references `deploy-backend-dev`), link to it with `/skill-name` notation. +- **Include verification steps** — After key actions, add a "verify this worked" step. Agents make mistakes; verification catches them. +- **Separate required vs. optional sections** — Use collapsible `
` blocks for conditional/optional content. +- **Keep it DRY** — If the same info appears in multiple skills, consider whether it belongs in a shared reference or a dedicated skill. + +### Quality Checklist (Before Saving) + +- [ ] Frontmatter `name` matches directory name +- [ ] Description is clear and concise +- [ ] Steps are numbered and ordered logically +- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) +- [ ] Includes "what NOT to do" guardrails +- [ ] Verification/confirmation steps after critical actions +- [ ] References to other skills use `/skill-name` format +- [ ] No hardcoded secrets or tokens (use env vars like `$VARIABLE_NAME`) +- [ ] Markdown renders correctly (check headers, tables, code blocks) + +--- + +## Step 4: Apply Changes to ALL Repos + +For **each repo** that needs the skill: + +### 4a: Create a Feature Branch + +```bash +cd +git fetch origin main +git checkout -b cursor/ origin/main +``` + +**Branch naming convention:** `cursor/update-skill-` or `cursor/add-skill-` + +> ⚠️ **Never edit directly on `develop` or `main`.** +> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. + +### 4b: Write to BOTH Directories Within Each Repo + +```bash +# Create directories if they don't exist +mkdir -p .cursor/skills/ +mkdir -p .codex/skills/ + +# Write the skill file to both locations (content must be IDENTICAL) +# ... write SKILL.md to .cursor/skills//SKILL.md +# ... write SKILL.md to .codex/skills//SKILL.md +``` + +### 4c: Update .gitignore (if needed) + +Some repos may have `.cursor` in their `.gitignore`. If so, add an exception for skills: + +```gitignore +# IDE (ignore all except skills/) +.cursor/* +!.cursor/skills/ +``` + +Same for `.codex` if applicable. + +### 4d: Verify Sync Within the Repo + +```bash +diff .cursor/skills//SKILL.md .codex/skills//SKILL.md +``` + +If `diff` produces any output, something went wrong — fix it before committing. + +### 4e: Commit and Push + +```bash +# Stage skill files (always) +git add .cursor/skills//SKILL.md .codex/skills//SKILL.md + +# Stage .gitignore too if it was modified in step 4c +git diff --name-only .gitignore 2>/dev/null | grep -q . && git add .gitignore + +git commit -m "skill(): " +git push -u origin +``` + +### Commit Message Conventions + +| Action | Message Format | +|--------|---------------| +| New skill | `skill(): add new skill — ` | +| Update skill | `skill(): ` | +| Fix skill | `skill(): fix ` | +| Multiple skills | `skills: ` | + +--- + +## Step 5: Create GitHub Issues (for Non-Monorepo Repos) + +**For each repo EXCEPT the davidsulitzer.com**, create a GitHub issue using the `/new-issue` skill patterns. + +> ⚠️ **Monorepo exception:** The davidsulitzer.com does NOT need a GitHub issue — only the PR is sufficient. + +### 5a: Create Issues + +Use the `gh` CLI to create issues in the relevant repos: + +```bash +gh issue create --repo stepscode/ \ + --title "skill(): " \ + --body "" \ + --assignee 8Gaston8 \ + --label "fastlane" +``` + +### 5b: Issue Body Template + +```markdown +## Goal + +[Create / Update] the `` skill [in this repo / across all repos]. + +## Context + +[Brief description of what the skill does and why it's being created/updated.] + +## Changes + +- [List of specific changes being made] + +## Cross-Repo Sync + +This skill [is being created / has been updated] across all repos where it exists: +- [ ] `stepscode/davidsulitzer.com` — PR #XXX +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) +- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX + +All instances must remain identical. + +## Out of Scope + +- Content changes to other skills +- Skill deletion + +## Acceptance Criteria + +- [ ] Skill file exists in `.cursor/skills//SKILL.md` +- [ ] Skill file exists in `.codex/skills//SKILL.md` +- [ ] Both copies are identical (`diff` produces no output) +- [ ] Content matches all other repos +``` + +### 5c: Set Issue Type + +Set the issue type to **Task**: + +```bash +# Get issue node ID +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' + +# Set Issue Type to Task +gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "IT_kwDOBluP0c4BBYRC" }) { issue { id } } }' +``` + +### 5d: Add to davidsulitzer.com Project with "In Review" Status + +```bash +# Add to davidsulitzer.com project +gh project item-add 2 --owner 8Gaston8 --url + +# Get project item ID +gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' + +# Set Status to "In Review" +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "2895eb73" } }) { projectV2Item { id } } }' + +# Set Priority to Low (skill updates are low-risk housekeeping) +gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "9d5cb3c5" } }) { projectV2Item { id } } }' +``` + +### 5e: Verify Issue Configuration + +```bash +# Verify issue type +gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' +``` + +--- + +## Step 6: Create Pull Requests + +Create a PR for **every repo** that was changed: + +### 6a: Create the PR + +```bash +cd +gh pr create \ + --repo stepscode/ \ + --base develop \ + --head \ + --title "skill(): " \ + --body "" \ + --assignee 8Gaston8 +``` + +### 6b: PR Body Template + +```markdown +## Summary + +[Create / Update] the `` skill. + +## Changes + +- [List of changes] + +## Cross-Repo Sync + +This change is part of a cross-repo skill sync: +- `stepscode/davidsulitzer.com` — PR #XXX +- `8Gaston8/davidsulitzer.com` — PR #XXX + +All skill files are identical across repos. + +Closes # +``` + +> **Note:** Include `Closes #` in the PR body to auto-link and auto-close the issue when merged. For davidsulitzer.com PRs, omit this since there's no associated issue. + +### 6c: Mark PR as Ready for Review + +PRs created with `gh pr create` are ready for review by default (not draft). Verify: + +```bash +gh pr view --repo stepscode/ --json isDraft +``` + +If it's a draft, convert it: + +```bash +gh pr ready --repo stepscode/ +``` + +### 6d: Assign & Request Review (MANDATORY) + +Every PR must be assigned to Gaston and have Aviad as a reviewer. Use the GitHub GraphQL API: + +```bash +# 1. Get user node IDs (only needed once — cache these) +gh api graphql -f query='{ user(login: "8Gaston8") { id } }' +# → Use this ID for assignee +gh api graphql -f query='{ user(login: "aviadsteps") { id } }' +# → Use this ID for reviewer + +# 2. Assign Gaston to the PR +gh api graphql -f query='mutation { addAssigneesToAssignable(input: { assignableId: "", assigneeIds: [""] }) { assignable { ... on PullRequest { number } } } }' + +# 3. Request review from Aviad +gh api graphql -f query='mutation { requestReviews(input: { pullRequestId: "", userIds: [""] }) { pullRequest { number } } }' +``` + +> **To get the PR node ID:** +> ```bash +> gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' +> ``` + +> ⚠️ **Do this for EVERY PR created** — davidsulitzer.com, iOS, server, and tileserver. No exceptions. + +### 6e: Enable Auto-Merge (iOS Only) + +If the PR is in the **davidsulitzer.com** repo, enable auto-merge so it merges automatically once CI passes and the review is approved: + +```bash +gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash +``` + +> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on davidsulitzer.com, server, or tileserver PRs. + +### 6f: Link Issues to PRs + +For non-davidsulitzer.com repos, the `Closes #` in the PR body handles the link. Additionally, verify the connection: + +```bash +gh pr view --repo stepscode/ --json closingIssuesReferences +``` + +--- + +## Step 7: Final Verification + +### 7a: Content Sync Across All Repos + +Verify the skill content is identical across every repo where it was written: + +```bash +# Compare all instances (adjust paths based on which repos have the skill) +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md +# etc. +``` + +### 7b: Issue & PR Checklist + +For each non-davidsulitzer.com repo, verify: + +- [ ] GitHub issue exists with: + - [ ] Issue Type: Task (verified via GraphQL) + - [ ] Label: `fastlane` + - [ ] Assignee: @8Gaston8 + - [ ] Added to davidsulitzer.com project + - [ ] Status: "In Review" +- [ ] PR exists and is: + - [ ] Ready for review (not draft) + - [ ] Linked to the issue (`Closes #XXX`) + - [ ] Assignee: @8Gaston8 + +For the davidsulitzer.com: +- [ ] PR exists and is ready for review (no issue needed) + +### 7c: Share All Links with the User (MANDATORY) + +**You MUST always end by sharing every relevant link** so the user can review everything at a glance. Present the results as a clear summary with **full clickable URLs** — not just PR/issue numbers: + +```markdown +## Here are all the links: + +### Pull Requests +- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX +- **Server:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX +- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX + +### Issues (linked to PRs above) +- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- **Server:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX +- *(Monorepo: no issue needed)* + +### Summary Table +| Repo | PR | Issue | Status | Skill Synced | +|------|-----|-------|--------|-------------| +| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | +| davidsulitzer.com | [#XXX](url) | [#XXX](url) (In Review, fastlane) | Ready for Review | ✅ | +``` + +> ⚠️ **Do NOT skip this step.** The user needs all links in one place to review and merge. Always use full URLs, not just `#123` references. + +--- + +## Handling Special Cases + +### Renaming a Skill + +1. Create the new skill directory in both `.cursor/` and `.codex/` locations across ALL repos +2. Copy the content (with updated `name` field in frontmatter) +3. Delete the old skill directory from both locations across ALL repos +4. Create PRs for every affected repo (same issue/PR workflow as above) + +### Deleting a Skill + +1. Remove from both directories across ALL repos where it exists +2. Create PRs for every affected repo (same issue/PR workflow as above) + +### Auditing All Skills for Sync + +If you suspect drift across repos: + +```bash +# Compare all skills within the current repo +diff -rq .cursor/skills/ .codex/skills/ + +# Compare a specific skill across repos via GitHub API +for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do + echo "=== $repo ===" + gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.content' 2>/dev/null | base64 -d | md5sum +done +``` + +All checksums should be identical. + +### Skill Only Relevant to Some Repos + +Not every skill belongs everywhere. Use judgment: + +| Skill Type | Typical Repos | +|-----------|---------------| +| Deployment skills | Repo being deployed | +| QA/testing skills | Repo being tested | +| Issue/project skills | All repos (agents work in any) | +| Release skills | iOS repo primarily | +| General workflow skills | All repos | +| Skill management (this skill!) | All repos | + +When in doubt, ask the user. + +--- + +## Reference: Current Skill Inventory + +| Skill | Purpose | Repos | +|-------|---------|-------| +| `deploy-backend-dev` | Deploy backend functions to dev environment | davidsulitzer.com | +| `deploy-backend-prod` | Deploy backend functions to production | davidsulitzer.com | +| `latest-release-notes` | Generate release notes for Slack | davidsulitzer.com | +| `new-issue` | Create GitHub issues with full research and project setup | davidsulitzer.com | +| `new-release` | Prepare iOS releases (branch, PR, regression testing, App Store) | davidsulitzer.com | +| `qa-start` | Comprehensive QA workflow for PRs (iOS + server) | davidsulitzer.com | +| `review-new-bugs` | Triage bug reports from Slack with screenshot analysis | davidsulitzer.com | +| `review-new-crashes` | Review and triage Crashlytics crash reports | davidsulitzer.com | +| `test-backend-changes-dev` | Test backend changes on dev environment | davidsulitzer.com | +| `test-backend-changes-local` | Test backend changes locally | davidsulitzer.com | +| `test-backend-changes-prod` | Test backend changes on production | davidsulitzer.com | +| `update-skill` | This skill — create or update skills across repos | all repos | +| `user-interview` | Simulate data-driven user interviews for product insights | davidsulitzer.com | + +> ⚠️ **Keep this table updated** when creating, deleting, or expanding skills to new repos! + +--- + +## Reference: Repo Quick Info + +| Repo | GitHub | Default Branch | Submodule Path | +|------|--------|----------------|----------------| +| Monorepo | `stepscode/davidsulitzer.com` | `develop` | `.` (root) | +| iOS | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | +| Server | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | +| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | + +--- + +## Reference: Project Field IDs + +| Field | Field ID | Key Options | +|-------|----------|-------------| +| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `01f8a8de`, Todo: `54ffad90`, Ready for Dev: `cac3311f`, In Development: `d7c83647`, Human QA: `6a5c111e`, In Review: `2895eb73`, Done: `782fbd7c` | +| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5` | + +--- + +## Notes + +- **Skill names must be kebab-case** — lowercase, words separated by hyphens (e.g., `deploy-backend-dev`, not `deployBackendDev`) +- **One SKILL.md per skill** — each skill lives in its own directory with a single `SKILL.md` file +- **Skills are version-controlled** — every change goes through git with proper branching, issues, and PRs +- **Both `.cursor/` and `.codex/` must always be in sync** within each repo +- **All repos must be in sync** with each other for shared skills +- **Monorepo gets PRs but NO issues** — only the subrepos (iOS, server, tileserver) get issues +- **Issues get "In Review" status and "fastlane" label** — skill updates are low-friction, ship fast +- **PRs are always Ready for Review** — never left as draft +- **Test after creating** — always suggest the user tests the skill with a real invocation diff --git a/.cursor/skills/user-interview/SKILL.md b/.cursor/skills/user-interview/SKILL.md new file mode 100644 index 0000000..86c3f6a --- /dev/null +++ b/.cursor/skills/user-interview/SKILL.md @@ -0,0 +1,628 @@ +--- +name: user-interview +description: Simulate data-driven user interviews for product insights, feature feedback, or design testing +disable-model-invocation: true +--- +# user-interview + +Simulate realistic user interviews grounded in real product data. Adapts to three modes depending on what you need. + +> **Default product scope: davidsulitzer.com.** All interviews, personas, and data queries should focus on the davidsulitzer.com product (local discovery, smart search, curated spots) unless the user explicitly asks about Gluten Free Eats (GFE). If the user mentions GFE, celiac, gluten-free, or dietary safety, switch to GFE-specific personas and data. When in doubt, ask. + +**Usage examples:** +- `@user-interview` — "I want to understand why trial users churn" +- `@user-interview` — "How would users react to a weekly recommendation email?" +- `@user-interview` — "Here's the new onboarding design — simulate a user going through it" + +--- + +## Step 1: Determine the Interview Mode + +Based on what the user asks, pick the right mode: + +| Mode | Trigger | Purpose | +|------|---------|---------| +| **Exploratory** | Open-ended question, no specific feature | Understand user segments, generate hypotheses | +| **Feature Feedback** | Specific feature or product change mentioned | Simulate reactions to a proposed change | +| **Design Testing** | Design/screenshot/mockup provided or referenced | Simulate users experiencing a specific flow | + +> If unclear, ask: "Are you exploring broadly, testing a specific feature idea, or validating a design?" + +--- + +## Step 2: Gather Real Data + +**This is what makes the interviews valuable.** Before simulating ANYTHING, pull real data from every available source. + +> ### ⚠️ Cloud Agent? Use APIs Instead of MCP +> +> If you are running as a **cloud agent** (headless environment, no MCP tools available), you **MUST** use REST APIs with credentials from environment variable secrets instead of MCP tools. All required API keys are available via **Cursor Dashboard → Cloud Agents → Secrets** and are injected as environment variables. +> +> | Data Source | MCP Tool (Local) | Cloud Agent Alternative | Secret / Env Var | +> |-------------|-------------------|------------------------|------------------| +> | **Mixpanel** | `run_segmentation_query` etc. | [Mixpanel Query API](https://developer.mixpanel.com/reference/query-api) | `MIXPANEL_API_SECRET` | +> | **MongoDB** | Atlas MCP tools | [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/app-services/data-api/) | `MONGODB_ATLAS_API_KEY`, `MONGODB_APP_ID` | +> | **Crisp** | Shell (API) | [Crisp REST API](https://docs.crisp.chat/references/rest-api/v1/) | `CRISP_API_ID`, `CRISP_API_KEY` | +> | **App Store** | Shell (JWT) | [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi) | `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_PRIVATE_KEY` | +> | **Slack** | Slack MCP | [Slack Web API](https://api.slack.com/web) | `SLACK_BOT_TOKEN` | +> +> **Important:** The local `.env` file at `davidsulitzer.com/api/.env` is **NOT available** in cloud agent environments. Always use secrets. If a secret is missing, ask the user to add it in the Cursor Dashboard. +> +> Example (Mixpanel segmentation in cloud): +> ```bash +> curl -X POST "https://mixpanel.com/api/2.0/segmentation" \ +> -u "$MIXPANEL_API_SECRET:" \ +> -d '{"event": "app_search_initiated", "from_date": "2026-01-01", "to_date": "2026-02-09"}' +> ``` +> +> Example (MongoDB Atlas Data API in cloud): +> ```bash +> curl -X POST "https://data.mongodb-api.com/app/$MONGODB_APP_ID/endpoint/data/v1/action/find" \ +> -H "api-key: $MONGODB_ATLAS_API_KEY" \ +> -H "Content-Type: application/json" \ +> -d '{"dataSource": "production", "database": "prod", "collection": "userFeedback", "filter": {}, "limit": 50}' +> ``` +> +> **When a data source is inaccessible:** Don't silently skip it. Explicitly note in the report: "Could not access [source] — [reason]. Confidence scores adjusted accordingly." + +### 2a. Mixpanel — Behavioral Data (Project ID: ) + +Pull data relevant to the interview topic. Choose from: + +**Segmentation** — Understand user behavior patterns: +``` +run_segmentation_query with project_id: +``` +Useful queries: +- Event frequency by user cohort (e.g., how often do trial users search?) +- Feature usage distribution (e.g., what % of users save places?) +- Drop-off points in key flows + +**Funnels** — Understand conversion: +``` +run_funnels_query with project_id: +``` +Useful funnels: +- Pricing Screen → Trial Start → Trial Converted +- App Open → Search → Place Profile View → Save +- Trial Start → Session 2 → Session 3 (retention) + +**Retention** — Understand stickiness: +``` +run_retention_query with project_id: +``` +Useful queries: +- Day 1 / Day 3 / Day 7 retention by cohort +- Retention by acquisition source +- Feature-correlated retention (users who saved vs didn't) + +**Frequency** — Understand usage patterns: +``` +run_frequency_query with project_id: +``` +Useful queries: +- Sessions per week distribution +- Search frequency during trial +- Save frequency by subscription status + +**User Properties** — Understand who they are: +``` +get_property_values with project_id: , resource_type: "User" +``` +Key properties to check: +- `$country_code`, `$city` — geography +- `product` — which product they use (filter for davidsulitzer.com by default) +- `abi_onboarding_type` — how they arrived +- `gfe_sensitivity` — GFE sensitivity level (only relevant if interviewing GFE users) + +### 2b. MongoDB Atlas — User & Content Data (Database: prod) + +Pull data relevant to the interview topic: + +**User feedback & reviews:** +``` +Collection: userFeedback — Direct user feedback +Collection: reviews — Place reviews (shows what users value) +Collection: userState — Subscription/engagement state +``` + +**Engagement signals:** +``` +Collection: saveListItems — What users save (investment behavior) +Collection: stepLikes — What users like +Collection: mapfollowers — Community engagement +Collection: viewsCount — Browsing patterns +``` + +**Content quality:** +``` +Collection: steps — Places/locations in the app +Collection: maps — Available maps +Collection: details — Place details (what info we show) +``` + +**Churn signals:** +``` +Collection: userFeedback — Look for cancellation feedback +Collection: userSubscriptionBypassesForPremiumMaps — Trial behavior +``` + +### 2c. App Store Reviews + +Pull recent App Store reviews using the App Store Connect API: + +```bash +# Generate JWT token for App Store Connect +source .env + +# Get recent reviews (requires JWT auth — see App Store Connect API docs) +# Look for: complaints, praise, feature requests, frustrations +``` + +Alternative: Search the web for recent davidsulitzer.com reviews: +``` +Search: "davidsulitzer.com app" review site:apps.apple.com OR site:play.google.com +``` + +### 2d. Support Conversations — Crisp + +Check recent support conversations for common themes: + +```bash +# Crisp API — get recent conversations +# Website ID: 51f0b9f1-c96c-452a-8e15-e437101d7d80 +# Look for: recurring complaints, feature requests, confusion points +``` + +### 2e. Slack — Internal Signals + +Check bug reports and feedback channels: +``` +Channel: #bugs-mobile-apps (CS93RCDSB) — User-reported issues via Julia +``` + +### 2f. Codebase — Product Context + +**Always** read relevant code to understand: +- The actual user flow being discussed (not assumptions!) +- What the user sees, taps, and experiences +- Feature flags and A/B tests currently running +- Paywall/subscription logic +- Onboarding flows and their variations + +Key areas: +- `davidsulitzer.com/Steps/AppFlows/` — All user flows +- `davidsulitzer.com/Steps/AppFlows/Paywall/` — Pricing/trial logic +- `davidsulitzer.com/Steps/AppFlows/Onboarding/` — First-time experience +- `davidsulitzer.com/Steps/AppFlows/Retention/` — Churn/cancellation flow +- `davidsulitzer.com/api/businessLogic/` — Backend logic for features + +### 2g. Competitor Landscape + +**Always** check what alternatives the simulated user has. Personas don't exist in a vacuum — they're comparing davidsulitzer.com to real alternatives. + +Search the web for recent competitor features and reviews: +``` +Search: "Google Maps" OR "Yelp" OR "TripAdvisor" OR "Foursquare" [topic relevant to interview] +Search: "best restaurant discovery app 2026" +``` + +Key competitors to benchmark against: +| Competitor | What They Offer | davidsulitzer.com's Differentiation | +|------------|----------------|----------------------| +| **Google Maps** | Universal coverage, reviews at scale, AI summaries | davidsulitzer.com: curated quality > quantity, community maps, smart vibe-based search | +| **Yelp** | Review volume, photos, reservations | davidsulitzer.com: less noise, no sponsored results, real community curation | +| **TripAdvisor** | Travel-focused, hotel + restaurant combo | davidsulitzer.com: local-first (not tourist-first), daily use not just travel | +| **Instagram/TikTok** | Social discovery, viral food content | davidsulitzer.com: searchable + saveable, not ephemeral scroll | +| **Friends/Word of mouth** | Trusted, personal, free | davidsulitzer.com: scales the "friend who knows a spot" feeling | + +**Use this in interviews:** When a persona says "I could find this elsewhere," push back with specifics. WHAT would they use? What's the actual experience on that alternative? Make the comparison concrete, not vague. + +### 2h. GitHub Projects — Roadmap & Product Context + +Check both GitHub Projects for relevant context on what's planned, in progress, or ideated: + +**davidsulitzer.com Project** (number: 2, ID: ``) — Active development work: +```bash +# List recent items with status +gh project item-list 2 --owner 8Gaston8 --limit 30 +``` +Look for: items In Development, Todo, or Done that relate to the interview topic. + +**davidsulitzer.com Ideation Lab** (number: 4, ID: `PVT_kwDOBluP0c4BOk4F`) — Ideas and experiments: +```bash +# List ideation items +gh project item-list 4 --owner 8Gaston8 --limit 30 +``` +Look for: feature ideas, experiments, and hypotheses that relate to the interview topic. + +Also search GitHub issues directly for relevant context: +```bash +gh search issues "" --repo 8Gaston8/davidsulitzer.com --state all +``` + +--- + +## Step 3: Build Data-Grounded Personas + +Based on the data gathered, build 2-4 personas that represent REAL behavioral segments. + +### Persona Template + +For each persona, specify: + +```markdown +## Persona: "[Name]" +**Segment:** [What real data segment they represent] +**Data basis:** [Specific Mixpanel/MongoDB data that shaped this persona] + +| Attribute | Details | Data Source | +|-----------|---------|-------------| +| Demographics | [Age, location, lifestyle] | Mixpanel geo data | +| Behavior pattern | [How they use the app — frequency, features, timing] | Mixpanel events | +| Subscription status | [Trial, converted, churned, never started] | MongoDB userState | +| Key actions | [Searches, saves, visits, reviews] | Mixpanel + MongoDB | +| Pain points | [From reviews, feedback, support tickets] | App Store + Crisp | +| Motivation | [Why they downloaded, what they hope for] | Onboarding data | +``` + +### Persona Selection by Mode + +| Mode | Personas to Build | +|------|-------------------| +| **Exploratory** | Cover 3-4 segments: a converter, a churner, a power user, and an edge case | +| **Feature Feedback** | 2-3 personas most affected by the proposed feature | +| **Design Testing** | 2 personas: one tech-savvy, one less experienced with apps | + +--- + +## Step 4: Run the Simulated Interviews + +### Interview Structure + +Each interview should follow this format: + +1. **Context setting** — Who they are, how they found the app +2. **Experience narrative** — Walk through their actual journey (grounded in real product flow!) +3. **Key moment** — The critical point where they converted/churned/got stuck +4. **Probing questions** — Deep dive into motivations and frustrations +5. **Forward-looking** — What would change their behavior + +### Mode-Specific Interview Approaches + +#### Exploratory Mode +- Open-ended questions about their experience +- Focus on emotional journey, not just functional +- Probe for unmet needs and surprising behaviors +- Ask: "What would make this indispensable to you?" + +#### Feature Feedback Mode +- Describe the proposed feature mid-interview +- Capture initial reaction (excitement, confusion, indifference) +- Probe for edge cases: "What if [scenario]?" +- Ask: "Would this change how often you use the app?" +- Ask: "Would this have changed your decision to [subscribe/cancel]?" + +#### Design Testing Mode +- Walk the persona through the design step by step +- Capture first impressions at each screen/state +- Note confusion points: "What do you think this button does?" +- Test comprehension: "What would you do next?" +- Identify emotional reactions: excitement, trust, skepticism, frustration + +### Realism Guidelines + +- **Use natural speech** — contractions, hesitation, tangents (real people don't speak in bullet points) +- **Include "I don't know" moments** — real users aren't always articulate about their needs +- **Show contradictions** — people say one thing and do another (e.g., "I love discovering places" but saves 0 places) +- **Ground in ACTUAL product behavior** — reference real screens, real flows, real pricing ($12/month) +- **Vary emotional states** — some users are enthusiastic, some are apathetic, some are frustrated +- **Inject real user quotes** — When you pulled App Store reviews, Crisp tickets, or MongoDB feedback in Step 2, weave those ACTUAL words into the simulated persona's dialogue. Mark them clearly: + + > **Example:** The persona says *"I don't use it often enough to justify paying"* — and you note: `[echoes real churn reason: "dont_use_often_enough" — selected by X% of cancelers]` + + This makes the simulation tangibly grounded. If you found a 1-star review saying "I searched for brunch and got 3 results," have the persona express that frustration in their own words. Tag it: + + > `[mirrors App Store review, 2 stars, Jan 2026: "searched for brunch spots and barely got any results"]` + + **The goal:** Anyone reading the simulation can immediately see which parts are data-backed and which are AI extrapolation. + +--- + +## Step 5: Synthesize Insights + +After running all interviews, produce a structured synthesis: + +### Comparison Table + +```markdown +| Factor | [Persona 1] | [Persona 2] | [Persona 3] | +|--------|-------------|-------------|-------------| +| First impression | ... | ... | ... | +| Key value moment | ... | ... | ... | +| Biggest friction | ... | ... | ... | +| Would they pay? | ... | ... | ... | +| Usage frequency | ... | ... | ... | +``` + +### Hypotheses (with Confidence Scoring) + +Generate 3-5 testable hypotheses based on the interviews. **Every hypothesis MUST have a confidence level** so the user knows what to trust vs. what to validate first: + +| Confidence | Meaning | When to Use | +|------------|---------|-------------| +| 🟢 **High** | Backed by real data (Mixpanel numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | +| 🟡 **Medium** | Supported by qualitative signals (support tickets, a few reviews, codebase patterns) | Some real-world backing but not statistically rigorous | +| 🔴 **Low** | Speculative — AI extrapolation, no direct data found | Plausible reasoning but no evidence yet; validate first | + +```markdown +| # | Hypothesis | Confidence | Supporting Evidence | How to Validate | +|---|-----------|------------|-------------------|-----------------| +| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [Mixpanel query / real interview question / A/B test] | +``` + +> **Rule:** If more than half your hypotheses are 🔴, explicitly call that out: "Most of these insights are speculative — real user interviews should be the priority before acting on them." + +### Impact on Key Metrics + +Connect insights back to Q1 2026 goals: + +**North Star:** $1M ARR for davidsulitzer.com + +| Metric | Current | Insight Impact | Suggested Action | +|--------|---------|---------------|-----------------| +| Pricing → Trial Start (goal: 20-25%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Trial → Converted (goal: 35-40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Activation Rate (goal: 60%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Early Retention (goal: 40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Investment Rate (goal: 10%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | + +--- + +## Step 6: Generate Actionable Outputs + +Based on the mode, produce the right deliverable: + +### Exploratory Mode → Interview Guide +Generate a real-user interview question guide: + +```markdown +## Interview Guide: [Topic] +**Target segment:** [Who to recruit] +**Session length:** 30 min + +### Warm-up (5 min) +1. [Question about their life/context] +2. [Question about how they find places today] + +### Core Questions (20 min) +3. [Probing question derived from simulation insight #1] +4. [Probing question derived from simulation insight #2] +... + +### Wrap-up (5 min) +8. "If you could change one thing about davidsulitzer.com..." +9. "Would you recommend davidsulitzer.com to a friend? Why/why not?" +``` + +### Feature Feedback Mode → Decision Brief +```markdown +## Feature Decision Brief: [Feature Name] + +### Verdict: 🟢 Build / 🟡 Iterate / 🔴 Rethink + +**Summary:** [One paragraph] + +### User Reception +| Persona | Reaction | Risk | +|---------|----------|------| +| ... | ... | ... | + +### Recommendations +1. [If building: specific implementation suggestions] +2. [If iterating: what to change first] +3. [If rethinking: alternative approaches] + +### Questions to Validate with Real Users +1. [Most critical assumption to test] +2. ... +``` + +### Design Testing Mode → Usability Report +```markdown +## Usability Report: [Design Name] + +### Overall Score: X/5 + +### Screen-by-Screen Findings +| Screen | Comprehension | Emotional Reaction | Issues Found | +|--------|--------------|-------------------|--------------| +| ... | ... | ... | ... | + +### Critical Issues (Fix Before Ship) +1. [Issue + which persona hit it + severity] + +### Recommendations +1. [Specific design change] +2. ... +``` + +--- + +## Step 7: Generate HTML Report + +**MANDATORY.** In addition to any markdown output, you MUST generate a self-contained HTML report file that makes the entire interview simulation comfortable to read in a browser. This is the primary deliverable — the thing Gaston will actually read. + +### Requirements +- **Single file, zero dependencies** — all CSS must be inline in a ` + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + +``` + +> **Quality bar:** The HTML report should feel like a premium product research document — the kind of thing you'd send to a CEO and they'd actually read cover to cover. Not a markdown-to-HTML conversion, but a designed reading experience. + +--- + +## Step 8: Offer Follow-ups + +> **Reminder:** Before offering follow-ups, make sure you've committed and pushed the HTML report file if running in a git-enabled environment. + +After presenting results, suggest next steps: + +- **"Want me to simulate another persona?"** — Different segment, different perspective +- **"Want me to draft a GitHub issue for any of these insights?"** — Using `/new-issue` skill +- **"Want me to add an idea to the davidsulitzer.com Ideation Lab?"** — Track hypotheses as ideation items +- **"Want me to pull more specific data on any of these findings?"** — Deeper Mixpanel/MongoDB dive +- **"Should I run this for GFE too?"** — Same framework, but switches to GFE-specific personas, safety concerns, celiac sensitivity levels, and dietary-focused data + +--- + +## Reference: Available Data Sources + +| Source | Tool | What It Provides | +|--------|------|-----------------| +| **Mixpanel** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | +| **MongoDB** | Atlas MCP (database: prod) | Users, reviews, feedback, saves, engagement, subscription state | +| **App Store** | Shell (App Store Connect API) | User reviews, ratings, feature requests | +| **Crisp** | Shell (API) | Support conversations, common complaints | +| **Slack** | Slack MCP | Bug reports (#bugs-mobile-apps), internal discussions | +| **GitHub Projects** | `gh project` CLI | davidsulitzer.com project (dev work), davidsulitzer.com Ideation Lab (ideas/experiments) | +| **GitHub Issues** | `gh search issues` | Feature requests, bugs, past decisions | +| **Codebase** | File tools | Actual product flows, screens, logic | +| **Web** | Web search/fetch | Competitor analysis, market context, davidsulitzer.com public pages | + +--- + +## Reference: Key Metrics (Q1 2026) + +**North Star:** $1M ARR for davidsulitzer.com + +### Top-Down Metrics (Pricing Funnel) +| Metric | Goal | +|--------|------| +| Pricing Screen → Trial Start | 20-25% | +| Trial Start → Trial Converted | 35-40% | + +### Leading Indicators +| Metric | Goal | Definition | +|--------|------|------------| +| Activation Rate | 60% | % of new trial users who view 5+ place profiles from multi-category searches in first 7 days | +| Early Retention | 40% | % of trial users with 3+ sessions in first 3 days | +| Investment Rate | 10% | % of trial users who save at least 1 place in first 7 days | + +--- + +## Reference: davidsulitzer.com Primary Personas + +These are the baseline personas. Data should refine and override these: + +| Persona | Core Need | Key Behavior | +|---------|-----------|-------------| +| **Decision-Fatigued Foodie** | Just tell me where to go | Searches with specific intent, wants fast answers | +| **Urban Explorer** | Discover hidden gems, feel like a local | Browses and saves, wants to impress friends | +| **Vegan User** | Plant-based focus, not afterthought | Cuisine-specific searches, community-oriented | +| **Date Planner** | Impressive, memorable spots | Plans ahead, cares about ambiance/vibe | +| **Frequent Traveler** | Local expertise in unfamiliar cities | Location-based searches, pre-trip planning | +| **Busy Professional** | Fast, reliable answers | Efficiency over exploration, specific intent | + +--- + +## Reference: Churn Reasons (from Retention Flow) + +Real cancellation reasons captured in-app: + +| Reason | Code | Frequency Insight | +|--------|------|-------------------| +| Too expensive | `too_expensive` | Check Mixpanel for distribution | +| Didn't find relevant places | `didnt_find_relevant_places` | Check Mixpanel for distribution | +| Don't use often enough | `dont_use_often_enough` | Check Mixpanel for distribution | +| Not enough places | `not_enough_places` | Check Mixpanel for distribution | +| Safety concerns | `safety_concerns` | Check Mixpanel for distribution | +| Technical issues/bugs | `technical_issues_bugs` | Check Mixpanel for distribution | +| Other (free text) | `other` | Check MongoDB userFeedback | + +--- + +## Notes + +- **Never fabricate data.** If you can't pull real numbers, say "I couldn't access this — here's my best estimate based on [source]." +- **Always read the actual codebase** before describing product flows. Getting the flow wrong undermines the entire simulation. +- **Simulations are hypotheses, not truths.** Always frame outputs as "worth validating" not "definitely true." +- **Real interviews > simulated interviews.** The goal is to sharpen questions and challenge assumptions, not to replace talking to humans. +- **Adapt tone to Gaston** — Keep it casual, witty, and insightful. This is a thinking tool, not a corporate report. From 9fe6a2482d1c8da9f6801310d770f801c4141d8a Mon Sep 17 00:00:00 2001 From: David Sulitzer Date: Tue, 17 Feb 2026 17:42:05 +0200 Subject: [PATCH 2/3] Remove Mixpanel-specific guidance from migrated skills. Replace Mixpanel references with provider-agnostic analytics wording and make release analytics steps optional so the skill set fits davidsulitzer.com without Mixpanel dependencies. --- .codex/skills/new-issue/SKILL.md | 4 +- .codex/skills/new-release/SKILL.md | 4 +- .codex/skills/refine-spec/SKILL.md | 4 +- .codex/skills/release-shipped/SKILL.md | 22 +++++----- .codex/skills/user-interview/SKILL.md | 58 ++++++++++++------------- .cursor/skills/new-issue/SKILL.md | 4 +- .cursor/skills/new-release/SKILL.md | 4 +- .cursor/skills/refine-spec/SKILL.md | 4 +- .cursor/skills/release-shipped/SKILL.md | 22 +++++----- .cursor/skills/user-interview/SKILL.md | 58 ++++++++++++------------- 10 files changed, 92 insertions(+), 92 deletions(-) diff --git a/.codex/skills/new-issue/SKILL.md b/.codex/skills/new-issue/SKILL.md index 49ebe38..aa17e97 100644 --- a/.codex/skills/new-issue/SKILL.md +++ b/.codex/skills/new-issue/SKILL.md @@ -132,7 +132,7 @@ When creating a **bug** issue, do this additional research: 4. **Identify dependencies** — What other systems does this code touch? - Feature flags that gate the behavior - API endpoints involved (grep for route definitions) - - Third-party services (Mixpanel, RevenueCat, Firebase, etc.) + - Third-party services (analytics, RevenueCat, Firebase, etc.) - Caching layers (tileserver, CDN, local cache) 5. **Gather reproduction context:** @@ -251,7 +251,7 @@ For each issue, include: | **Feature Flag** | New features or risky changes that should be gated | | **Reference Implementation** | When similar patterns/PRs exist to follow | | **Risk/Rollback** | Medium+ risk changes that could cause issues | -| **Analytics Requirements** | When new Mixpanel events are needed | +| **Analytics Requirements** | When new analytics events are needed | | **Documentation Updates** | When README, API docs, or agent rules need updating (see Step 7) | ### Section Templates diff --git a/.codex/skills/new-release/SKILL.md b/.codex/skills/new-release/SKILL.md index 699418d..6d73e0e 100644 --- a/.codex/skills/new-release/SKILL.md +++ b/.codex/skills/new-release/SKILL.md @@ -5,7 +5,7 @@ disable-model-invocation: true --- # new-release -Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, Mixpanel, stability score, issue tracking), see `/release-shipped`. +Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, analytics, stability score, issue tracking), see `/release-shipped`. --- @@ -165,4 +165,4 @@ Trial Start → Trial Converted (goal: 35-40%) ## Next Step -When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate Mixpanel, and close out project tracking. +When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate analytics, and close out project tracking. diff --git a/.codex/skills/refine-spec/SKILL.md b/.codex/skills/refine-spec/SKILL.md index 4e56004..a196e74 100644 --- a/.codex/skills/refine-spec/SKILL.md +++ b/.codex/skills/refine-spec/SKILL.md @@ -103,7 +103,7 @@ rg -l "" davidsulitzer.com/api/test/ 2>/dev/null Look for: - Feature flags that gate the behavior - API endpoints involved (grep for route definitions) -- Third-party services (Mixpanel, RevenueCat, Firebase, etc.) +- Third-party services (analytics, RevenueCat, Firebase, etc.) - Caching layers - Cross-repo dependencies (iOS ↔ Server ↔ Tileserver) @@ -138,7 +138,7 @@ Compare the existing issue body against the required and conditional sections fr | **Feature Flag** | New features, risky changes | ✅/❌ | | | **Reference Implementation** | Similar patterns exist | ✅/❌ | | | **Risk/Rollback** | Medium+ risk changes | ✅/❌ | | -| **Analytics Requirements** | New Mixpanel events | ✅/❌ | | +| **Analytics Requirements** | New analytics events | ✅/❌ | | | **Documentation Updates** | README, API docs, agent rules | ✅/❌ | | > Present this gap table to the PM. Let them decide which missing sections are relevant before you start writing. diff --git a/.codex/skills/release-shipped/SKILL.md b/.codex/skills/release-shipped/SKILL.md index 0694ec6..ab001d8 100644 --- a/.codex/skills/release-shipped/SKILL.md +++ b/.codex/skills/release-shipped/SKILL.md @@ -1,11 +1,11 @@ --- name: release-shipped -description: Post-Apple-approval steps — publish release, Mixpanel annotation, close out tracking, assign iOS versions to server & tile-server issues +description: Post-Apple-approval steps — publish release, analytics annotation, close out tracking, assign iOS versions to server & tile-server issues disable-model-invocation: true --- # release-shipped -Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating Mixpanel, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. +Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating analytics, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. > **Prerequisite:** The `/new-release` skill must have been completed first — the draft GitHub release, tag, and PR should already exist. @@ -27,14 +27,14 @@ gh release edit VERSION --repo 8Gaston8/davidsulitzer.com --draft=false --- -## Step 2: Add Mixpanel Annotation +## Step 2: Record Release Metrics (Optional) -Annotate the release in Mixpanel with the **actual release date** (when Apple made it live, not when it was submitted): +If you use a product analytics tool, record the release timestamp (actual App Store live date) and attach a short note for future analysis: ```bash curl -X POST \ - 'https://mixpanel.com/api/app/projects//annotations' \ - --user "$MIXPANEL_SERVICE_ACCOUNT_USERNAME:$MIXPANEL_SERVICE_ACCOUNT_SECRET" \ + '' \ + --user "$ANALYTICS_SERVICE_ACCOUNT_USERNAME:$ANALYTICS_SERVICE_ACCOUNT_SECRET" \ -H 'Content-Type: application/json' \ -d '{ "description": "iOS VERSION released", @@ -43,13 +43,13 @@ curl -X POST \ }' ``` -> **Note:** `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set in your environment (from `.env` file at `davidsulitzer.com/api/.env`). +> **Note:** Set `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` in your environment if you use this optional step. --- -## Step 3: List New Mixpanel Events +## Step 3: List New Analytics Events (If Any) -If any new Mixpanel events were created as part of this release, list them so Gaston can prepare relevant Mixpanel dashboards or reports. +If any new analytics events were introduced in this release, list them so reporting and dashboards can be updated. --- @@ -261,7 +261,7 @@ Also report any issues that could NOT be assigned (e.g., closed before the earli ## What NOT to Do -- **Don't run this before Apple approves** — the Mixpanel annotation timestamp must reflect the actual release date +- **Don't run this before Apple approves** — the analytics annotation timestamp must reflect the actual release date - **Don't forget to publish the GitHub release** — it stays as a draft until this skill is invoked - **Don't update issue versions for PRs merged before the previous release** — only PRs merged after the last tag - **Don't skip server/tile-server issues** — they ship independently but still need iOS Version assignment for timeline tracking @@ -274,5 +274,5 @@ Also report any issues that could NOT be assigned (e.g., closed before the earli - `/new-release` must have been completed (draft release, tag, and PR exist) - The app must be **approved and live** in the App Store -- Environment variables `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) +- Environment variables `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) - `gh` CLI must be authenticated with access to `8Gaston8/davidsulitzer.com`, `8Gaston8/davidsulitzer.com`, and `8Gaston8/davidsulitzer.com` repos diff --git a/.codex/skills/user-interview/SKILL.md b/.codex/skills/user-interview/SKILL.md index 86c3f6a..58b8685 100644 --- a/.codex/skills/user-interview/SKILL.md +++ b/.codex/skills/user-interview/SKILL.md @@ -40,7 +40,7 @@ Based on what the user asks, pick the right mode: > > | Data Source | MCP Tool (Local) | Cloud Agent Alternative | Secret / Env Var | > |-------------|-------------------|------------------------|------------------| -> | **Mixpanel** | `run_segmentation_query` etc. | [Mixpanel Query API](https://developer.mixpanel.com/reference/query-api) | `MIXPANEL_API_SECRET` | +> | **analytics** | `run_segmentation_query` etc. | Your analytics provider API docs | `ANALYTICS_API_SECRET` | > | **MongoDB** | Atlas MCP tools | [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/app-services/data-api/) | `MONGODB_ATLAS_API_KEY`, `MONGODB_APP_ID` | > | **Crisp** | Shell (API) | [Crisp REST API](https://docs.crisp.chat/references/rest-api/v1/) | `CRISP_API_ID`, `CRISP_API_KEY` | > | **App Store** | Shell (JWT) | [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi) | `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_PRIVATE_KEY` | @@ -48,10 +48,10 @@ Based on what the user asks, pick the right mode: > > **Important:** The local `.env` file at `davidsulitzer.com/api/.env` is **NOT available** in cloud agent environments. Always use secrets. If a secret is missing, ask the user to add it in the Cursor Dashboard. > -> Example (Mixpanel segmentation in cloud): +> Example (analytics segmentation in cloud): > ```bash -> curl -X POST "https://mixpanel.com/api/2.0/segmentation" \ -> -u "$MIXPANEL_API_SECRET:" \ +> curl -X POST "" \ +> -u "$ANALYTICS_API_SECRET:" \ > -d '{"event": "app_search_initiated", "from_date": "2026-01-01", "to_date": "2026-02-09"}' > ``` > @@ -65,13 +65,13 @@ Based on what the user asks, pick the right mode: > > **When a data source is inaccessible:** Don't silently skip it. Explicitly note in the report: "Could not access [source] — [reason]. Confidence scores adjusted accordingly." -### 2a. Mixpanel — Behavioral Data (Project ID: ) +### 2a. Product Analytics (Optional) — Behavioral Data (Project ID: ) Pull data relevant to the interview topic. Choose from: **Segmentation** — Understand user behavior patterns: ``` -run_segmentation_query with project_id: +run_segmentation_query with project_id: ``` Useful queries: - Event frequency by user cohort (e.g., how often do trial users search?) @@ -80,7 +80,7 @@ Useful queries: **Funnels** — Understand conversion: ``` -run_funnels_query with project_id: +run_funnels_query with project_id: ``` Useful funnels: - Pricing Screen → Trial Start → Trial Converted @@ -89,7 +89,7 @@ Useful funnels: **Retention** — Understand stickiness: ``` -run_retention_query with project_id: +run_retention_query with project_id: ``` Useful queries: - Day 1 / Day 3 / Day 7 retention by cohort @@ -98,7 +98,7 @@ Useful queries: **Frequency** — Understand usage patterns: ``` -run_frequency_query with project_id: +run_frequency_query with project_id: ``` Useful queries: - Sessions per week distribution @@ -107,7 +107,7 @@ Useful queries: **User Properties** — Understand who they are: ``` -get_property_values with project_id: , resource_type: "User" +get_property_values with project_id: , resource_type: "User" ``` Key properties to check: - `$country_code`, `$city` — geography @@ -254,14 +254,14 @@ For each persona, specify: ```markdown ## Persona: "[Name]" **Segment:** [What real data segment they represent] -**Data basis:** [Specific Mixpanel/MongoDB data that shaped this persona] +**Data basis:** [Specific analytics/MongoDB data that shaped this persona] | Attribute | Details | Data Source | |-----------|---------|-------------| -| Demographics | [Age, location, lifestyle] | Mixpanel geo data | -| Behavior pattern | [How they use the app — frequency, features, timing] | Mixpanel events | +| Demographics | [Age, location, lifestyle] | analytics geo data | +| Behavior pattern | [How they use the app — frequency, features, timing] | analytics events | | Subscription status | [Trial, converted, churned, never started] | MongoDB userState | -| Key actions | [Searches, saves, visits, reviews] | Mixpanel + MongoDB | +| Key actions | [Searches, saves, visits, reviews] | analytics + MongoDB | | Pain points | [From reviews, feedback, support tickets] | App Store + Crisp | | Motivation | [Why they downloaded, what they hope for] | Onboarding data | ``` @@ -351,14 +351,14 @@ Generate 3-5 testable hypotheses based on the interviews. **Every hypothesis MUS | Confidence | Meaning | When to Use | |------------|---------|-------------| -| 🟢 **High** | Backed by real data (Mixpanel numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | +| 🟢 **High** | Backed by real data (analytics numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | | 🟡 **Medium** | Supported by qualitative signals (support tickets, a few reviews, codebase patterns) | Some real-world backing but not statistically rigorous | | 🔴 **Low** | Speculative — AI extrapolation, no direct data found | Plausible reasoning but no evidence yet; validate first | ```markdown | # | Hypothesis | Confidence | Supporting Evidence | How to Validate | |---|-----------|------------|-------------------|-----------------| -| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [Mixpanel query / real interview question / A/B test] | +| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [analytics query / real interview question / A/B test] | ``` > **Rule:** If more than half your hypotheses are 🔴, explicitly call that out: "Most of these insights are speculative — real user interviews should be the priority before acting on them." @@ -371,11 +371,11 @@ Connect insights back to Q1 2026 goals: | Metric | Current | Insight Impact | Suggested Action | |--------|---------|---------------|-----------------| -| Pricing → Trial Start (goal: 20-25%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Trial → Converted (goal: 35-40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Activation Rate (goal: 60%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Early Retention (goal: 40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Investment Rate (goal: 10%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Pricing → Trial Start (goal: 20-25%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Trial → Converted (goal: 35-40%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Activation Rate (goal: 60%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Early Retention (goal: 40%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Investment Rate (goal: 10%) | [Pull from analytics] | [How insights relate] | [Specific change] | --- @@ -548,7 +548,7 @@ After presenting results, suggest next steps: - **"Want me to simulate another persona?"** — Different segment, different perspective - **"Want me to draft a GitHub issue for any of these insights?"** — Using `/new-issue` skill - **"Want me to add an idea to the davidsulitzer.com Ideation Lab?"** — Track hypotheses as ideation items -- **"Want me to pull more specific data on any of these findings?"** — Deeper Mixpanel/MongoDB dive +- **"Want me to pull more specific data on any of these findings?"** — Deeper analytics/MongoDB dive - **"Should I run this for GFE too?"** — Same framework, but switches to GFE-specific personas, safety concerns, celiac sensitivity levels, and dietary-focused data --- @@ -557,7 +557,7 @@ After presenting results, suggest next steps: | Source | Tool | What It Provides | |--------|------|-----------------| -| **Mixpanel** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | +| **analytics** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | | **MongoDB** | Atlas MCP (database: prod) | Users, reviews, feedback, saves, engagement, subscription state | | **App Store** | Shell (App Store Connect API) | User reviews, ratings, feature requests | | **Crisp** | Shell (API) | Support conversations, common complaints | @@ -609,12 +609,12 @@ Real cancellation reasons captured in-app: | Reason | Code | Frequency Insight | |--------|------|-------------------| -| Too expensive | `too_expensive` | Check Mixpanel for distribution | -| Didn't find relevant places | `didnt_find_relevant_places` | Check Mixpanel for distribution | -| Don't use often enough | `dont_use_often_enough` | Check Mixpanel for distribution | -| Not enough places | `not_enough_places` | Check Mixpanel for distribution | -| Safety concerns | `safety_concerns` | Check Mixpanel for distribution | -| Technical issues/bugs | `technical_issues_bugs` | Check Mixpanel for distribution | +| Too expensive | `too_expensive` | Check analytics for distribution | +| Didn't find relevant places | `didnt_find_relevant_places` | Check analytics for distribution | +| Don't use often enough | `dont_use_often_enough` | Check analytics for distribution | +| Not enough places | `not_enough_places` | Check analytics for distribution | +| Safety concerns | `safety_concerns` | Check analytics for distribution | +| Technical issues/bugs | `technical_issues_bugs` | Check analytics for distribution | | Other (free text) | `other` | Check MongoDB userFeedback | --- diff --git a/.cursor/skills/new-issue/SKILL.md b/.cursor/skills/new-issue/SKILL.md index 49ebe38..aa17e97 100644 --- a/.cursor/skills/new-issue/SKILL.md +++ b/.cursor/skills/new-issue/SKILL.md @@ -132,7 +132,7 @@ When creating a **bug** issue, do this additional research: 4. **Identify dependencies** — What other systems does this code touch? - Feature flags that gate the behavior - API endpoints involved (grep for route definitions) - - Third-party services (Mixpanel, RevenueCat, Firebase, etc.) + - Third-party services (analytics, RevenueCat, Firebase, etc.) - Caching layers (tileserver, CDN, local cache) 5. **Gather reproduction context:** @@ -251,7 +251,7 @@ For each issue, include: | **Feature Flag** | New features or risky changes that should be gated | | **Reference Implementation** | When similar patterns/PRs exist to follow | | **Risk/Rollback** | Medium+ risk changes that could cause issues | -| **Analytics Requirements** | When new Mixpanel events are needed | +| **Analytics Requirements** | When new analytics events are needed | | **Documentation Updates** | When README, API docs, or agent rules need updating (see Step 7) | ### Section Templates diff --git a/.cursor/skills/new-release/SKILL.md b/.cursor/skills/new-release/SKILL.md index 699418d..6d73e0e 100644 --- a/.cursor/skills/new-release/SKILL.md +++ b/.cursor/skills/new-release/SKILL.md @@ -5,7 +5,7 @@ disable-model-invocation: true --- # new-release -Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, Mixpanel, stability score, issue tracking), see `/release-shipped`. +Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, analytics, stability score, issue tracking), see `/release-shipped`. --- @@ -165,4 +165,4 @@ Trial Start → Trial Converted (goal: 35-40%) ## Next Step -When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate Mixpanel, and close out project tracking. +When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate analytics, and close out project tracking. diff --git a/.cursor/skills/refine-spec/SKILL.md b/.cursor/skills/refine-spec/SKILL.md index 4e56004..a196e74 100644 --- a/.cursor/skills/refine-spec/SKILL.md +++ b/.cursor/skills/refine-spec/SKILL.md @@ -103,7 +103,7 @@ rg -l "" davidsulitzer.com/api/test/ 2>/dev/null Look for: - Feature flags that gate the behavior - API endpoints involved (grep for route definitions) -- Third-party services (Mixpanel, RevenueCat, Firebase, etc.) +- Third-party services (analytics, RevenueCat, Firebase, etc.) - Caching layers - Cross-repo dependencies (iOS ↔ Server ↔ Tileserver) @@ -138,7 +138,7 @@ Compare the existing issue body against the required and conditional sections fr | **Feature Flag** | New features, risky changes | ✅/❌ | | | **Reference Implementation** | Similar patterns exist | ✅/❌ | | | **Risk/Rollback** | Medium+ risk changes | ✅/❌ | | -| **Analytics Requirements** | New Mixpanel events | ✅/❌ | | +| **Analytics Requirements** | New analytics events | ✅/❌ | | | **Documentation Updates** | README, API docs, agent rules | ✅/❌ | | > Present this gap table to the PM. Let them decide which missing sections are relevant before you start writing. diff --git a/.cursor/skills/release-shipped/SKILL.md b/.cursor/skills/release-shipped/SKILL.md index 0694ec6..ab001d8 100644 --- a/.cursor/skills/release-shipped/SKILL.md +++ b/.cursor/skills/release-shipped/SKILL.md @@ -1,11 +1,11 @@ --- name: release-shipped -description: Post-Apple-approval steps — publish release, Mixpanel annotation, close out tracking, assign iOS versions to server & tile-server issues +description: Post-Apple-approval steps — publish release, analytics annotation, close out tracking, assign iOS versions to server & tile-server issues disable-model-invocation: true --- # release-shipped -Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating Mixpanel, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. +Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating analytics, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. > **Prerequisite:** The `/new-release` skill must have been completed first — the draft GitHub release, tag, and PR should already exist. @@ -27,14 +27,14 @@ gh release edit VERSION --repo 8Gaston8/davidsulitzer.com --draft=false --- -## Step 2: Add Mixpanel Annotation +## Step 2: Record Release Metrics (Optional) -Annotate the release in Mixpanel with the **actual release date** (when Apple made it live, not when it was submitted): +If you use a product analytics tool, record the release timestamp (actual App Store live date) and attach a short note for future analysis: ```bash curl -X POST \ - 'https://mixpanel.com/api/app/projects//annotations' \ - --user "$MIXPANEL_SERVICE_ACCOUNT_USERNAME:$MIXPANEL_SERVICE_ACCOUNT_SECRET" \ + '' \ + --user "$ANALYTICS_SERVICE_ACCOUNT_USERNAME:$ANALYTICS_SERVICE_ACCOUNT_SECRET" \ -H 'Content-Type: application/json' \ -d '{ "description": "iOS VERSION released", @@ -43,13 +43,13 @@ curl -X POST \ }' ``` -> **Note:** `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set in your environment (from `.env` file at `davidsulitzer.com/api/.env`). +> **Note:** Set `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` in your environment if you use this optional step. --- -## Step 3: List New Mixpanel Events +## Step 3: List New Analytics Events (If Any) -If any new Mixpanel events were created as part of this release, list them so Gaston can prepare relevant Mixpanel dashboards or reports. +If any new analytics events were introduced in this release, list them so reporting and dashboards can be updated. --- @@ -261,7 +261,7 @@ Also report any issues that could NOT be assigned (e.g., closed before the earli ## What NOT to Do -- **Don't run this before Apple approves** — the Mixpanel annotation timestamp must reflect the actual release date +- **Don't run this before Apple approves** — the analytics annotation timestamp must reflect the actual release date - **Don't forget to publish the GitHub release** — it stays as a draft until this skill is invoked - **Don't update issue versions for PRs merged before the previous release** — only PRs merged after the last tag - **Don't skip server/tile-server issues** — they ship independently but still need iOS Version assignment for timeline tracking @@ -274,5 +274,5 @@ Also report any issues that could NOT be assigned (e.g., closed before the earli - `/new-release` must have been completed (draft release, tag, and PR exist) - The app must be **approved and live** in the App Store -- Environment variables `MIXPANEL_SERVICE_ACCOUNT_USERNAME` and `MIXPANEL_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) +- Environment variables `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) - `gh` CLI must be authenticated with access to `8Gaston8/davidsulitzer.com`, `8Gaston8/davidsulitzer.com`, and `8Gaston8/davidsulitzer.com` repos diff --git a/.cursor/skills/user-interview/SKILL.md b/.cursor/skills/user-interview/SKILL.md index 86c3f6a..58b8685 100644 --- a/.cursor/skills/user-interview/SKILL.md +++ b/.cursor/skills/user-interview/SKILL.md @@ -40,7 +40,7 @@ Based on what the user asks, pick the right mode: > > | Data Source | MCP Tool (Local) | Cloud Agent Alternative | Secret / Env Var | > |-------------|-------------------|------------------------|------------------| -> | **Mixpanel** | `run_segmentation_query` etc. | [Mixpanel Query API](https://developer.mixpanel.com/reference/query-api) | `MIXPANEL_API_SECRET` | +> | **analytics** | `run_segmentation_query` etc. | Your analytics provider API docs | `ANALYTICS_API_SECRET` | > | **MongoDB** | Atlas MCP tools | [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/app-services/data-api/) | `MONGODB_ATLAS_API_KEY`, `MONGODB_APP_ID` | > | **Crisp** | Shell (API) | [Crisp REST API](https://docs.crisp.chat/references/rest-api/v1/) | `CRISP_API_ID`, `CRISP_API_KEY` | > | **App Store** | Shell (JWT) | [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi) | `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_PRIVATE_KEY` | @@ -48,10 +48,10 @@ Based on what the user asks, pick the right mode: > > **Important:** The local `.env` file at `davidsulitzer.com/api/.env` is **NOT available** in cloud agent environments. Always use secrets. If a secret is missing, ask the user to add it in the Cursor Dashboard. > -> Example (Mixpanel segmentation in cloud): +> Example (analytics segmentation in cloud): > ```bash -> curl -X POST "https://mixpanel.com/api/2.0/segmentation" \ -> -u "$MIXPANEL_API_SECRET:" \ +> curl -X POST "" \ +> -u "$ANALYTICS_API_SECRET:" \ > -d '{"event": "app_search_initiated", "from_date": "2026-01-01", "to_date": "2026-02-09"}' > ``` > @@ -65,13 +65,13 @@ Based on what the user asks, pick the right mode: > > **When a data source is inaccessible:** Don't silently skip it. Explicitly note in the report: "Could not access [source] — [reason]. Confidence scores adjusted accordingly." -### 2a. Mixpanel — Behavioral Data (Project ID: ) +### 2a. Product Analytics (Optional) — Behavioral Data (Project ID: ) Pull data relevant to the interview topic. Choose from: **Segmentation** — Understand user behavior patterns: ``` -run_segmentation_query with project_id: +run_segmentation_query with project_id: ``` Useful queries: - Event frequency by user cohort (e.g., how often do trial users search?) @@ -80,7 +80,7 @@ Useful queries: **Funnels** — Understand conversion: ``` -run_funnels_query with project_id: +run_funnels_query with project_id: ``` Useful funnels: - Pricing Screen → Trial Start → Trial Converted @@ -89,7 +89,7 @@ Useful funnels: **Retention** — Understand stickiness: ``` -run_retention_query with project_id: +run_retention_query with project_id: ``` Useful queries: - Day 1 / Day 3 / Day 7 retention by cohort @@ -98,7 +98,7 @@ Useful queries: **Frequency** — Understand usage patterns: ``` -run_frequency_query with project_id: +run_frequency_query with project_id: ``` Useful queries: - Sessions per week distribution @@ -107,7 +107,7 @@ Useful queries: **User Properties** — Understand who they are: ``` -get_property_values with project_id: , resource_type: "User" +get_property_values with project_id: , resource_type: "User" ``` Key properties to check: - `$country_code`, `$city` — geography @@ -254,14 +254,14 @@ For each persona, specify: ```markdown ## Persona: "[Name]" **Segment:** [What real data segment they represent] -**Data basis:** [Specific Mixpanel/MongoDB data that shaped this persona] +**Data basis:** [Specific analytics/MongoDB data that shaped this persona] | Attribute | Details | Data Source | |-----------|---------|-------------| -| Demographics | [Age, location, lifestyle] | Mixpanel geo data | -| Behavior pattern | [How they use the app — frequency, features, timing] | Mixpanel events | +| Demographics | [Age, location, lifestyle] | analytics geo data | +| Behavior pattern | [How they use the app — frequency, features, timing] | analytics events | | Subscription status | [Trial, converted, churned, never started] | MongoDB userState | -| Key actions | [Searches, saves, visits, reviews] | Mixpanel + MongoDB | +| Key actions | [Searches, saves, visits, reviews] | analytics + MongoDB | | Pain points | [From reviews, feedback, support tickets] | App Store + Crisp | | Motivation | [Why they downloaded, what they hope for] | Onboarding data | ``` @@ -351,14 +351,14 @@ Generate 3-5 testable hypotheses based on the interviews. **Every hypothesis MUS | Confidence | Meaning | When to Use | |------------|---------|-------------| -| 🟢 **High** | Backed by real data (Mixpanel numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | +| 🟢 **High** | Backed by real data (analytics numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | | 🟡 **Medium** | Supported by qualitative signals (support tickets, a few reviews, codebase patterns) | Some real-world backing but not statistically rigorous | | 🔴 **Low** | Speculative — AI extrapolation, no direct data found | Plausible reasoning but no evidence yet; validate first | ```markdown | # | Hypothesis | Confidence | Supporting Evidence | How to Validate | |---|-----------|------------|-------------------|-----------------| -| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [Mixpanel query / real interview question / A/B test] | +| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [analytics query / real interview question / A/B test] | ``` > **Rule:** If more than half your hypotheses are 🔴, explicitly call that out: "Most of these insights are speculative — real user interviews should be the priority before acting on them." @@ -371,11 +371,11 @@ Connect insights back to Q1 2026 goals: | Metric | Current | Insight Impact | Suggested Action | |--------|---------|---------------|-----------------| -| Pricing → Trial Start (goal: 20-25%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Trial → Converted (goal: 35-40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Activation Rate (goal: 60%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Early Retention (goal: 40%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | -| Investment Rate (goal: 10%) | [Pull from Mixpanel] | [How insights relate] | [Specific change] | +| Pricing → Trial Start (goal: 20-25%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Trial → Converted (goal: 35-40%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Activation Rate (goal: 60%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Early Retention (goal: 40%) | [Pull from analytics] | [How insights relate] | [Specific change] | +| Investment Rate (goal: 10%) | [Pull from analytics] | [How insights relate] | [Specific change] | --- @@ -548,7 +548,7 @@ After presenting results, suggest next steps: - **"Want me to simulate another persona?"** — Different segment, different perspective - **"Want me to draft a GitHub issue for any of these insights?"** — Using `/new-issue` skill - **"Want me to add an idea to the davidsulitzer.com Ideation Lab?"** — Track hypotheses as ideation items -- **"Want me to pull more specific data on any of these findings?"** — Deeper Mixpanel/MongoDB dive +- **"Want me to pull more specific data on any of these findings?"** — Deeper analytics/MongoDB dive - **"Should I run this for GFE too?"** — Same framework, but switches to GFE-specific personas, safety concerns, celiac sensitivity levels, and dietary-focused data --- @@ -557,7 +557,7 @@ After presenting results, suggest next steps: | Source | Tool | What It Provides | |--------|------|-----------------| -| **Mixpanel** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | +| **analytics** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | | **MongoDB** | Atlas MCP (database: prod) | Users, reviews, feedback, saves, engagement, subscription state | | **App Store** | Shell (App Store Connect API) | User reviews, ratings, feature requests | | **Crisp** | Shell (API) | Support conversations, common complaints | @@ -609,12 +609,12 @@ Real cancellation reasons captured in-app: | Reason | Code | Frequency Insight | |--------|------|-------------------| -| Too expensive | `too_expensive` | Check Mixpanel for distribution | -| Didn't find relevant places | `didnt_find_relevant_places` | Check Mixpanel for distribution | -| Don't use often enough | `dont_use_often_enough` | Check Mixpanel for distribution | -| Not enough places | `not_enough_places` | Check Mixpanel for distribution | -| Safety concerns | `safety_concerns` | Check Mixpanel for distribution | -| Technical issues/bugs | `technical_issues_bugs` | Check Mixpanel for distribution | +| Too expensive | `too_expensive` | Check analytics for distribution | +| Didn't find relevant places | `didnt_find_relevant_places` | Check analytics for distribution | +| Don't use often enough | `dont_use_often_enough` | Check analytics for distribution | +| Not enough places | `not_enough_places` | Check analytics for distribution | +| Safety concerns | `safety_concerns` | Check analytics for distribution | +| Technical issues/bugs | `technical_issues_bugs` | Check analytics for distribution | | Other (free text) | `other` | Check MongoDB userFeedback | --- From 8d342d522947d4e55f88261d73238569a4812359 Mon Sep 17 00:00:00 2001 From: David Sulitzer Date: Tue, 17 Feb 2026 17:57:54 +0200 Subject: [PATCH 3/3] Keep only Codex skills in personal projects. Remove all migrated Cursor skill copies under .cursor/skills while preserving the .codex skill set as the single source of truth. --- .cursor/skills/generate-assets/SKILL.md | 173 ------- .cursor/skills/idea-generator/SKILL.md | 494 ------------------ .cursor/skills/new-issue/SKILL.md | 651 ------------------------ .cursor/skills/new-release/SKILL.md | 168 ------ .cursor/skills/new-skill/SKILL.md | 514 ------------------- .cursor/skills/qa-start/SKILL.md | 318 ------------ .cursor/skills/refine-spec/SKILL.md | 356 ------------- .cursor/skills/release-shipped/SKILL.md | 278 ---------- .cursor/skills/respect-figma/SKILL.md | 148 ------ .cursor/skills/update-skill/SKILL.md | 588 --------------------- .cursor/skills/user-interview/SKILL.md | 628 ----------------------- 11 files changed, 4316 deletions(-) delete mode 100644 .cursor/skills/generate-assets/SKILL.md delete mode 100644 .cursor/skills/idea-generator/SKILL.md delete mode 100644 .cursor/skills/new-issue/SKILL.md delete mode 100644 .cursor/skills/new-release/SKILL.md delete mode 100644 .cursor/skills/new-skill/SKILL.md delete mode 100644 .cursor/skills/qa-start/SKILL.md delete mode 100644 .cursor/skills/refine-spec/SKILL.md delete mode 100644 .cursor/skills/release-shipped/SKILL.md delete mode 100644 .cursor/skills/respect-figma/SKILL.md delete mode 100644 .cursor/skills/update-skill/SKILL.md delete mode 100644 .cursor/skills/user-interview/SKILL.md diff --git a/.cursor/skills/generate-assets/SKILL.md b/.cursor/skills/generate-assets/SKILL.md deleted file mode 100644 index e282cbd..0000000 --- a/.cursor/skills/generate-assets/SKILL.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -name: "generate-assets" -description: "Use when the user asks to generate or edit images via the OpenAI Image API (for example: generate image, edit/inpaint/mask, background removal or replacement, transparent background, product shots, concept art, covers, or batch variants); run the bundled CLI (`scripts/image_gen.py`) and require `OPENAI_API_KEY` for live calls." ---- - -# Image Generation Skill - -Generates or edits images for the current project (e.g., website assets, game assets, UI mockups, product mockups, wireframes, logo design, photorealistic images, infographics). Defaults to `gpt-image-1.5` and the OpenAI Image API, and prefers the bundled CLI for deterministic, reproducible runs. - -## When to use -- Generate a new image (concept art, product shot, cover, website hero) -- Edit an existing image (inpainting, masked edits, lighting or weather transformations, background replacement, object removal, compositing, transparent background) -- Batch runs (many prompts, or many variants across prompts) - -## Decision tree (generate vs edit vs batch) -- If the user explicitly asks to modify an image (e.g., "edit/retouch/inpaint/mask/translate/localize/change only X") -> **edit** (providing an input image alone does not imply edit; it may be a style reference) -- Else if the user needs many different prompts/assets -> **generate-batch** -- Else -> **generate** - -## Workflow -1. Decide intent: generate vs edit vs batch (see decision tree above). -2. Collect inputs up front: prompt(s), exact text (verbatim), constraints/avoid list, and any input image(s)/mask(s). For multi-image edits, label each input by index and role; for edits, list invariants explicitly. -3. If batch: write a temporary JSONL under tmp/ (one job per line), run once, then delete the JSONL. -4. Augment prompt into a short labeled spec (structure + constraints) without inventing new creative requirements. -5. Run the bundled CLI (`scripts/image_gen.py`) with sensible defaults (see references/cli.md). -6. For complex edits/generations, inspect outputs (open/view images) and validate: subject, style, composition, text accuracy, and invariants/avoid items. -7. Iterate: make a single targeted change (prompt or mask), re-run, re-check. -8. Save/return final outputs and note the final prompt + flags used. - -## Temp and output conventions -- Use `tmp/imagegen/` for intermediate files (for example JSONL batches); delete when done. -- Write final artifacts under `output/imagegen/` when working in this repo. -- Use `--out` or `--out-dir` to control output paths; keep filenames stable and descriptive. - -## Dependencies (install if missing) -Prefer `uv` for dependency management. - -Python packages: -``` -uv pip install openai pillow -``` -If `uv` is unavailable: -``` -python3 -m pip install openai pillow -``` - -## Environment -- `OPENAI_API_KEY` must be set for live API calls. - -If the key is missing, give the user these steps: -1. Create an API key in the OpenAI platform UI: https://platform.openai.com/api-keys -2. Set `OPENAI_API_KEY` as an environment variable in their system. -3. Offer to guide them through setting the environment variable for their OS/shell if needed. -- Never ask the user to paste the full key in chat. Ask them to set it locally and confirm when ready. - -If installation isn't possible in this environment, tell the user which dependency is missing and how to install it locally. - -## Defaults & rules -- Use `gpt-image-1.5` unless the user explicitly asks for `gpt-image-1-mini` or explicitly prefers a cheaper/faster model. -- Assume the user wants a new image unless they explicitly ask for an edit. -- Require `OPENAI_API_KEY` before any live API call. -- Use the OpenAI Python SDK (`openai` package) for all API calls; do not use raw HTTP. -- If the user requests edits, use `client.images.edit(...)` and include input images (and mask if provided). -- Prefer the bundled CLI (`scripts/image_gen.py`) over writing new one-off scripts. -- Never modify `scripts/image_gen.py`. If something is missing, ask the user before doing anything else. -- If the result isn’t clearly relevant or doesn’t satisfy constraints, iterate with small targeted prompt changes; only ask a question if a missing detail blocks success. - -## Prompt augmentation -Reformat user prompts into a structured, production-oriented spec. Only make implicit details explicit; do not invent new requirements. - -## Use-case taxonomy (exact slugs) -Classify each request into one of these buckets and keep the slug consistent across prompts and references. - -Generate: -- photorealistic-natural — candid/editorial lifestyle scenes with real texture and natural lighting. -- product-mockup — product/packaging shots, catalog imagery, merch concepts. -- ui-mockup — app/web interface mockups that look shippable. -- infographic-diagram — diagrams/infographics with structured layout and text. -- logo-brand — logo/mark exploration, vector-friendly. -- illustration-story — comics, children’s book art, narrative scenes. -- stylized-concept — style-driven concept art, 3D/stylized renders. -- historical-scene — period-accurate/world-knowledge scenes. - -Edit: -- text-localization — translate/replace in-image text, preserve layout. -- identity-preserve — try-on, person-in-scene; lock face/body/pose. -- precise-object-edit — remove/replace a specific element (incl. interior swaps). -- lighting-weather — time-of-day/season/atmosphere changes only. -- background-extraction — transparent background / clean cutout. -- style-transfer — apply reference style while changing subject/scene. -- compositing — multi-image insert/merge with matched lighting/perspective. -- sketch-to-render — drawing/line art to photoreal render. - -Quick clarification (augmentation vs invention): -- If the user says “a hero image for a landing page”, you may add *layout/composition constraints* that are implied by that use (e.g., “generous negative space on the right for headline text”). -- Do not introduce new creative elements the user didn’t ask for (e.g., adding a mascot, changing the subject, inventing brand names/logos). - -Template (include only relevant lines): -``` -Use case: -Asset type: -Primary request: -Scene/background: -Subject:
-Style/medium: -Composition/framing: -Lighting/mood: -Color palette: -Materials/textures: -Quality: -Input fidelity (edits): -Text (verbatim): "" -Constraints: -Avoid: -``` - -Augmentation rules: -- Keep it short; add only details the user already implied or provided elsewhere. -- Always classify the request into a taxonomy slug above and tailor constraints/composition/quality to that bucket. Use the slug to find the matching example in `references/sample-prompts.md`. -- If the user gives a broad request (e.g., "Generate images for this website"), use judgment to propose tasteful, context-appropriate assets and map each to a taxonomy slug. -- For edits, explicitly list invariants ("change only X; keep Y unchanged"). -- If any critical detail is missing and blocks success, ask a question; otherwise proceed. - -## Examples - -### Generation example (hero image) -``` -Use case: stylized-concept -Asset type: landing page hero -Primary request: a minimal hero image of a ceramic coffee mug -Style/medium: clean product photography -Composition/framing: centered product, generous negative space on the right -Lighting/mood: soft studio lighting -Constraints: no logos, no text, no watermark -``` - -### Edit example (invariants) -``` -Use case: precise-object-edit -Asset type: product photo background replacement -Primary request: replace the background with a warm sunset gradient -Constraints: change only the background; keep the product and its edges unchanged; no text; no watermark -``` - -## Prompting best practices (short list) -- Structure prompt as scene -> subject -> details -> constraints. -- Include intended use (ad, UI mock, infographic) to set the mode and polish level. -- Use camera/composition language for photorealism. -- Quote exact text and specify typography + placement. -- For tricky words, spell them letter-by-letter and require verbatim rendering. -- For multi-image inputs, reference images by index and describe how to combine them. -- For edits, repeat invariants every iteration to reduce drift. -- Iterate with single-change follow-ups. -- For latency-sensitive runs, start with quality=low; use quality=high for text-heavy or detail-critical outputs. -- For strict edits (identity/layout lock), consider input_fidelity=high. -- If results feel “tacky”, add a brief “Avoid:” line (stock-photo vibe; cheesy lens flare; oversaturated neon; harsh bloom; oversharpening; clutter) and specify restraint (“editorial”, “premium”, “subtle”). - -More principles: `references/prompting.md`. Copy/paste specs: `references/sample-prompts.md`. - -## Guidance by asset type -Asset-type templates (website assets, game assets, wireframes, logo) are consolidated in `references/sample-prompts.md`. - -## CLI + environment notes -- CLI commands + examples: `references/cli.md` -- API parameter quick reference: `references/image-api.md` -- If network approvals / sandbox settings are getting in the way: `references/codex-network.md` - -## Reference map -- **`references/cli.md`**: how to *run* image generation/edits/batches via `scripts/image_gen.py` (commands, flags, recipes). -- **`references/image-api.md`**: what knobs exist at the API level (parameters, sizes, quality, background, edit-only fields). -- **`references/prompting.md`**: prompting principles (structure, constraints/invariants, iteration patterns). -- **`references/sample-prompts.md`**: copy/paste prompt recipes (generate + edit workflows; examples only). -- **`references/codex-network.md`**: environment/sandbox/network-approval troubleshooting. diff --git a/.cursor/skills/idea-generator/SKILL.md b/.cursor/skills/idea-generator/SKILL.md deleted file mode 100644 index 6828f07..0000000 --- a/.cursor/skills/idea-generator/SKILL.md +++ /dev/null @@ -1,494 +0,0 @@ ---- -name: idea-generator -description: Brainstorm bold, data-informed product feature ideas with competitor research, visual demos, and metric alignment. Use when exploring new feature concepts or looking for creative ways to move key metrics. Don't use for creating issues (/new-issue) or refining existing specs (/refine-spec). -disable-model-invocation: true ---- -# idea-generator - -## When to Use - -- You want creative feature ideas to move a specific metric (activation, retention, conversion, etc.) -- You're exploring "what could we build?" before committing to a spec -- You want competitor-informed brainstorming with visual mockups - -## When NOT to Use - -- You've already decided what to build and need an issue → use `/new-issue` -- You have an existing spec that needs refinement → use `/refine-spec` -- You need user research to validate an idea → use `/user-interview` -- You're doing QA or deployment work → wrong skill entirely - ---- - -You are a **wildly creative** product brainstorm partner for davidsulitzer.com — a places discovery app that helps users find amazing spots (coffee shops, bars, restaurants, etc.) through community curation. - -## Your Mission - -Generate **bold, original, unexpected** feature ideas that move our metrics. Don't play it safe — the best ideas often sound crazy at first. Be inspired by what competitors do well, but don't copy — **innovate**. - -Think like a mix of: a product visionary, a behavioral psychologist, and a user who's slightly obsessed with finding the perfect coffee shop. - ---- - -## Q1 2026 Goals - -### 🎯 North Star -**Define your current north-star metric and date.** - -### 📊 Top-Down Metrics (Funnel Conversion) -| Stage | Goal | -|-------|------| -| **💰 Pricing Screen → Trial Start** | 20-25% | -| **✅ Trial Start → Trial Converted** | 35-40% | - -### 💡 Leading Indicators - -| Metric | Goal | Definition | Why It Matters | -|--------|------|------------|----------------| -| **🔥 Activation Rate** | 60% | % of new users who perform at least ONE of: save, like, search result click, or place profile view in their first session | First session is make-or-break. If they don't engage on day 1, they're ghosts. | -| **🔄 Early Retention** | 40% | % of trial users with 3+ sessions in first 3 days | Habit formation window. Users who return within 3 days are building davidsulitzer.com into their routine. | -| **💾 Investment Rate** | 10% | % of trial users who save at least 1 place in their first 7 days | Saving = "I found value I want to keep." Creates switching costs and signals product-market fit. | - ---- - -## Brainstorm Process - -### Step 1: Pick Your Target -Ask me which metric or screen I want to focus on, or suggest one based on the biggest opportunity gap. - -### Step 2: Understand Current State -Before ideating, briefly explore the codebase to understand: -- What's the current UX for this metric area? -- What screens/flows are involved? -- What analytics events exist? - -Use semantic search and grep to find relevant code in both `davidsulitzer.com` and `davidsulitzer.com`. - -### Step 3: Research Competitors -Before generating ideas, use the **WebSearch tool** to research what competitors are doing well in this space. Don't rely on outdated knowledge — find recent features and updates! - -**Search queries to run:** -- `"[competitor] new features [current year]"` — e.g., "Google Maps new features 2026" -- `"[competitor] [metric area] improvements"` — e.g., "Yelp user engagement improvements" -- `"best [category] app features [current year]"` — e.g., "best restaurant discovery app features 2026" - -**Competitors to research:** -- **Maps & Discovery:** Google Maps, Yelp, Foursquare, TripAdvisor -- **Food & Booking:** Uber Eats, DoorDash, OpenTable -- **Travel:** Airbnb, Booking.com -- **Engagement patterns:** Instagram, TikTok, BeReal, Duolingo - -Note what's working for them and why — then think about how davidsulitzer.com can do it **better or differently**. - -### Step 4: Review User Interview Notes - -Before ideating, check the **User Interview Notes** Slack channel for real user feedback, pain points, and feature requests. This is gold — actual users telling us what they want! - -**Slack Channel:** `#user-interview-notes` (ID: `C09PTLMHKLP`) - -Use the Slack MCP to search for relevant insights: -``` -Search the Slack channel C09PTLMHKLP for messages related to [TOPIC/METRIC AREA] -``` - -**What to look for:** -- Recurring pain points or frustrations -- Feature requests that align with the target metric -- Emotional language (excitement, frustration, confusion) -- Workarounds users have invented (signals unmet needs!) -- Quotes that could inform your hypothesis - -**Pro tip:** Real user words make the best hypothesis justifications. Quote them in your ideas! - -### Step 5: Mine Crisp Support Conversations - -**Real users complaining = real product insights.** Before ideating, pull recent support conversations from Crisp to find recurring pain points, feature requests, and confusion signals. - -> #### Cloud Agent? Use Environment Secrets -> -> If running as a **cloud agent**, use `CRISP_IDENTIFIER`, `CRISP_KEY`, and `CRISP_WEBSITE_ID` from environment secrets instead of sourcing `.env`. - -#### 5a. Load Credentials - -```bash -source .env -``` - -#### 5b. Search Conversations by Topic - -Search for conversations related to the metric area or feature you're brainstorming on: - -```bash -# Search conversations matching a keyword (returns up to 20 per page) -curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?search_query=KEYWORD&search_type=text" \ - --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ - --header "X-Crisp-Tier: plugin" | python3 -m json.tool -``` - -**Search tips:** -- Search for the feature area: `search`, `save`, `map`, `subscription`, `trial`, `cancel`, `bug`, `crash`, `payment` -- Search for emotional signals: `frustrated`, `broken`, `love`, `wish`, `want`, `missing`, `need` -- Search for competitor mentions: `google maps`, `yelp`, `tripadvisor` -- Page through results by changing the page number in the URL (`.../conversations/2`, `.../conversations/3`, etc.) - -#### 5c. Read Messages from Interesting Conversations - -When you find a conversation that looks relevant, pull the full message thread: - -```bash -# Get all messages in a specific conversation -curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversation/SESSION_ID/messages" \ - --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ - --header "X-Crisp-Tier: plugin" | python3 -m json.tool -``` - -Replace `SESSION_ID` with the `session_id` from the conversation list (e.g., `session_fa5663c8-6f5c-473c-b4e9-44c3db8ef2e0`). - -#### 5d. Browse Recent Conversations (No Search) - -Sometimes the best insights come from just scanning what people are writing about lately: - -```bash -# List most recent conversations (page 1, most recent first) -curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1" \ - --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ - --header "X-Crisp-Tier: plugin" | python3 -m json.tool -``` - -Each conversation includes a `topic`, `last_message`, `meta.nickname`, `meta.email`, and `meta.device.geolocation` — useful for building persona context. - -#### 5e. Filter by Date Range - -Focus on recent feedback (e.g., last 30 days): - -```bash -# Conversations updated in the last 30 days -curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_date_start=$(date -u -v-30d '+%Y-%m-%dT00:00:00.000Z')&filter_date_end=$(date -u '+%Y-%m-%dT23:59:59.999Z')" \ - --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ - --header "X-Crisp-Tier: plugin" | python3 -m json.tool -``` - -#### 5f. Filter by Conversation State - -Focus on unresolved issues (things users are STILL frustrated about): - -```bash -# Only unresolved conversations -curl -s "https://api.crisp.chat/v1/website/$CRISP_WEBSITE_ID/conversations/1?filter_not_resolved=1" \ - --user "$CRISP_IDENTIFIER:$CRISP_KEY" \ - --header "X-Crisp-Tier: plugin" | python3 -m json.tool -``` - -#### 5g. What to Extract - -From the conversations, look for: - -| Signal | What It Tells You | How to Use It | -|--------|-------------------|---------------| -| **Recurring complaints** | What's broken or frustrating right now | Direct problem to solve | -| **Feature requests** | What users wish existed | Idea fuel — validate with data | -| **Confusion patterns** | Where the UX fails to communicate | UX improvement opportunities | -| **Competitor mentions** | What users compare davidsulitzer.com to | Competitive positioning insights | -| **Emotional language** | How strongly users feel | Prioritization signal (strong = urgent) | -| **Workarounds** | Hacks users invent to get what they want | Unmet needs hiding in plain sight | -| **Churn signals** | "I'm canceling because..." | Retention idea triggers | -| **Praise / delight** | What users genuinely love | Double down on what works | - -**Pro tip:** Quote real user words from Crisp in your ideas! Nothing sells a hypothesis like an actual frustrated human saying exactly the thing your idea would fix. - -### Step 6: Find Related Backlog -Search GitHub for related existing issues: -```bash -gh issue list -R 8Gaston8/davidsulitzer.com --search "KEYWORDS" --limit 10 -``` - -This helps connect ideas to existing work and avoid duplicating efforts. - -### Step 7: Generate Ideas (BE CREATIVE!) - -Generate **5-10 ideas** that are: -- **Original** — not obvious, not what everyone else is doing -- **Bold** — willing to take risks, challenge assumptions -- **Grounded** — tied to user psychology and mobile UX best practices -- **Varied** — across different categories (quick wins to moonshots) - ---- - -## Output Format - -**IMPORTANT:** Present ALL ideas using this detailed format. Each idea should be a complete, self-contained brief. - ---- - -### 💡 Idea #[N]: [Creative, Catchy Name] - -**Category:** ⚡ Quick Win / 🚀 Feature / 🧪 Experiment / 🌙 Moonshot - -> *[One sentence elevator pitch — make it punchy and memorable!]* - -#### The Concept -[2-3 sentences describing what this is and how it works. Be specific enough that someone could understand and visualize it.] - ---- - -#### 🎯 Goals Alignment - -**Primary Target:** -| Goal | Expected Impact | Confidence | -|------|-----------------|------------| -| [🔥 Activation / 🔄 Retention / 💾 Investment / 💰 Pricing→Trial / ✅ Trial→Converted] | [+X-Y%] | [High/Medium/Low] | - -**Also Impacts:** [List secondary goals with emojis] - -**Hypothesis:** -> [2-3 sentences explaining WHY this will work. Be specific about the user psychology, behavioral principle, or proven pattern you're drawing from. This is the most important part — convince me!] - -**Rationale:** -[1-2 sentences on the underlying logic or evidence. Reference specific data, competitor success, or behavioral science if applicable.] - ---- - -#### 🔍 Competitor Inspiration - -| Competitor | What They Do | davidsulitzer.com's Twist | -|------------|--------------|--------------| -| [Company 1] | [Their implementation] | [How we do it better/differently] | -| [Company 2] | [Their implementation] | [How we do it better/differently] | - -**Key Insight:** [What's the learning here? Why does this pattern work?] - ---- - -#### 🔗 Related Backlog - -| Repo | Issue | How It Relates | -|------|-------|----------------| -| 📱 davidsulitzer.com | [#XXXX - Title](link) | [Connection] | -| ⚙️ davidsulitzer.com | [#XXXX - Title](link) | [Connection] | - -*If no related issues exist, note: "No existing issues — this is net new!"* - ---- - -#### 📊 Evaluation - -| Dimension | Rating | Notes | -|-----------|--------|-------| -| **Impact** | 🟢 High / 🟡 Medium / 🔴 Low | [Brief justification] | -| **Effort** | S / M / L / XL | [Key complexity drivers] | -| **Confidence** | 🟢 High / 🟡 Medium / 🔴 Low | [What gives us confidence or uncertainty?] | -| **Dependencies** | [List] | Backend? Design? Data? External? | - ---- - -#### 💭 Open Questions -- [Question 1 that needs answering before implementation] -- [Question 2 about user behavior or technical feasibility] - ---- - -## Creativity Prompts - -When generating ideas, challenge yourself with these questions: - -**Flip the script:** -- What if we did the opposite of what's expected? -- What would a gaming app do here? -- What would feel magical to users? - -**Steal from other domains:** -- How does Duolingo make learning addictive? -- How does Tinder make swiping fun? -- How does Spotify personalize discovery? -- How does BeReal create urgency? - -**Remove friction vs add delight:** -- What's the laziest path to value? -- What would make users smile? -- What would they screenshot and share? - -**Think about emotions:** -- What makes users feel smart/cool/in-the-know? -- What creates FOMO? -- What builds trust? -- What creates "unfinished business" that brings them back? - ---- - ---- - -## Visual Demo (Required!) - -**After generating ideas, create a visual demo page** that renders each idea inside realistic iPhone frames. This is NOT optional — seeing ideas visually makes them 10x more useful. - -### Setup - -Create a temporary React demo in a `demos/` folder: - -```bash -mkdir -p demos/[feature-name] -cd demos/[feature-name] -npm init -y -npm install react react-dom vite @vitejs/plugin-react -``` - -### Phone Frame Structure - -Every design must be rendered inside a phone mockup: - -```jsx -
-
-
- {/* Or other app context */} - - -
-
-``` - -### Template Contexts - -Choose the right context based on what screen your idea lives on: - -| Context | When to Use | Key Elements | -|---------|-------------|--------------| -| `PlaceProfileContext` | Place details, reviews, actions | Gallery, title, rating, People Are Saying, Good To Know | -| `SearchContext` | Search results, filters | Search header, follow-up chips, map with pins | -| `HomeContext` | Discovery, categories | Search bar, category pills, map background | -| `ListContext` | Place lists, collections | List header, place cards, filters | - -### Phone Frame CSS - -Use **280×560px** for demos (fits more ideas on screen) or **375×812px** for pixel-perfect iPhone fidelity: - -```css -.phone-frame { - width: 280px; /* Or 375px for full size */ - height: 560px; /* Or 812px for full size */ - background: #1a1a1a; - border-radius: 32px; - padding: 10px; - position: relative; - box-shadow: 0 10px 30px rgba(0,0,0,0.3); -} - -.phone-notch { - width: 80px; - height: 24px; - background: #1a1a1a; - border-radius: 0 0 14px 14px; - position: absolute; - top: 10px; - left: 50%; - transform: translateX(-50%); - z-index: 10; -} - -.phone-screen { - width: 100%; - height: 100%; - background: #f8f8f8; - border-radius: 24px; - overflow: hidden; - overflow-y: auto; /* Important for scrollable content! */ -} - -/* Scale content to fit smaller frame */ -.phone-screen .template-context { - transform: scale(0.72); - transform-origin: top left; - width: 139%; - height: 139%; -} -``` - -### Color Contrast (Important!) - -| Background | Text Color | Example | -|------------|------------|---------| -| Light (#fff, #f8f8f8) | Dark (#333 or darker) | ✅ Readable | -| Dark (#1a1a1a, #333) | White or light | ✅ Readable | -| Gradients | Test at ALL points | ⚠️ Check edges | -| Light gray | Light text | ❌ NEVER | - -### Page Layout - -Create a unified view with: - -1. **Sticky header** showing North Star + metrics goals -2. **Filter bar** with toggles: - - Category: All | ⚡ Quick Wins | 🚀 Features | 🌙 Moonshots - - Goal: All | 🔥 Activation | 🔄 Retention | 💾 Investment - - Data panels: 🎯 Goals | 🔍 Competitors | 🔗 Backlog (toggle visibility) -3. **Idea cards** showing: - - Phone mockup on the left - - Idea info + data panels on the right (always visible, no expand/collapse!) - -### Data Panels (Compact, Always Visible) - -```jsx -{/* Goals Panel */} -
-
🎯 Goal Alignment
-
{emoji} {goalName}
-
{expectedImpact}
-

{hypothesis}

-
- -{/* Competitors Panel */} -
-
🔍 Competitors ({count})
-
- {competitors.map(c => {c.name}: {c.feature})} -
-
- -{/* Backlog Panel */} -
-
🔗 Backlog ({count})
-
- {issues.map(i => 📱 #{i.number})} -
-
-``` - -### Run the Demo - -```bash -npm run dev -# Opens at http://localhost:3000 -``` - -**Open the browser automatically** so I can see the ideas visualized! - -### Key Principles - -| Do ✅ | Don't ❌ | -|-------|---------| -| Render inside phone frames | Show standalone components | -| Use real app context (Place Profile, Search, etc.) | Use fake/simplified mockups | -| Show all data panels immediately | Hide behind expand buttons | -| Make it interactive (filters, toggles) | Static walls of text | -| Use realistic placeholder data | Lorem ipsum | - ---- - -## After Brainstorming - -Once we finish brainstorming, I can help with next steps **if you ask**: -- Deep dive on a specific idea -- Draft a mini PRD with success metrics -- Identify key code areas to modify - -**Note:** I will NOT automatically create GitHub issues without your explicit request. Brainstorming is just brainstorming! - -**Cleanup:** The demo folder is temporary — delete it when done unless you want to keep it. - ---- - -## Let's Brainstorm! 🧠✨ - -Which metric or screen do you want to focus on today? Or should I analyze the codebase first to identify where the biggest opportunities might be? - -Remember: **The best ideas often sound a little crazy at first.** Don't hold back! diff --git a/.cursor/skills/new-issue/SKILL.md b/.cursor/skills/new-issue/SKILL.md deleted file mode 100644 index aa17e97..0000000 --- a/.cursor/skills/new-issue/SKILL.md +++ /dev/null @@ -1,651 +0,0 @@ ---- -name: new-issue -description: Create well-researched GitHub issues with full project integration (issue type, davidsulitzer.com project, priority, cross-repo blocking). Use when the user wants to create a new issue from a bug report, feature request, or task. Don't use for refining existing issues (/refine-spec) or for creating issues as part of QA (/qa-start handles that internally). -disable-model-invocation: true ---- -# new-issue - -Create GitHub issues based on the mentioned requirements. **Always** perform pre-research (Step 0) before writing anything. - ---- - -## When to Use - -- The user describes a bug, feature request, or task that needs a GitHub issue -- A bug review (`/review-new-bugs`) or crash review (`/review-new-crashes`) identified something worth tracking -- The user explicitly says "create an issue" or "file a ticket" - -## When NOT to Use - -- An issue already exists and needs improvement → use `/refine-spec` -- You're in the middle of QA and found a bug → `/qa-start` handles issue creation internally -- The user is brainstorming ideas → use `/idea-generator` first, create issues only when asked -- You're unsure if an issue is needed → ask the user before creating anything - ---- - -## Step 0: Pre-Research (BEFORE Creating Any Issue) - -Before writing anything, you MUST investigate the codebase and existing issues. This step ensures the issue lands in the correct repo and arrives loaded with useful context for the implementer. - -### 0a: Search for Relevant Code - -Search **all three** repos for keywords related to the problem or feature — function names, screen names, model names, API endpoints, error messages: - -```bash -# Search the current repo first -rg -l "" . - -# If in the davidsulitzer.com with submodules checked out, also search: -rg -l "" davidsulitzer.com/ 2>/dev/null - -# If in a standalone repo checkout, use gh to search other repos: -gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 -``` - -> **Note:** The `rg` commands with `2>/dev/null` gracefully handle missing sibling directories in standalone checkouts. Use `gh search code` as a fallback when submodules aren't available. - -Take note of: -- **Which files are involved** — full paths and key line numbers -- **Which repo(s) contain the relevant code** — this determines where the issue goes (Step 0b) -- **Recent changes** — check git log for the relevant files: - ```bash - git log --oneline -10 -- - ``` - -### 0b: Determine the Correct Repo - -Based on your code search results from 0a: - -| Search Results | Repo Decision | -|---------------|---------------| -| Relevant code **only** in `davidsulitzer.com/` | Create issue in `8Gaston8/davidsulitzer.com` | -| Relevant code in **multiple** repos | Create one issue per repo (see Step 2) | -| **Can't find relevant code** | **ASK the user** — do NOT guess | - -**Heuristic helpers** (use alongside code search, not instead of it): - -| Signal | Likely Repo | -|--------|-------------| -| UI, screens, animations, SwiftUI views, layout | iOS | -| API endpoints, database, migrations, queries, cron jobs | Server | -| Map tiles, vector tiles, pin scoring, pin priority, category pins | Tileserver | -| Location filtering, atly score, tile rendering, map vectors | Tileserver | -| "Data is wrong", "results are incorrect" | Usually Server or Tileserver — verify with code search | -| "Crashes", "freezes", "layout broken", "animation janky" | Usually iOS | -| "Pins wrong", "map shows wrong places", "scoring issue" | Usually Tileserver — verify with code search | -| Model names, feature names | Could be any repo — always search first | - -> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. - -### 0c: Check for Duplicate / Related Issues - -Before creating a new issue, search for existing ones: - -```bash -# Search open issues in all repos -gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --limit 10 - -# Also check recently closed issues (could be a regression of a previous fix) -gh issue list --repo 8Gaston8/davidsulitzer.com --search "" --state closed --limit 5 -``` - -- **Open duplicate found** → Tell the user and link to it instead of creating a new one -- **Closed related issue found** → Reference it in the new issue for context (possible regression?) - -### 0d: Bug-Specific Investigation - -When creating a **bug** issue, do this additional research: - -1. **Find the error source** — If the user mentions an error message, search for where it's thrown: - ```bash - rg "" davidsulitzer.com/ davidsulitzer.com/ davidsulitzer.com/ - ``` - Include the file path, line number, and surrounding context in the issue. - -2. **Check recent commits for regressions** — Look for changes that could have introduced the bug: - ```bash - # Recent commits touching relevant files - git log --oneline -10 -- - - # Recent PRs merged to the repo - gh pr list --repo 8Gaston8/davidsulitzer.com --state merged --limit 10 - ``` - If a relevant file was changed recently, note the PR in the issue as a potential regression source. - -3. **Check test coverage** — Look for existing tests in the affected area: - ```bash - # iOS tests (use davidsulitzer.com paths if available, otherwise search current repo) - rg -l "" davidsulitzer.com/AtlyTests/ davidsulitzer.com/AtlyUITests/ 2>/dev/null || \ - rg -l "" AtlyTests/ AtlyUITests/ 2>/dev/null - - # Server tests (few exist — check services) - rg -l "" davidsulitzer.com/services/ 2>/dev/null || \ - rg -l "" services/ api/test/ 2>/dev/null - - # Tileserver tests - rg -l "" davidsulitzer.com/app/__tests__/ davidsulitzer.com/app/businessLogic/__tests__/ 2>/dev/null || \ - rg -l "" app/__tests__/ app/businessLogic/__tests__/ 2>/dev/null - ``` - Note whether tests exist and whether they cover the reported scenario. - -4. **Identify dependencies** — What other systems does this code touch? - - Feature flags that gate the behavior - - API endpoints involved (grep for route definitions) - - Third-party services (analytics, RevenueCat, Firebase, etc.) - - Caching layers (tileserver, CDN, local cache) - -5. **Gather reproduction context:** - - Which API endpoints are involved - - What user state or data triggers the bug - - Which app version or server deployment may have introduced it (if determinable from git log) - -### 0e: Include Research Findings in Issue - -All findings from Step 0 MUST be woven into the issue body. Add a **Pre-Research Findings** section: - -```markdown -## Pre-Research Findings - -**Relevant code:** -- `:` — [brief description of what this code does] -- `:` — [brief description] - -**Recent changes in this area:** -- PR # () — [what it changed] - -**Related issues:** -- # — [title] (open/closed) - -**Test coverage:** [Existing tests cover X / No existing test coverage for this path] - -**Dependencies:** [Feature flags, APIs, services involved] -``` - -> ⚠️ **Do NOT skip this step.** Issues created without pre-research waste the implementer's time re-discovering context you already had access to. - ---- - -## Step 1: Determine the Right Repo(s) - -By this point, Step 0 should have told you which repo(s) contain the relevant code. Confirm your decision: - -| Scope | Action | -|-------|--------| -| **iOS only** | Create issue in `8Gaston8/davidsulitzer.com` | -| **Server only** | Create issue in `8Gaston8/davidsulitzer.com` | -| **Tileserver only** | Create issue in `8Gaston8/davidsulitzer.com` | -| **Multiple repos** | Create one issue per repo (see Step 2) | -| **Uncertain** | Go back to Step 0a and search more, or **ask the user** | - -> ⚠️ Do NOT use `iOS` or `backend` labels — the repo itself indicates the platform. - ---- - -## Step 2: For Cross-Repo Issues - -When an issue requires work in multiple repos (any combination of iOS, Server, Tileserver): - -1. **Create backend/data issues first** — start with `8Gaston8/davidsulitzer.com` and/or `8Gaston8/davidsulitzer.com` - - Focus on backend requirements: API changes, data model, endpoints, tile logic - - Reference the downstream issue(s) that will be created - - Set project status to **Todo** - -2. **Create the iOS issue** in `8Gaston8/davidsulitzer.com` (if applicable) - - Focus on client requirements: UI, models, integration - - Reference the backend issue(s): "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" or "🚧 Blocked by: 8Gaston8/davidsulitzer.com#XXXX" - - Include a section documenting the expected API contract from server/tileserver - -3. **Set up blocking relationships** — THIS IS MANDATORY FOR CROSS-REPO ISSUES: - -> The general rule: **iOS is blocked by Server/Tileserver. Server may be blocked by Tileserver.** Create `addBlockedBy` for each dependency. - - **Step 3a: Get the issue node IDs:** - ```bash - # Get issue node ID (replace and for each issue) - gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' - ``` - - **Step 3b: Create the blocked-by relationship (repeat for each dependency):** - ```bash - # Downstream issue is BLOCKED BY upstream issue - # e.g., iOS blocked by Server, iOS blocked by Tileserver, Server blocked by Tileserver - gh api graphql -f query='mutation { addBlockedBy(input: { issueId: "", blockingIssueId: "" }) { clientMutationId } }' - ``` - - **Step 3c: VERIFY the relationship was created (repeat for each downstream issue):** - ```bash - gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { blockedBy(first: 5) { nodes { number title repository { name } } } } } }' - ``` - - > ⚠️ **CRITICAL:** Do NOT use `addSubIssue` — that creates a parent/child hierarchy, NOT a blocking dependency. ONLY use `addBlockedBy`. - > - > ⚠️ **CRITICAL:** Always run the verification query. The mutation can return success even if no relationship was created. You MUST confirm `blockedBy.nodes` contains the server issue. - ---- - -## Step 3: Issue Content Requirements - -For each issue, include: - -### Required Sections -- **Pre-Research Findings** — Output from Step 0 (relevant code, recent changes, related issues, test coverage, dependencies) -- **Goal** — Clear one-liner at the top -- **Context/Background** — Why this matters -- **Requirements** — What needs to be built (platform-specific) -- **Figma Design** — ⚠️ If a Figma URL exists, include it as-is. **Do NOT translate the Figma into text** — the design file IS the spec. See the Figma Design template below. -- **Out of Scope** — Explicitly state what's NOT part of this issue (prevents scope creep, helps AI agents stay focused) -- **Acceptance Criteria** — Checkboxes for definition of done -- **Testing Requirements** — Unit tests, regression tests, manual QA scope (see Step 6) -- **Related Issues/PRs** — Cross-references - -### Conditional Sections (include when applicable) - -| Section | When to Include | -|---------|-----------------| -| **Figma Design** | ⚠️ **REQUIRED** when a Figma URL or design exists — see template below | -| **Design Brief** | ⚠️ **REQUIRED** when issue has `design` label — see template below | -| **Technical Approach** | Features/refactors where you have a sense of the architecture | -| **Investigation Checklist** | Bugs where root cause is unknown | -| **Constraints** | When there are performance, compatibility, or deadline requirements | -| **Feature Flag** | New features or risky changes that should be gated | -| **Reference Implementation** | When similar patterns/PRs exist to follow | -| **Risk/Rollback** | Medium+ risk changes that could cause issues | -| **Analytics Requirements** | When new analytics events are needed | -| **Documentation Updates** | When README, API docs, or agent rules need updating (see Step 7) | - -### Section Templates - -
-Out of Scope (always include) - -```markdown -## Out of Scope -- [What you're explicitly NOT doing] -- [Related work that's a separate issue] -- [Platform exclusions, e.g., "Android — separate issue"] -``` -
- -
-⚠️ Figma Design (REQUIRED when a Figma design exists) - -When the user provides a Figma URL or the feature has an existing Figma design, you MUST include a Figma Design section. The critical rule here is: - -> **DO NOT translate the Figma design into text.** The Figma file IS the spec. Just link to it. - -Agents and humans can read Figma directly — there's no need to describe layouts, colors, spacing, or component structure in prose. Doing so creates a stale, lossy copy of the design that will drift from the source of truth. - -```markdown -## Figma Design - -> **The Figma design is the source of truth for all visual and interaction details.** -> The implementer MUST use the `/respect-figma` skill to read the design directly from Figma. -> Do NOT rely on text descriptions of the design — open the Figma link below. - -🎨 **Figma:** [paste full Figma URL here] - -**Scope of the design:** -- [Which screens/flows are covered in this Figma link] -- [Any specific frames or variants to focus on] - -**Notes (only if needed):** -- [Anything that's NOT in the Figma but is relevant — e.g., "ignore the old header in the design, we're keeping the current one"] -- [Behavior/logic that isn't captured visually — e.g., "tapping X triggers a confirmation dialog"] -``` - -**What to include in the issue body:** -- The Figma link(s) — full URL with the correct node selected -- Which screens/frames are in scope -- Any behavioral notes that Figma can't express (conditional logic, animations, error states) - -**What NOT to include:** -- ❌ Text descriptions of the layout ("there's a card with rounded corners and a blue header...") -- ❌ Extracted color values, font sizes, or spacing numbers -- ❌ Re-drawn diagrams or ASCII mockups of what Figma already shows -- ❌ Screenshots of the Figma — link to the actual file so the agent can query it programmatically - -The agent picking up this issue will use `/respect-figma` to fetch design context, screenshots, variables, and assets directly from the Figma API. That's far more accurate than any text description. -
- -
-⚠️ Design Brief (REQUIRED when issue has `design` label) - -When an issue has the `design` label, it means designer Liron needs to create or review designs. You MUST include these three sections: - -```markdown -## Problem -[The deep problem which the feature aims to solve. Be as descriptive as possible about WHY this matters and what pain it causes users.] - -## Design Brief -[Conceptual description of the design elements needed. This briefs Liron to start prototyping. Keep it conceptual — describe WHAT needs to be designed, not HOW to design it.] - -Consider including: -- Key screens/components that need design -- User flows to design -- States to consider (empty, loading, error, success) -- Interactions and animations needed -- Open questions for the designer - -## Design Requirements - -| Title | User Story | Importance | -|-------|------------|------------| -| [Requirement Name] | As a [user], I want [goal] so that [benefit] | Must Have / Should Have / Nice to Have | -``` - -**Importance Levels:** -- **Must Have** — Essential for the feature to work -- **Should Have** — Important but not blocking -- **Nice to Have** — Enhancement if time permits - -**Example:** -```markdown -## Problem -Users in the churn/retention flow often complete cancellation without engaging support. We miss opportunities to understand why they're leaving or offer solutions. - -## Design Brief -Design a visually striking element that draws attention to the Contact Support option in the churn flow. Could be an arrow, illustration, or animation. Should be impossible to miss but not obnoxious — friendly and helpful, not desperate. - -## Design Requirements - -| Title | User Story | Importance | -|-------|------------|------------| -| Attention-Grabbing Element | As a churning user, I want to notice the Contact Support option | Must Have | -| Friendly Tone | As a user, I want the prompt to feel helpful, not guilt-trippy | Must Have | -| Non-Intrusive | As a user who decided to cancel, I don't want to feel blocked | Should Have | -``` - -> ⚠️ **When NOT to use the `design` label:** -> - Bug fixes where the expected behavior is already clear -> - Code/implementation fixes (janky animations, keyboard issues) -> - Technical setup/tooling tasks -> - Backend-only work with no UI impact -
- -
-Technical Approach (for features/refactors) - -```markdown -## Technical Approach -High-level strategies to consider: -- [Approach 1 and why it might work] -- [Approach 2 as alternative] -- [Key architectural decisions to make] - -**Recommended approach:** [If you have a preference, state it] -``` -
- -
-Investigation Checklist (for bugs) - -```markdown -## Investigation Checklist -- [ ] Check [specific file/area] for [potential cause] -- [ ] Review recent changes in [related PR] -- [ ] Test with [specific user configuration] -- [ ] Verify [expected behavior] still works -``` -
- -
-Constraints - -```markdown -## Constraints -- **Performance:** [e.g., Must load in <200ms] -- **Compatibility:** [e.g., Must support iOS 15+] -- **Data:** [e.g., Cannot break existing API contract] -- **Timeline:** [e.g., Must ship in 4.29.0] -``` -
- -
-Feature Flag - -```markdown -## Feature Flag -- **Flag name:** `enable_[feature_name]` -- **Default:** OFF in prod, ON in dev -- **Rollout plan:** [e.g., 10% → 50% → 100%] -- **Kill switch:** [How to disable quickly if needed] -``` -
- -
-Reference Implementation - -```markdown -## Reference Implementation -- Similar work: PR #[number] — [brief description] -- Follow pattern in: `[FilePath.swift]` -- Avoid approach from: PR #[number] — [why it didn't work] -``` -
- -
-Risk/Rollback - -```markdown -## Risk Assessment -- **Risk:** [What could go wrong] -- **Likelihood:** [Low/Medium/High] -- **Impact:** [What breaks if it goes wrong] -- **Mitigation:** [How to reduce risk] -- **Rollback plan:** [How to undo — revert PR, feature flag off, data migration, etc.] -``` -
- -
-Analytics Requirements - -```markdown -## Analytics Requirements -- **New events:** - - `[event_name]` — triggered when [condition] - - Properties: `[prop1]`, `[prop2]` -- **Success metric:** [How to measure if this worked] -- **Dashboard:** [Link or name of dashboard to update] -``` -
- ---- - -## Step 4: Labels, Assignee & Issue Type - -- **Assignee:** David Sulitzer (@8Gaston8) -- **Labels:** Use existing labels (funnel stages, GFE, davidsulitzer.com, design, regression, etc.) - - Never use `bug`, `feature`, or `task` as labels — use Project Issue Type instead - - ⚠️ **`design` label** — Only use when designer (Liron) needs to create/review designs. When used, you MUST include Problem, Design Brief, and Design Requirements sections (see Step 3 template). - -### Issue Type — MANDATORY FOR EVERY ISSUE - -Issue Type is a GitHub-level field (not a project field). You MUST set it via GraphQL. - -**Available Issue Types:** -| Type | ID | When to Use | -|------|-----|-------------| -| **Task** | `IT_kwDOBluP0c4BBYRC` | General work items, refactors, chores | -| **Bug** | `IT_kwDOBluP0c4BBYRF` | Defects, broken functionality | -| **Feature** | `IT_kwDOBluP0c4BBYRI` | New user-facing capabilities | - -**Set Issue Type:** -```bash -# Replace with the issue's node ID (e.g., I_kwDOJF_Lac7kFn4f) -# Replace with the appropriate ID from the table above -gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "" }) { issue { id } } }' -``` - -**Verify Issue Type was set:** -```bash -gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } } } }' -``` - -> ⚠️ **CRITICAL:** Always verify the issue type was set. The `gh issue view` CLI command does NOT show issue type — you MUST use the GraphQL query above to confirm. - ---- - -## Step 5: davidsulitzer.com Project Configuration - -Ensure EVERY issue is added to the **davidsulitzer.com** GitHub Project (number: 2, ID: ``) with ALL fields filled. - -### Add Issue to Project -```bash -gh project item-add 2 --owner 8Gaston8 --url -``` - -### Get Project Item ID (needed to set fields) -```bash -gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' -``` - -### Project Field IDs Reference -| Field | Field ID | Options | -|-------|----------|---------| -| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `2c3687f7`, Todo: `f75ad846`, In Design: `646518b5`, To Clarify: `2fdcccf3`, In Development: `47fc9ee4`, Done: `98236657` | -| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5`, Backlog: `8ef4ba21` | - -### Set Project Field Values -```bash -# Set Status (e.g., Todo) -gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "f75ad846" } }) { projectV2Item { id } } }' - -# Set Priority (e.g., Medium) -gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "2b2b659f" } }) { projectV2Item { id } } }' -``` - -### Priority Criteria -- **Critical** — Bugs affecting majority of users -- **High** — In focus, will move the needle significantly -- **Medium** — In focus, incremental improvement -- **Low** — Nice to have -- **Backlog** — Uncertain importance - ---- - -## Q1 2026 Focus: Key Metrics - -**🎯 North Star:** Define your current north-star metric and date. - -Prioritize issues that impact these metrics: - -### Top-Down Metrics (Pricing Funnel) -| Metric | Goal | -|--------|------| -| Pricing Screen → Trial Start | 20-25% | -| Trial Start → Trial Converted | 35-40% | - -### Leading Indicators -| Metric | Goal | -|--------|------| -| 🔥 Activation Rate | 60% | -| 🔄 Early Retention | 40% | -| 💾 Investment Rate | 10% | - -
-Metric Definitions - -- **🔥 Activation Rate:** % of new trial users who view 5+ place profiles from multi-category searches in their first 7 days -- **🔄 Early Retention:** % of trial users with 3+ sessions in first 3 days -- **💾 Investment Rate:** % of trial users who save at least 1 place in their first 7 days -
- ---- - -## Step 6: Testing & Quality Requirements - -Every issue should explicitly address testing needs: - -### Unit Tests -- [ ] **New functionality** — Specify what unit tests should be added -- [ ] **Edge cases** — List specific edge cases to cover -- [ ] **Target coverage** — Aim for ≥80% on modified files - -### Regression Tests -- [ ] **Affected areas** — Identify existing functionality that could break -- [ ] **Test updates needed** — Note if existing tests need modification -- [ ] **Manual QA scope** — Define what needs manual testing before merge - -### Include in Issue Body -```markdown -## Testing Requirements -- [ ] Unit tests for [specific functionality] -- [ ] Update existing tests in [file/area] if affected -- [ ] Manual QA: [specific flows to verify] -- [ ] Regression check: [related features to verify still work] -``` - ---- - -## Step 7: Documentation & Tooling Updates - -Consider what else needs updating alongside the code: - -### Documentation -| What | When to Update | -|------|----------------| -| **README** | New setup steps, env vars, or major features | -| **API Docs** | New/changed endpoints, request/response formats | -| **Architecture Docs** | Structural changes, new patterns introduced | -| **Inline Comments** | Complex logic that needs explanation | - -### AI Agents & Commands -| What | When to Update | -|------|----------------| -| **Cursor Commands** | New workflows, changed processes | -| **Agent Rules** | New conventions, deprecated patterns | -| **Prompts/Instructions** | Changed context the AI needs to know | - -### Include in Issue Body (if applicable) -```markdown -## Documentation Updates -- [ ] Update README: [specific section] -- [ ] Update API docs: [endpoint/schema changes] -- [ ] Update agent rules: [new convention/pattern] -- [ ] Add inline comments for: [complex logic] -``` - ---- - -## Step 8: Final Verification Checklist - -Before sharing results, you MUST verify ALL of the following via GraphQL queries (not assumptions): - -### For EVERY Issue: -- [ ] Issue Type is set (query `issueType { name }` on the issue) -- [ ] Added to davidsulitzer.com project -- [ ] Status field is set (Todo, etc.) -- [ ] Priority field is set - -### For Cross-Repo Issues (any combination of iOS, Server, Tileserver): -- [ ] Blocking relationships exist (query `blockedBy` on each downstream issue — must show upstream issue(s)) -- [ ] NO sub-issue relationship (we use blocking, not parent/child hierarchy) - -### Verification Query (run for each issue): -```bash -gh api graphql -f query='{ - repository(owner: "8Gaston8", name: "") { - issue(number: ) { - title - issueType { name } - blockedBy(first: 5) { nodes { number repository { name } } } - parent { number } - } - } -}' -``` - -> ⚠️ **DO NOT SKIP VERIFICATION.** Mutations can return success without actually creating the relationship/setting the field. Always confirm with queries. - ---- - -## Step 9: Share Results - -Share the link(s) to the new issue(s) when done so I can review. - -Include a summary table showing: -- Issue links -- Issue Type (verified) -- Project Status & Priority (verified) -- Blocking relationships (verified, if applicable) diff --git a/.cursor/skills/new-release/SKILL.md b/.cursor/skills/new-release/SKILL.md deleted file mode 100644 index 6d73e0e..0000000 --- a/.cursor/skills/new-release/SKILL.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -name: new-release -description: Prepare a new iOS release — branch, PR, regression testing, App Store submission -disable-model-invocation: true ---- -# new-release - -Prepare and ship a new iOS release — from creating the branch all the way through to creating the draft GitHub release. For post-Apple-approval steps (publishing, analytics, stability score, issue tracking), see `/release-shipped`. - ---- - -## Step 1: Create the Release Branch - -Create a new release branch on the iOS repo from `main`: - -```bash -cd -git fetch origin main -git checkout -b release/VERSION origin/main -``` - -**Branch naming:** `release/VERSION` (e.g., `release/4.31.0`) - -> If this is a **hotfix**, work on the existing relevant branch instead of creating a new one from develop. - ---- - -## Step 2: Create the Draft PR - -1. Check the diff since the most recent release tag -2. Create a **draft PR** targeting `main` with full details of what changed -3. Assign **Aviad** (`aviadsteps`) as the reviewer -4. Assign **Gaston** (`8Gaston8`) as the assignee -5. Add the label `testflight` to the draft PR - ---- - -## Hotfix PR Management - -When creating a hotfix release that cherry-picks commits from existing PRs: - -1. **Create the release PR** (e.g., `release/4.28.1` → `develop`) with the cherry-picked commits -2. **Close the original PR(s)** without merging, adding a comment explaining: - - The changes were cherry-picked into the hotfix release branch - - Reference the release PR number (e.g., "Closing in favor of Hotfix PR #XXXX") - - Explain that merging both would create duplicate commits in history - - Note that the original work is preserved in the closed PR for reference - -**Why?** Cherry-picking creates new commits with different SHAs. If both PRs are merged, you get duplicate commits in git history. The release PR should be merged because: -- It has the release tag pointing to its commits -- Keeps git history clean and traceable - ---- - -## Step 3: Create Fibery Release Item - -Create a new release item in the Fibery database (nomenclature: `iOS - VERSION`): -https://steps.fibery.io/fibery/space/Product_Pipeline_Space/database/Release - -- Link all relevant Fibery dev tasks to the release item -- Share the link with Gaston - ---- - -## Step 4: Update What's New Content - -**BEFORE proceeding with regression testing**, update the in-app What's New content: - -1. Open `Steps/AppFlows/Whats New/WhatsNewContent.swift` -2. Update the `version` to match the new release version -3. Update `title` with a catchy headline for the main feature -4. Update `features` with 2-4 compelling bullet points -5. Test the What's New sheet appears correctly (Case 22b in regression plan) - -**This is a required step for every release with user-facing features!** - -If no notable features warrant a What's New announcement, return `nil` from `WhatsNewContent.current`. - ---- - -## Step 5: Regression Testing - -Carefully read the regression test plan at `REGRESSION_TESTING_PLAN.md` (in the iOS repo root) and help execute it **one test case at a time**. - -### Handling Regression Bugs - -When bugs are found during regression testing: - -1. **Create a tracking issue** for the release's regression pass (optional but recommended): - - Title: `[Regression] v{VERSION} Regression Tracking` - - Description: Summary of bugs found and release risk status - - Label: `regression` - - Use `/new-issue` skill with GraphQL for proper project integration - -2. **Create focused issues** for each concrete bug: - - Use `/new-issue` skill and follow its Step 0 pre-research requirements - - Include reproducible steps, expected vs actual behavior, and impacted files - - Use relevant domain labels only (for example `regression`, `fastlane`) and avoid generic `bug` label - - Link related issues in issue body and comments; do not require parent-child/sub-issue hierarchy - -3. **Apply triage before fixing** (see `REGRESSION_TESTING_PLAN.md`): - - Classify each bug by area, risk, and coupling - - Default fix path: land focused fix PR on `develop`, then cherry-pick/merge into release branch - - If bugs are unrelated (for example CI/Fastlane + product flow), split into separate PRs - - Only bundle fixes when they are tightly coupled in one flow/root cause - -4. **Execution order**: - - Continue regression testing and log all issues first - - Fix blockers/high-risk regressions for the release - - Track non-blockers into next version when appropriate - ---- - -## Step 6: App Store Release Notes - -Once regression testing is over, write a neat, concise, and cool release note for the App Store. - ---- - -## Step 7: Finalize the PR & Project - -1. **Update the PR** — convert from draft to ready for review -2. **Update the relevant davidsulitzer.com GitHub project items**, especially: - - Status - - iOS Version - - Server Version -3. **Add a PR comment** summarizing the regression test results clearly - ---- - -## Step 8: Tag & Create Draft GitHub Release - -1. **Add a tag** to the latest commit on the release branch: `VERSION` (without any "v" prefix) -2. **Create a draft GitHub release** at https://github.com/8Gaston8/davidsulitzer.com/releases pointing to the tag: - - Release name: `vVERSION` - - Include the release notes - - Add a **"Marketing Story"** section — a very short story to make people *feel* the change that was shipped - - Add a **"Top Down Metrics Impact"** section describing the expected impact: - -**🎯 Q1 2026 North Star:** $1M ARR for davidsulitzer.com - -### Top-Down Metrics (Pricing Funnel) -Pricing Screen → Trial Start (goal: 20-25%) -Trial Start → Trial Converted (goal: 35-40%) - -### Leading Indicators -🔥 Activation Rate (goal: 60%) -> "% of new trial users who view 5+ place profiles from multi-category searches in their first 7 days" -🔄 Early Retention (goal: 40%) -> "% of trial users with 3+ sessions in first 3 days" -💾 Investment Rate (goal: 10%) -> "% of trial users who save at least 1 place in their first 7 days" - ---- - -## What NOT to Do - -- **Don't skip the What's New update** — every release with user-facing features needs it -- **Don't rush regression testing** — log all bugs before starting fixes -- **Don't merge hotfix PRs and the original PRs** — close the original, keep history clean -- **Don't forget to tag the commit** — the tag is needed for the GitHub release -- **Don't publish the GitHub release** — keep it as a draft until the app is approved by Apple (see `/release-shipped`) - ---- - -## Next Step - -When Apple approves the release and it's live in the App Store, invoke `/release-shipped` to publish the GitHub release, annotate analytics, and close out project tracking. diff --git a/.cursor/skills/new-skill/SKILL.md b/.cursor/skills/new-skill/SKILL.md deleted file mode 100644 index 463a0f2..0000000 --- a/.cursor/skills/new-skill/SKILL.md +++ /dev/null @@ -1,514 +0,0 @@ ---- -name: new-skill -description: Create a new agent skill from scratch following best practices — strong routing description, "Use when / Don't use when" blocks, templates, guardrails, and verification steps. Handles cross-repo sync, branching, issues, and PRs. Don't use for updating existing skills (/update-skill). -disable-model-invocation: true ---- -# new-skill - -Create a new agent skill from scratch, following [OpenAI's skills best practices](https://developers.openai.com/blog/skills-shell-tips). Handles the full workflow: designing the skill content to quality standards, syncing across repos, and setting up issues/PRs. - ---- - -## When to Use - -- The user wants to create a brand-new skill that doesn't exist yet -- A recurring workflow needs to be codified into a reusable skill -- The user says "create a skill for X" or "I need a skill that does Y" - -## When NOT to Use - -- Updating an existing skill → use `/update-skill` -- The task is a simple one-off that won't recur → just do it, no skill needed -- Deleting or renaming a skill → `/update-skill` handles those cases -- Installing a third-party/external skill → different workflow - ---- - -## Step 0: Understand the Request - -Before writing anything, clarify: - -1. **What does this skill do?** — Get a clear, specific description of the workflow -2. **Who/what invokes it?** — User-triggered only (`disable-model-invocation: true`) or proactive model invocation? -3. **Which repos need it?** — Relevant to all repos, or specific ones? -4. **Are there similar skills?** — Check the existing inventory to avoid overlap or confusion - -> If the user's request is vague, **ask for clarification**. "Create a skill that helps with deployments" is too broad — which environment? Which repo? What steps? - ---- - -## Step 1: Research Before Writing - -### 1a: Check for Existing Similar Skills - -Search the skill inventory for overlap: - -```bash -# List all skills in the current repo -ls .cursor/skills/ .codex/skills/ 2>/dev/null - -# Check other repos via GitHub API -for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do - echo "=== $repo ===" - gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No skills" -done -``` - -If a similar skill exists, ask the user: "Should this be a new skill, or an update to `/existing-skill`?" - -### 1b: Research the Domain - -Before writing the skill, understand what it's automating: - -- **Read relevant code** — If the skill involves deploying, search for deploy scripts. If it involves testing, find existing test patterns. -- **Check existing workflows** — Are there manual runbooks, README instructions, or Slack threads that document this process? -- **Identify tools involved** — Which MCP tools, CLI commands, APIs, or services does this workflow use? -- **Talk to the user** — Ask what the current process looks like and what goes wrong. - -> The best skills codify something the team already does — they don't invent new processes. - ---- - -## Step 2: Write the Skill — Quality Standards - -This is the most important step. A well-written skill saves hours; a poorly-written one creates confusion. - -### 2a: The Description (Most Critical Field!) - -The description is the model's **routing logic** — it decides whether to invoke the skill. A skill with a bad description might as well not exist. - -**Formula:** What it does + When to use it + When NOT to use it (with alternatives) - -| Quality | Example | Why | -|---------|---------|-----| -| ❌ Bad | `deploy-backend-dev` | Just the name — tells the model nothing | -| ❌ Bad | `A helpful skill for deploying things` | Marketing copy — no routing signal | -| ✅ Good | `Deploy specific backend functions to the dev environment via the deployer API. Use after local testing passes and the branch is synced with develop. Don't use for prod deployments (/deploy-backend-prod) or for full deployments (always deploy individual functions).` | Clear what, when, and when-not | - -### 2b: Required Sections (Every Skill MUST Have) - -| Section | Purpose | Why It Matters | -|---------|---------|---------------| -| **Frontmatter** | name, description, disable-model-invocation | Metadata for the skill system | -| **"When to Use"** | Concrete triggers for invocation | Helps the model (and humans) know when this skill applies | -| **"When NOT to Use"** | Anti-triggers with alternatives | **Prevents misfires — can improve routing accuracy by ~20%** | -| **Numbered Steps** | Clear progression from start to finish | Agents follow procedural instructions best | -| **"What NOT to Do"** | Guardrails, anti-patterns, common mistakes | Agents make fewer errors when told what to avoid | -| **Verification Steps** | After critical actions, confirm they worked | Agents make mistakes; verification catches them early | - -### 2c: Strongly Recommended Sections - -| Section | When to Include | Why | -|---------|----------------|-----| -| **Templates** | Any repeatable output (PR comments, Slack messages, curl commands, issue bodies) | Templates inside skills are **free when unused but invaluable when needed** — they don't inflate tokens for unrelated queries | -| **Examples** | When behavior might be ambiguous | Worked examples reduce misinterpretation dramatically | -| **Quick Reference Tables** | Lookup data (field IDs, endpoint URLs, common patterns) | Saves the agent from searching every time | -| **Notes** | Edge cases, environment differences, related skills | Catches gotchas before they bite | - -### 2d: Negative Examples — The Secret Weapon - -The [OpenAI blog](https://developers.openai.com/blog/skills-shell-tips) found that making skills available can **drop correct triggering by ~20%** until you add "Don't use when..." guidance. For every "When to Use," write a corresponding "When NOT to Use" with the correct alternative: - -```markdown -## When NOT to Use - -- You're updating an existing skill → use `/update-skill` -- The task is a simple one-liner → just do it, no skill needed -- You need to delete a skill → `/update-skill` handles deletion too -``` - -For easily confused skill pairs, be extra explicit: - -```markdown -> ⚠️ This skill is for LOCAL testing only. -> If changes are already deployed to dev, use `/test-backend-changes-dev` instead. -``` - -### 2e: Writing Guidelines - -| Do ✅ | Don't ❌ | -|-------|---------| -| Be specific and actionable | Write vague guidance ("be careful") | -| Include exact commands (copy-pasteable) | Leave placeholders unexplained | -| Explain the "why" behind rules | Just list rules without context | -| Add negative examples ("Don't do X because Y") | Only write positive instructions | -| Reference other skills with `/skill-name` | Duplicate content from other skills | -| Use env vars for secrets (`$VAR_NAME`) | Hardcode credentials or tokens | -| Include verification after critical steps | Assume actions succeeded | -| Put templates inside the skill | Put templates in the system prompt | -| Keep skills self-contained | Require reading other files mid-execution | - ---- - -## Step 3: Quality Review Checklist - -Before saving, verify against this checklist: - -### Content Quality -- [ ] Description answers: What? When to use? When NOT to use? -- [ ] "When to Use" section has concrete triggers -- [ ] "When NOT to Use" section has alternatives for each case -- [ ] Steps are numbered and ordered logically -- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) -- [ ] Includes "What NOT to Do" guardrails -- [ ] Verification/confirmation steps after critical actions -- [ ] Templates for any repeatable output -- [ ] Examples where behavior might be ambiguous - -### Formatting -- [ ] Frontmatter `name` matches directory name -- [ ] `disable-model-invocation` is set (default: `true` unless proactive invocation is needed) -- [ ] References to other skills use `/skill-name` format -- [ ] No hardcoded secrets or tokens -- [ ] Markdown renders correctly (tables, code blocks, headers) - -### Routing Quality (The "Confusion Test") -- [ ] Could this skill be confused with any existing skill? If yes, add explicit disambiguation -- [ ] Would a model reading only the description know when to pick this vs alternatives? -- [ ] Are there edge cases where the wrong skill might be triggered? Add negative examples for those - ---- - -## Step 4: Determine Which Repos Need It - -| Skill Type | Typical Repos | -|-----------|---------------| -| Deployment skills | Repo being deployed | -| QA/testing skills | Repo being tested | -| Issue/project management skills | All repos (agents work in any) | -| Release skills | iOS repo primarily | -| General workflow skills | All repos | -| Skill management (like this one!) | All repos that have skills | - -When in doubt, **ask the user**. - ---- - -## Step 5: Apply to All Repos - -For **each repo** that needs the skill: - -### 5a: Create a Feature Branch - -```bash -cd -git fetch origin main # or origin/main for davidsulitzer.com -git checkout -b cursor/add-skill- origin/main -``` - -> ⚠️ **Never edit directly on `develop` or `main`.** -> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. - -### 5b: Write to BOTH Directories - -```bash -# Create directories -mkdir -p .cursor/skills/ -mkdir -p .codex/skills/ - -# Write SKILL.md to both locations (content MUST be identical) -``` - -### 5c: Update .gitignore (if needed) - -Some repos may have `.cursor` in their `.gitignore`. If so, add an exception: - -```gitignore -.cursor/* -!.cursor/skills/ -``` - -### 5d: Verify Sync Within the Repo - -```bash -diff .cursor/skills//SKILL.md .codex/skills//SKILL.md -``` - -If `diff` produces any output, fix it before committing. - -### 5e: Commit and Push - -```bash -git add .cursor/skills//SKILL.md .codex/skills//SKILL.md -git commit -m "skill(): add new skill — " -git push -u origin cursor/add-skill- -``` - ---- - -## Step 6: Create GitHub Issues (Non-Monorepo Repos Only) - -For each repo **except the davidsulitzer.com**, create a GitHub issue: - -```bash -gh issue create --repo stepscode/ \ - --title "skill(): add new skill — " \ - --body "" \ - --assignee 8Gaston8 \ - --label "fastlane" -``` - -### Issue Body Template - -```markdown -## Goal - -Create the `` skill in this repo. - -## Context - -[Brief description of what the skill does and why it's being created.] - -## Changes - -- New skill: `.cursor/skills//SKILL.md` -- New skill: `.codex/skills//SKILL.md` - -## Cross-Repo Sync - -This skill is being created across all relevant repos: -- [ ] `stepscode/davidsulitzer.com` — PR #XXX -- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) -- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX - -All instances must remain identical. - -## Out of Scope - -- Content changes to other skills - -## Acceptance Criteria - -- [ ] Skill file exists in `.cursor/skills//SKILL.md` -- [ ] Skill file exists in `.codex/skills//SKILL.md` -- [ ] Both copies are identical (`diff` produces no output) -- [ ] Content matches all other repos -``` - -### Set Issue Type to Task - -```bash -ISSUE_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' --jq '.data.repository.issue.id') - -gh api graphql -f query="mutation { updateIssue(input: { id: \"$ISSUE_ID\", issueTypeId: \"IT_kwDOBluP0c4BBYRC\" }) { issue { id } } }" -``` - -### Add to davidsulitzer.com Project (Status: In Review, Priority: Low) - -```bash -# Add to project -gh project item-add 2 --owner 8Gaston8 --url - -# Get project item ID -ITEM_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id') - -# Set Status: In Review -gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzgntIc4\", value: { singleSelectOptionId: \"2895eb73\" } }) { projectV2Item { id } } }" - -# Set Priority: Low -gh api graphql -f query="mutation { updateProjectV2ItemFieldValue(input: { projectId: \"\", itemId: \"$ITEM_ID\", fieldId: \"PVTSSF_lADOBluP0c4AxnzVzg5mgJc\", value: { singleSelectOptionId: \"9d5cb3c5\" } }) { projectV2Item { id } } }" -``` - ---- - -## Step 7: Create Pull Requests - -Create a PR for **every repo** that was changed: - -```bash -gh pr create \ - --repo stepscode/ \ - --base develop \ # or main for davidsulitzer.com - --head cursor/add-skill- \ - --title "skill(): add new skill — " \ - --body "" \ - --assignee 8Gaston8 -``` - -### PR Body Template - -```markdown -## Summary - -Create the `` skill — [one-line description]. - -## What This Skill Does - -[2-3 sentences describing the skill's purpose and workflow] - -## Cross-Repo Sync - -- `stepscode/davidsulitzer.com` — PR #XXX -- `8Gaston8/davidsulitzer.com` — PR #XXX - -All skill files are identical across repos. - -Closes # -``` - -> For davidsulitzer.com PRs, omit `Closes #` since there's no associated issue. - -### Assign & Request Review (MANDATORY) - -```bash -# Get PR node ID -PR_ID=$(gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' --jq '.data.repository.pullRequest.id') - -# Request review from Aviad -gh api graphql -f query="mutation { requestReviews(input: { pullRequestId: \"$PR_ID\", userIds: [\"MDQ6VXNlcjQ1NTMwOTMw\"] }) { pullRequest { number } } }" -``` - -> ⚠️ **Do this for EVERY PR created.** No exceptions. - -### Enable Auto-Merge (iOS Only) - -```bash -gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash -``` - -> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on other repos. - ---- - -## Step 8: Final Verification & Share Links - -### 8a: Verify Content Sync - -```bash -# Compare across repos (adjust paths based on which repos have the skill) -diff /.codex/skills//SKILL.md /.codex/skills//SKILL.md -``` - -### 8b: Verify Issues - -```bash -# For each non-davidsulitzer.com issue: -gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' -``` - -### 8c: Share All Links (MANDATORY) - -**You MUST always end by sharing every relevant link:** - -```markdown -## Here are all the links: - -### Pull Requests -- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX -- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX - -### Issues -- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX -- *(Monorepo: no issue needed)* - -### Summary Table -| Repo | PR | Issue | Status | Skill Synced | -|------|-----|-------|--------|-------------| -| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | -| davidsulitzer.com | [#XXX](url) | [#XXX](url) | Ready for Review | ✅ | -``` - -> ⚠️ **Do NOT skip sharing links.** Always use full URLs. - ---- - -## What NOT to Do - -- **Don't create a skill without researching existing ones** — you might be duplicating -- **Don't write a description that's just the skill name** — the description is routing logic -- **Don't skip "When NOT to Use"** — prevents misfires, improves routing by ~20% -- **Don't leave templates in the system prompt** — put them inside the skill (free when unused) -- **Don't hardcode secrets** — always use `$ENV_VAR` references -- **Don't create the skill in only one repo when it belongs in multiple** — sync all of them -- **Don't skip the quality checklist** — a sloppy skill is worse than no skill -- **Don't forget verification steps in the skill you're writing** — agents make mistakes -- **Don't create skills for one-off tasks** — skills are for recurring workflows - ---- - -## Reference: Skill File Template - -```markdown ---- -name: -description: -disable-model-invocation: true ---- -# - -[Opening line: what this skill does in one sentence] - ---- - -## When to Use - -- [Concrete trigger 1] -- [Concrete trigger 2] - -## When NOT to Use - -- [Anti-trigger 1] → use `/alternative` instead -- [Anti-trigger 2] → just do X directly - ---- - -## Step 1: [First Action] - -[Instructions with exact commands] - ---- - -## Step 2: [Second Action] - -[Instructions with verification] - ---- - -... - ---- - -## What NOT to Do - -- [Guardrail 1] — [why it matters] -- [Guardrail 2] — [why it matters] - ---- - -## Notes - -- [Environment info, related skills, edge cases] -``` - ---- - -## Reference: Project Field IDs - -| Field | Field ID | Key Options | -|-------|----------|-------------| -| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | In Review: `2895eb73`, Done: `782fbd7c` | -| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Low: `9d5cb3c5` | -| **Issue Type: Task** | `IT_kwDOBluP0c4BBYRC` | — | - ---- - -## Reference: Repo Quick Info - -| Repo | GitHub | Default Branch | -|------|--------|----------------| -| Monorepo | `stepscode/davidsulitzer.com` | `main` | -| iOS | `8Gaston8/davidsulitzer.com` | `develop` | -| Server | `8Gaston8/davidsulitzer.com` | `develop` | -| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | - ---- - -## Notes - -- **Skill names must be kebab-case** — lowercase, words separated by hyphens -- **One SKILL.md per skill** — each skill lives in its own directory -- **Both `.cursor/` and `.codex/` must always be in sync** within each repo -- **All repos must be in sync** with each other for shared skills -- **Monorepo gets PRs but NO issues** — only the subrepos get issues -- **Test after creating** — always suggest the user tests the skill with a real invocation -- Related skill: `/update-skill` — for updating, renaming, or deleting existing skills diff --git a/.cursor/skills/qa-start/SKILL.md b/.cursor/skills/qa-start/SKILL.md deleted file mode 100644 index da67d95..0000000 --- a/.cursor/skills/qa-start/SKILL.md +++ /dev/null @@ -1,318 +0,0 @@ ---- -name: qa-start -description: Comprehensive QA workflow for PRs - adapts to iOS, server, or davidsulitzer.com context -disable-model-invocation: true ---- -# qa-start - -Help me QA this PR step by step. - -## Step 1: Checkout & Analyze - -1. **Checkout to the PR's branch** (in the appropriate repo) -2. **Analyze the PR changes** to determine what type of QA is needed: - - **iOS-only changes** → Use iOS QA workflow - - **Server-only changes** → Use Server QA workflow - - **Mixed changes (davidsulitzer.com)** → Run both workflows as needed - ---- - -## iOS QA Workflow - -For iOS/client changes: - -### Server Dependency Check - -**If NO server dependency:** -- Proceed with manual Xcode testing -- I will guide you through test scenarios -- You build and run in Xcode - -**If server changes must deploy first (blocking):** -⚠️ **Skip Xcode testing for now.** Instead: -1. **Code review only** — Verify client code is compatible and ready for server changes -2. **Still mark PR as Ready for Review** — Don't wait for server deployment -3. **Note:** End-to-end testing will happen during regression testing on the release branch (created from develop where all client changes will have been merged) - -### iOS PR Comment Template (Full QA) - -```markdown -## ✅ QA Complete - -### Testing Summary -| Area | Status | Notes | -|------|--------|-------| -| Happy Path | ✅ Pass | Main feature works | -| Edge Cases | ✅ Pass | Handled gracefully | -| UI/UX | ✅ Pass | Looks good | -| Regression | ✅ Pass | No issues | - -### Devices/Simulators Tested -- iPhone 17 Pro (latest iOS) - ---- -*QA performed by Cursor Agent + manual Xcode testing* -``` - -### iOS PR Comment Template (Server-Blocked) - -```markdown -## ✅ Code Review Complete — Server Dependency - -### Code Review -- ✅ Client code reviewed and compatible with expected server changes -- ✅ Error handling in place for new/modified endpoints - -### Server Dependency -- **Server PR:** #XXX -- **Note:** E2E testing will occur during regression on the release branch - ---- -*Code review performed by Cursor Agent* -``` - ---- - -## Server QA Workflow - -For server/backend changes, follow this **5-phase process**: - -### Phase 1: Local Testing -> Reference: `/test-backend-changes-local` - -**All yarn commands must run from the `api/` directory:** - -1. `cd api` (or `cd /api` if in the davidsulitzer.com root) -2. Create a test plan covering all new/changed functionality -3. Verify backward and forward compatibility -4. Run relevant unit tests: `yarn test:file ` -5. Start local server: `yarn api::offline::no_debug` -6. Execute test plan against `http://localhost:3000` -7. Stop local server when done - -> **Note:** Local server connects to **dev-mobile database**, not prod. - -### Phase 2: Deploy to Dev -> Reference: `/deploy-backend-dev` - -**Pre-deployment checklist (MUST verify before deploying):** -1. **Branch is synced with develop** — Run `git fetch origin main && git merge origin/main` to sync. This prevents deployment failures from conflicting or outdated code. -2. **Tests are passing** — Verify that all tests for your new code pass locally. Failing tests = failed deployment. -3. **Resolve any merge conflicts** if they occur during sync -4. **Push changes** to remote - -**Deploy functions one by one:** -```bash -source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=dev-mobile&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" -``` - -> **Required env vars:** `DEPLOYER_BASE_URL`, `DEPLOYER_USER_NAME`, `DEPLOYMENT_API_KEY` (from `api/.env`) -> **Note:** If in the davidsulitzer.com, use `davidsulitzer.com/api/.env` instead. - -**Wait for deployment confirmation:** -```bash -# Wait 30-45 seconds for deployment to complete -sleep 40 - -# Then check #deployments_from_ci Slack channel (ID: C01BS5TKHAN) -# Look for ":tada: A deploy_specific_function_job job has succeeded!" -``` - -⚠️ **Deploy functions one at a time** — wait for success message before deploying next function. - -**Important:** -- Only deploy relevant functions — **no full deployments** -- A script might comment out some code — make sure to **uncomment** anything that shouldn't be commented before deploying - -### Phase 3: Test on Dev -> Reference: `/test-backend-changes-dev` - -1. Reuse the test plan from local testing -2. Get/refresh dev access token if needed (via `/renew` endpoint) -3. Execute test plan against `$DEV_API_BASE_URL` (from `api/.env`) -4. Verify all scenarios pass - -### Phase 4: Claim a logTest slot + Deploy to Prod -> Reference: `/deploy-backend-prod` - -**IMPORTANT:** Claim a `logTest` slot first (`logTest`, `logTest2` ... `logTest10`) so multiple QA runs can happen in parallel without collisions. - -1. Claim a slot in dev: -```bash -source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/claim" \ - -H "Content-Type: application/json" \ - -d '{ - "agent_id": "", - "branch": "", - "pr_number": - }' -``` - - Save the returned `slot` value (example: `logTest4`) as `` - - If claim fails with "no slots available", wait and retry; do not deploy to a random slot - -2. Modify `api/api/general.js` (or `davidsulitzer.com/api/api/general.js` from davidsulitzer.com root): - - Add action name to `allowedProdActions` array - - Add handler in `logTestFunction` - - **Temporarily comment out** this authorization check: - ```javascript - if (process.env.NAME_OF_STAGE !== "dev" && process.env.IS_OFFLINE !== "true") { - return { - statusCode: 401, - body: JSON.stringify({ message: "Unauthorized" }), - }; - } - ``` -3. Commit, push, and deploy the claimed slot to prod: -```bash -source .env && curl -s "$DEPLOYER_BASE_URL?requester=$DEPLOYER_USER_NAME&branch=&deployStage=prod&deployType=function&functionName=&key=$DEPLOYMENT_API_KEY" -``` - -4. Wait and verify deployment (same sleep + Slack check pattern) - -⚠️ **Only deploy to real functions (not logTest slots) if the user explicitly requests it!** - -### Phase 5: Test on Prod -> Reference: `/test-backend-changes-prod` - -1. Reuse the test plan -2. Get/refresh prod access token if needed -3. Test via the claimed slot endpoint: `$PROD_API_BASE_URL/v1/` (from `api/.env`) -4. Required headers for prod: - - `Origin: $MAP_MANAGER_ORIGIN` (from `api/.env`) - - `Content-Type: application/json` - - `Authorization: ` -5. Verify all scenarios pass -6. Release the slot when done (always release, even if tests fail): -```bash -source .env && curl -s -X POST "$DEV_API_BASE_URL/logtest-slots/release" \ - -H "Content-Type: application/json" \ - -d '{ - "slot": "", - "agent_id": "" - }' -``` - -> **Note:** If changes were deployed to real functions (not just logTest slots), test using those real endpoints. - -### Server PR Comment Template - -```markdown -## ✅ QA Complete - -### Testing Summary -| Phase | Status | Details | -|-------|--------|---------| -| Local Testing | ✅ Pass | X unit tests passing | -| Dev Deployment | ✅ Done | Functions deployed | -| Dev Testing | ✅ Pass | All scenarios verified | -| Prod Deployment | ✅ Done | Deployed to logTest | -| Prod Testing | ✅ Pass | All scenarios working | - -### Compatibility -- ✅ Backward compatible -- ✅ Forward compatible - ---- -*QA performed by Cursor Agent* -``` - ---- - -## Deployment Monitoring Pattern - -When deploying, always use this pattern to wait and verify: - -```bash -# 1. Trigger deployment (returns immediately) -curl "$DEPLOYER_BASE_URL?..." - -# 2. Wait for CI to complete (30-45 seconds typically) -sleep 40 - -# 3. Check Slack for success message -# Use MCP tool: slack_get_channel_history with channel_id: C01BS5TKHAN -# Look for: ":tada: A deploy_specific_function_job job has succeeded!" -# Match the job number from your deployment request -``` - ---- - -## Monorepo Mixed Changes - -When the PR has both iOS and server changes: - -1. **First:** Complete the Server QA workflow (all 5 phases) -2. **Then:** Help with iOS QA (or code review if server-blocked) -3. Document both in the PR comment - ---- - -## Finalize PR - -After QA completes: - -1. **Commit & Push** any remaining changes -2. **Add detailed PR comment** using the appropriate template above -3. **Update the Project Board status** (see below) -4. **Notify the right people** based on the status - -### Step A: Determine the Status - -**Ask yourself: Would a screenshot look different before vs after your changes?** - -| Answer | Status | What type of review Gaston needs to do | -|--------|--------|----------------------------------------| -| **YES** (changed layouts, colors, text, images, animations, spacing, or anything the user sees) | "Human QA" | Build in Xcode and visually verify the UI changes | -| **NO** (pure logic, API, analytics, refactoring, performance, crash fixes) | "In Review" | Review the PR diff/code only (no Xcode needed) | - -### Step B: Update Project Board Status - -Use GitHub GraphQL API to update the status: - -**1. Get the issue's project item ID:** -```graphql -query { - repository(owner: "8Gaston8", name: "REPO_NAME") { - issue(number: ISSUE_NUMBER) { - projectItems(first: 10) { - nodes { - id - project { id } - } - } - } - } -} -``` -Replace `REPO_NAME` with `davidsulitzer.com`, `davidsulitzer.com`, or `davidsulitzer.com` as appropriate. -Find the item where `project.id = ""` (davidsulitzer.com project) - -**2. Update the status:** -```graphql -mutation { - updateProjectV2ItemFieldValue(input: { - projectId: "" - itemId: "ITEM_ID_FROM_STEP_1" - fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4" - value: { singleSelectOptionId: "OPTION_ID" } - }) { - projectV2Item { id } - } -} -``` - -**Status option IDs:** -- "Human QA": `6a5c111e` -- "In Review": `2895eb73` - -### Step C: Post Comment for Gaston's Review - -**Keep the PR as a draft.** Gaston will mark it Ready for Review after his review. - -**If "Human QA":** -- Post PR comment: `🔍 Visual QA needed — @8Gaston8 please build in Xcode and verify the UI changes look correct.` - -**If "In Review":** -- Post PR comment: `✅ QA complete — @8Gaston8 no visual changes, just verify the code looks good.` - -> **Note:** Do NOT mark the PR as Ready for Review or enable auto-merge. Gaston will do this after reviewing the AI's output. diff --git a/.cursor/skills/refine-spec/SKILL.md b/.cursor/skills/refine-spec/SKILL.md deleted file mode 100644 index a196e74..0000000 --- a/.cursor/skills/refine-spec/SKILL.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -name: refine-spec -description: Refine and improve an existing GitHub issue's spec through codebase research, edge case analysis, and iterative PM discussion. -disable-model-invocation: true ---- -# refine-spec - -Take an existing GitHub issue and elevate its spec to production-ready quality. This skill guides you through codebase research, section compliance (per the `/new-issue` skill), edge case discovery, and iterative refinement with the PM — so the AI agent or developer who picks it up makes zero mistakes. - ---- - -## When to Use - -- A spec exists but feels thin, vague, or missing context -- A design decision was just made and needs to be baked into the spec -- The PM wants to stress-test edge cases before handing off to development -- An issue was created quickly and now needs the full `/new-issue` treatment -- The spec drifted after discussion and needs to be reconciled with decisions made - ---- - -## Step 0: Read the Issue - -### 0a: Fetch the Full Issue - -```bash -gh issue view --repo stepscode/ --json title,body,labels,assignees,state,milestone -``` - -Take note of: -- **Labels** — especially `design` (triggers mandatory Design Brief sections) and `fastlane` -- **Milestone** — context for prioritization -- **Current body** — what's already there vs what's missing - -### 0b: Identify What the User Wants to Change - -Ask yourself: -1. Is this a **full rewrite** or **targeted improvements**? -2. Did the PM make specific design decisions that need to be captured? -3. Are there new requirements, constraints, or scope changes? -4. Is the PM asking for edge case analysis? - -> If the user's request is vague (e.g., "make this spec better"), ask what aspect they want improved: completeness, clarity, edge cases, technical depth, or all of the above. - ---- - -## Step 1: Pre-Research the Codebase - -Before changing a single word, investigate the codebase. This is the same research from `/new-issue` Step 0 — but applied to an existing issue that may have skipped this step. - -### 1a: Search for Relevant Code - -Search **all repos** for keywords related to the feature/bug: - -```bash -# 1. Always search the current repo first -rg -l "" . - -# 2. If in the davidsulitzer.com with submodules checked out, also search sibling repos: -rg -l "" davidsulitzer.com/ 2>/dev/null - -# 3. If in a standalone repo checkout (sibling dirs won't exist), use GitHub API: -gh search code "" --repo 8Gaston8/davidsulitzer.com --limit 10 -``` - -> **Note:** The `2>/dev/null` suppresses errors when sibling directories don't exist (standalone checkouts). Always search `.` first so you never miss the current repo's code. - -**Capture for each relevant file:** -- Full path and key line numbers -- What the code does (brief description) -- How it relates to the feature being specified - -### 1b: Check Recent Changes - -```bash -git log --oneline -10 -- -``` - -Note any recent PRs that touched the area — they may contain context or introduce constraints. - -### 1c: Check for Related Issues - -```bash -# Open issues -gh issue list --repo stepscode/ --search "" --limit 10 - -# Recently closed (possible regression context) -gh issue list --repo stepscode/ --search "" --state closed --limit 5 -``` - -### 1d: Check Test Coverage - -```bash -# iOS tests -rg -l "" davidsulitzer.com/AtlyTests/ 2>/dev/null - -# Server tests -rg -l "" davidsulitzer.com/api/test/ 2>/dev/null -``` - -### 1e: Identify Dependencies - -Look for: -- Feature flags that gate the behavior -- API endpoints involved (grep for route definitions) -- Third-party services (analytics, RevenueCat, Firebase, etc.) -- Caching layers -- Cross-repo dependencies (iOS ↔ Server ↔ Tileserver) - -> ⚠️ **Do NOT skip pre-research.** Even if the issue already has some code references, they may be outdated or incomplete. Fresh research catches drift. - ---- - -## Step 2: Audit Sections Against the `/new-issue` Skill - -Compare the existing issue body against the required and conditional sections from `/new-issue` Step 3. Build a gap table: - -### Required Sections (MUST exist in every issue) - -| Section | Present? | Notes | -|---------|----------|-------| -| **Pre-Research Findings** | ✅/❌ | Code refs, recent changes, related issues, test coverage, dependencies | -| **Goal** | ✅/❌ | Clear one-liner | -| **Context/Background** | ✅/❌ | Why this matters | -| **Requirements** | ✅/❌ | What needs to be built | -| **Out of Scope** | ✅/❌ | What's NOT part of this issue | -| **Acceptance Criteria** | ✅/❌ | Checkboxes for definition of done | -| **Testing Requirements** | ✅/❌ | Unit tests, regression, manual QA | -| **Related Issues/PRs** | ✅/❌ | Cross-references | - -### Conditional Sections (include when applicable) - -| Section | Applicable? | Present? | Notes | -|---------|------------|----------|-------| -| **Problem + Design Brief + Design Requirements** | Only if `design` label | ✅/❌ | ⚠️ REQUIRED when `design` label is present | -| **Technical Approach** | Features/refactors | ✅/❌ | Architecture, files to change | -| **Constraints** | Performance/compat/deadline | ✅/❌ | | -| **Feature Flag** | New features, risky changes | ✅/❌ | | -| **Reference Implementation** | Similar patterns exist | ✅/❌ | | -| **Risk/Rollback** | Medium+ risk changes | ✅/❌ | | -| **Analytics Requirements** | New analytics events | ✅/❌ | | -| **Documentation Updates** | README, API docs, agent rules | ✅/❌ | | - -> Present this gap table to the PM. Let them decide which missing sections are relevant before you start writing. - ---- - -## Step 3: Analyze Edge Cases - -This is where specs go from "good" to "bulletproof." For every feature in the spec, systematically think through: - -### 3a: State Transitions - -- What are all the possible states? (e.g., loading, active, inactive, error, empty) -- What triggers each transition? -- Can the user get stuck in an unexpected state? -- What happens on state reset (new session, navigation, app restart)? - -### 3b: Interaction Combinations - -- What happens when this feature interacts with other features in the same area? -- If there's a toggle/filter, does it survive navigation, back-button, other selections? -- What about concurrent actions (rapid tapping, network in flight)? - -### 3c: Data Edge Cases - -- What if the data is empty? (No results, no items, no content) -- What if the data is missing fields? (Null/undefined values from API) -- What if the data is stale? (Cached, outdated, timezone mismatch) -- What about pagination? Does the feature work across pages? - -### 3d: UI Edge Cases - -- What about different sheet/modal states? (Minimized, expanded, transitioning) -- What about transient states? (Search focused, autocomplete showing, animations in progress) -- Dark mode? Dynamic Type? VoiceOver? -- Does it work when related UI elements are hidden/empty? - -### 3e: Platform Edge Cases - -- App backgrounding and foregrounding -- App kill and restart -- Network errors mid-action -- Low connectivity / timeout scenarios - -### 3f: Present Edge Cases to the PM - -**Do NOT silently make decisions about edge cases.** Present them as a table: - -| Scenario | Question / Suggested Behavior | -|----------|-------------------------------| -| [Edge case 1] | [What should happen? / Here's what I suggest: ...] | -| [Edge case 2] | [What should happen? / Here's what I suggest: ...] | - -Let the PM confirm or override each one before writing them into the spec. - -> **The goal is to eliminate ambiguity.** Every edge case that's undocumented is a coin-flip for the implementer. Document the decision, even if the decision is "we don't handle this — it's out of scope." - ---- - -## Step 4: Draft the Updated Spec - -### 4a: Preserve What's Good - -Don't rewrite sections that are already solid. Only modify what needs improvement. - -### 4b: Add Missing Sections - -Fill in every gap identified in Step 2, using the templates from `/new-issue` Step 3. - -### 4c: Weave in Edge Cases - -Edge cases should NOT be a standalone dump. Integrate them: -- **Behavioral edge cases** → Add to an "Edge Cases & Clarifications" table -- **Empty/error states** → Add to the relevant spec section (e.g., "Empty State" subsection) -- **State management edge cases** → Add to the "State Management" or "Toggle Behavior" section -- **Testing edge cases** → Add to "Testing Requirements" as specific test cases - -### 4d: Add Pre-Research Findings - -If the original spec lacked code references, add the **Pre-Research Findings** section with: -- Relevant file paths + line numbers + brief descriptions -- Recent changes (PRs that touched the area) -- Related issues (open and closed) -- Test coverage status -- Dependencies (APIs, feature flags, services) - -### 4e: Ensure Technical Approach is Implementation-Ready - -The Technical Approach section should give the implementer a clear roadmap: -- **Files to change** — with specific descriptions of what changes in each file -- **Key implementation notes** — gotchas, caveats, patterns to follow or avoid -- **Recommended approach** — if there are multiple valid approaches, state which one and why - ---- - -## Step 5: Update the Issue - -### 5a: Apply the Changes - -```bash -gh issue edit --repo stepscode/ --body "$(cat <<'EOF' - -EOF -)" -``` - -> ⚠️ **This replaces the entire body.** Always include ALL sections — both unchanged and updated — in the new body. Never use `--body-file` with partial content. - -### 5b: Labels — Do NOT Remove - -> ⚠️ **NEVER remove existing labels during spec refinement.** Labels like `design` are kept permanently on the issue — they indicate the issue *involves* design work, not that design is pending. Even if the design decision has been made, the label stays. - -You may **add** labels if the refinement reveals they're needed (e.g., adding `fastlane`): - -```bash -gh issue edit --repo stepscode/ --add-label "fastlane" -``` - -### 5c: Do NOT Update Project Status During Refinement - -> ⚠️ **Do NOT change the project status at any point during spec refinement.** Regardless of what status the issue was in when the conversation started, leave it as-is until the PM explicitly confirms the spec is finalized. - -**Only when the PM gives final approval** (e.g., "looks good, this is ready"), update the status to **Ready for Dev**: - -```bash -# Get project item ID via the issue itself (avoids the last-N item window problem) -gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { projectItems(first: 5) { nodes { id project { title } } } } } }' --jq '.data.repository.issue.projectItems.nodes[] | select(.project.title == "davidsulitzer.com") | .id' - -# Move to "Ready for Dev" — ONLY after PM final approval -gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "cac3311f" } }) { projectV2Item { id } } }' -``` - -> **Note:** We query `projectItems` on the issue itself rather than scanning the last N project items. This works regardless of how many items are in the project. - ---- - -## Step 6: Iterate with the PM - -Spec refinement is rarely one-shot. After the first update: - -### 6a: Summarize What Changed - -Tell the PM exactly what you added, removed, or modified — in plain language, not diffs. - -### 6b: Flag Open Questions - -If any edge cases or design decisions are still unresolved, list them explicitly: - -> **Still need your input on:** -> 1. [Question about behavior X] -> 2. [Question about scope Y] -> 3. [Question about edge case Z] - -### 6c: Apply Follow-Up Changes - -When the PM responds with decisions: -1. Update the spec immediately -2. Summarize what changed in this iteration -3. Repeat until the PM is satisfied - -> **Keep iterating.** A spec is done when the PM says it's done, not when you think it's complete. - ---- - -## Step 7: Final Quality Check - -Before declaring the spec done, verify: - -### Content Quality -- [ ] Every section from Step 2's gap table is addressed (present or explicitly N/A) -- [ ] Edge cases are documented with clear expected behaviors -- [ ] Technical approach references specific file paths and line numbers -- [ ] Acceptance criteria are concrete and testable (not vague) -- [ ] Testing requirements include unit tests, regression tests, AND manual QA -- [ ] Out of scope is explicit (prevents scope creep during implementation) - -### Issue Metadata -- [ ] Labels are unchanged (never remove existing labels — especially `design`) -- [ ] Milestone is set -- [ ] Assignee is set -- [ ] Issue type is set (Feature / Bug / Task) -- [ ] Project status is unchanged (only update to "Ready for Dev" after PM gives explicit final approval) - -### Readability -- [ ] A developer or AI agent reading this spec for the first time can implement the feature without asking clarifying questions -- [ ] No contradictions between sections -- [ ] No placeholder text or TODOs left in the body -- [ ] Tables are formatted correctly (render check) - ---- - -## Anti-Patterns (What NOT to Do) - -| Anti-Pattern | Why It's Bad | Do This Instead | -|-------------|-------------|-----------------| -| Rewriting the entire spec without reading it first | Loses context and decisions already made | Read first, preserve what's good | -| Making design decisions without asking the PM | You're refining, not designing | Present options, let PM decide | -| Dumping edge cases without suggested behaviors | Shifts the thinking burden to the implementer | Always include a suggested behavior | -| Adding sections just to check a box | Bloats the spec with low-value content | Only add sections that provide real value | -| Ignoring the `design` label requirements | The `/new-issue` skill REQUIRES Problem + Design Brief + Design Requirements when `design` label is present | Check labels first, comply with the section requirements | -| Removing labels during refinement | Labels are permanent markers — `design` means the issue involves design, not that design is pending | Never remove labels; only add if needed | -| Updating project status without PM approval | Only the PM decides when a spec is ready for development | Leave status unchanged until PM explicitly says "ready" | -| Updating the spec without telling the PM what changed | PM can't review what they can't see | Always summarize changes after each update | -| Skipping pre-research because "the issue already has context" | Existing context may be outdated or incomplete | Always do fresh research | -| Writing vague acceptance criteria | "Works correctly" is not testable | Be specific: "When X happens, Y should result" | - ---- - -## Quick Reference: Relationship to Other Skills - -| Skill | Relationship | -|-------|-------------| -| `/new-issue` | `refine-spec` applies `/new-issue`'s section requirements to an EXISTING issue. If the issue was created without `/new-issue`, this skill brings it up to standard. | -| `/qa-start` | A well-refined spec makes QA faster. The testing requirements section feeds directly into QA checklists. | -| `/update-skill` | If the refinement process reveals a gap in any skill (including this one), use `/update-skill` to improve it. | diff --git a/.cursor/skills/release-shipped/SKILL.md b/.cursor/skills/release-shipped/SKILL.md deleted file mode 100644 index ab001d8..0000000 --- a/.cursor/skills/release-shipped/SKILL.md +++ /dev/null @@ -1,278 +0,0 @@ ---- -name: release-shipped -description: Post-Apple-approval steps — publish release, analytics annotation, close out tracking, assign iOS versions to server & tile-server issues -disable-model-invocation: true ---- -# release-shipped - -Run this skill **after Apple approves the release and it's live in the App Store**. This handles publishing the GitHub release, annotating analytics, closing out project tracking, and assigning iOS Versions to server/tile-server issues that shipped between releases. - -> **Prerequisite:** The `/new-release` skill must have been completed first — the draft GitHub release, tag, and PR should already exist. - ---- - -## Step 1: Publish the GitHub Release - -Convert the draft GitHub release to a published (non-draft) release: - -```bash -# Find the draft release for this version -gh release list --repo 8Gaston8/davidsulitzer.com --json tagName,isDraft | jq '.[] | select(.isDraft == true)' - -# Publish it (replace VERSION with the actual version, e.g. "v4.30.0") -gh release edit VERSION --repo 8Gaston8/davidsulitzer.com --draft=false -``` - -> **Verify:** `gh release view VERSION --repo 8Gaston8/davidsulitzer.com --json isDraft` should return `false`. - ---- - -## Step 2: Record Release Metrics (Optional) - -If you use a product analytics tool, record the release timestamp (actual App Store live date) and attach a short note for future analysis: - -```bash -curl -X POST \ - '' \ - --user "$ANALYTICS_SERVICE_ACCOUNT_USERNAME:$ANALYTICS_SERVICE_ACCOUNT_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "description": "iOS VERSION released", - "datetime": "YYYY-MM-DDTHH:MM:SSZ", - "tags": ["ios-release", "VERSION"] - }' -``` - -> **Note:** Set `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` in your environment if you use this optional step. - ---- - -## Step 3: List New Analytics Events (If Any) - -If any new analytics events were introduced in this release, list them so reporting and dashboards can be updated. - ---- - -## Step 4: Update GitHub Issue Version Tracking — iOS Issues - -Check all GitHub issues associated with PRs that have been merged since the last version and update their davidsulitzer.com GitHub project items: - -1. **Find merged PRs** since the last release tag -2. **For each merged PR**, find the connected GitHub issues (via GraphQL `timelineItems` with `CONNECTED_EVENT`) -3. **Update the project items** — set the iOS Version field on each issue's project item - -```graphql -# Example: Get connected issues for a PR -{ - repository(owner: "8Gaston8", name: "davidsulitzer.com") { - pullRequest(number: PR_NUMBER) { - timelineItems(first: 50, itemTypes: [CONNECTED_EVENT]) { - nodes { - ... on ConnectedEvent { - subject { ... on Issue { number title } } - } - } - } - } - } -} -``` - -### Setting the iOS Version field - -```graphql -mutation { - updateProjectV2ItemFieldValue(input: { - projectId: "" - itemId: "" - fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" - value: { text: "" } - }) { - projectV2Item { id } - } -} -``` - -> ⚠️ **Always double-check** that the associated PR was merged *after* the previous version was released. Verify by checking the latest commit tags. - ---- - -## Step 5: Assign iOS Versions to Server & Tile-Server Issues - -Server and tile-server issues ship independently (via their own deploy cycles), but we want them tagged with the **iOS Version that was current when they shipped**. This creates a unified timeline: when you look at an iOS version, you see everything that shipped around that time. - -### The Rule - -> If a server or tile-server issue was closed (shipped) between iOS version A's release date and iOS version B's release date, assign it **iOS Version = A**. - -### 5a: Build the iOS Release Timeline - -```bash -# Get the last N iOS releases with their dates -gh release list --repo 8Gaston8/davidsulitzer.com --limit 20 -``` - -This gives you ordered pairs like: -- `v4.29.0` → `2026-01-27T08:55:57Z` -- `v4.30.0` → `2026-02-12T16:47:11Z` - -### 5b: Find Unassigned Server Issues - -Query all project items and find those from `davidsulitzer.com` that: -- Have **Status = Done** (shipped) -- Have a **Server Version** set (confirms it's a server issue that was deployed) -- Do **NOT** have an **iOS Version** set - -```graphql -# Paginate through all project items (100 at a time) -{ - organization(login: "8Gaston8") { - projectV2(number: 2) { - items(first: 100, after: CURSOR) { - pageInfo { hasNextPage endCursor } - nodes { - id - fieldValues(first: 20) { - nodes { - ... on ProjectV2ItemFieldTextValue { - text - field { ... on ProjectV2Field { name } } - } - ... on ProjectV2ItemFieldSingleSelectValue { - name - field { ... on ProjectV2SingleSelectField { name } } - } - } - } - content { - ... on Issue { - number - title - repository { name } - closedAt - state - } - } - } - } - } - } -} -``` - -Filter for items where: -- `repository.name == "davidsulitzer.com"` -- `status == "Done"` -- `Server Version` field is set (non-empty) -- `iOS Version` field is empty - -### 5c: Find Unassigned Tile-Server Issues - -Same query as above, but filter for: -- `repository.name == "davidsulitzer.com"` -- `status == "Done"` -- `Tile Server Version` field is set (non-empty) -- `iOS Version` field is empty - -### 5d: Assign iOS Versions - -For each unassigned issue, compare its `closedAt` timestamp against the iOS release timeline. Find the **latest iOS release that was released before the issue was closed** — that's the iOS Version to assign. - -**Algorithm:** -``` -for each unassigned_issue: - closed_at = issue.closedAt - ios_version = None - for release in ios_releases_sorted_by_date_ascending: - if release.date <= closed_at: - ios_version = release.version - else: - break - assign ios_version to issue -``` - -**Set the iOS Version:** - -```graphql -mutation { - updateProjectV2ItemFieldValue(input: { - projectId: "" - itemId: "" - fieldId: "PVTF_lADOBluP0c4AxnzVzg7CAVA" - value: { text: "" } - }) { - projectV2Item { id } - } -} -``` - -### 5e: Verify Assignments - -After updating, re-query each item to confirm the iOS Version was set correctly: - -```graphql -{ - node(id: "") { - ... on ProjectV2Item { - fieldValues(first: 20) { - nodes { - ... on ProjectV2ItemFieldTextValue { - text - field { ... on ProjectV2Field { name } } - } - } - } - content { - ... on Issue { number title } - } - } - } -} -``` - -### 5f: Report to the User - -Present the assignments in a clear table: - -| Issue | Repo | Server/Tile Version | Closed | Assigned iOS Version | -|-------|------|---------------------|--------|---------------------| -| #1234 | davidsulitzer.com | 3.140.0 | Jan 20 | 4.28.0 | -| #175 | davidsulitzer.com | 1.38.0 | Feb 5 | 4.29.0 | - -Also report any issues that could NOT be assigned (e.g., closed before the earliest known iOS release). - ---- - -## Project Field Reference - -| Field | Field ID | Type | -|-------|----------|------| -| **iOS Version** | `PVTF_lADOBluP0c4AxnzVzg7CAVA` | Text | -| **Server Version** | `PVTF_lADOBluP0c4AxnzVzg7CD1Q` | Text | -| **Tile Server Version** | `PVTF_lADOBluP0c4AxnzVzg9t2v4` | Text | -| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | SingleSelect | -| **Project ID** | `` | — | - -**Status Option IDs:** -- Done: `782fbd7c` -- Ready for Release: `2f956420` - ---- - -## What NOT to Do - -- **Don't run this before Apple approves** — the analytics annotation timestamp must reflect the actual release date -- **Don't forget to publish the GitHub release** — it stays as a draft until this skill is invoked -- **Don't update issue versions for PRs merged before the previous release** — only PRs merged after the last tag -- **Don't skip server/tile-server issues** — they ship independently but still need iOS Version assignment for timeline tracking -- **Don't guess iOS version windows** — always use the actual release dates from `gh release list`, not approximations -- **Don't assign iOS Versions to server/tile-server issues that aren't Done** — only issues with Status = Done and a Server/Tile Server Version set should get an iOS Version - ---- - -## Prerequisites - -- `/new-release` must have been completed (draft release, tag, and PR exist) -- The app must be **approved and live** in the App Store -- Environment variables `ANALYTICS_SERVICE_ACCOUNT_USERNAME` and `ANALYTICS_SERVICE_ACCOUNT_SECRET` must be set (from `davidsulitzer.com/api/.env`) -- `gh` CLI must be authenticated with access to `8Gaston8/davidsulitzer.com`, `8Gaston8/davidsulitzer.com`, and `8Gaston8/davidsulitzer.com` repos diff --git a/.cursor/skills/respect-figma/SKILL.md b/.cursor/skills/respect-figma/SKILL.md deleted file mode 100644 index 4b5a616..0000000 --- a/.cursor/skills/respect-figma/SKILL.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -name: respect-figma -description: Fetch design context, screenshots, variables, and assets from Figma and translate them into production code. Works with both the Figma MCP (local/desktop) and the Figma REST API (cloud/CI agents). Trigger when a task involves Figma URLs, node IDs, or design-to-code implementation. -disable-model-invocation: false ---- - -# Respect Figma - -Fetch design data from Figma and translate it faithfully into production code. -Works in **two modes** depending on where the agent is running — see "Choose your tool" below. - -For setup details (env vars, config, verification), see `references/figma-config.md`. -For a tool catalog and prompt patterns, see `references/figma-tools-and-prompts.md`. - ---- - -## 0 — Choose Your Tool - -| Environment | How to access Figma | -|---|---| -| **Local / desktop** (Cursor, Codex desktop) | Use the **Figma MCP** tools (`get_design_context`, `get_screenshot`, etc.). The MCP server communicates with the running Figma desktop app. | -| **Cloud / CI / headless** (Codex cloud, GitHub Actions) | The MCP is **not available**. Use the **Figma REST API** with an access token from environment secrets. | - -### Figma REST API quick reference - -Base URL: `https://api.figma.com` - -| What you need | Endpoint | Notes | -|---|---|---| -| Design data (node tree, styles, layout) | `GET /v1/files/:fileKey/nodes?ids=:nodeId` | Equivalent to `get_design_context` | -| Rendered image / screenshot | `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` | Equivalent to `get_screenshot` | -| Export SVG asset | `GET /v1/images/:fileKey?ids=:nodeId&format=svg` | For downloading vector assets | -| Variables and styles | `GET /v1/files/:fileKey/variables/local` | Equivalent to `get_variable_defs` | -| File styles | `GET /v1/files/:fileKey/styles` | Color, text, effect, and grid styles | - -**Authentication:** `X-Figma-Token: ` header, or `Authorization: Bearer `. -Use whatever token is available in your environment secrets (`FIGMA_ACCESS_TOKEN`, `FIGMA_OAUTH_TOKEN`, etc.). - -### Extracting fileKey and nodeId from a Figma URL - -``` -https://figma.com/design/:fileKey/:fileName?node-id=:int1-:int2 -``` - -- `fileKey` → the segment after `/design/` -- `nodeId` → replace the `-` in `int1-int2` with `:` → `int1:int2` - -Example: `https://figma.com/design/AbC123/MyFile?node-id=42-99` -→ fileKey = `AbC123`, nodeId = `42:99` - ---- - -## 1 — Required Flow (Do Not Skip) - -Whether you are using the MCP or the REST API, the flow is the same conceptually: - -1. **Get design context** for the exact node(s). - - MCP: `get_design_context(nodeId, fileKey)` - - REST: `GET /v1/files/:fileKey/nodes?ids=:nodeId` - -2. **If the response is too large or truncated**, get the high-level node map first, then re-fetch only the required child node(s). - - MCP: `get_metadata(nodeId, fileKey)` → then `get_design_context` on smaller nodes - - REST: same endpoint with individual child node IDs - -3. **Get a screenshot** for visual reference of the node/variant being implemented. - - MCP: `get_screenshot(nodeId, fileKey)` - - REST: `GET /v1/images/:fileKey?ids=:nodeId&format=png&scale=2` - -4. **Only after you have both** the design context AND the screenshot, download any needed assets and start implementation. - -5. **Translate** the Figma output into the project's conventions, styles, and framework. Reuse existing color tokens, components, and typography. - -6. **Validate** the final UI against the Figma screenshot for 1:1 visual parity before marking complete. - -> **Always start from the Figma source of truth.** Do not guess icons from code comments, asset names, or SF Symbol approximations. - ---- - -## 2 — Implementation Rules - -- Treat the Figma MCP output (typically React + Tailwind) as a **representation of design and behavior**, not as final code style. -- Replace Tailwind utility classes with the project's preferred utilities / design-system tokens when applicable. -- **Reuse existing components** (buttons, inputs, typography, icon wrappers) instead of duplicating functionality. -- Use the project's **color system, typography scale, and spacing tokens** consistently. -- Respect existing **routing, state management, and data-fetch patterns** already adopted in the repo. -- Strive for **1:1 visual parity** with the Figma design. When conflicts arise, prefer design-system tokens and adjust spacing or sizes minimally to match visuals. -- Validate the final UI against the Figma screenshot for both look and behavior. - ---- - -## 3 — Asset Handling - -### General rules - -- The Figma MCP provides an assets endpoint which can serve image and SVG assets. -- **If the MCP returns a localhost source** for an image or SVG, use that source directly. Do NOT create placeholders. -- **Do NOT import/add new icon packages** — all assets should come from the Figma payload. -- When using the REST API, export assets via `GET /v1/images/:fileKey?ids=:nodeId&format=svg` (or `png`/`pdf`). - -### iOS-specific asset workflow - -When extracting icons or images for an iOS / Xcode project: - -1. Call `get_design_context` (or REST equivalent) on the parent view or the specific node. -2. Identify the asset by its `data-name` attribute and corresponding download URL. -3. Download the SVG from the asset URL. -4. Convert to PDF for the Xcode asset catalog: - ```bash - rsvg-convert -f pdf -o icon.pdf icon.svg - ``` -5. Add the PDF to the appropriate `.imageset` directory with a `Contents.json`: - ```json - { - "images": [{ "filename": "icon.pdf", "idiom": "universal" }], - "info": { "author": "xcode", "version": 1 }, - "properties": { "preserves-vector-representation": true } - } - ``` -6. Register in SwiftGen's `Assets.swift` (or the project's asset registry). - -> **Never substitute SF Symbols or existing assets by name alone.** Always visually verify against the Figma design using `get_screenshot` or the REST images endpoint. - ---- - -## 4 — Link-Based Prompting - -- The MCP server is link-based: copy the Figma frame/layer URL and give it to the agent. -- The agent extracts the node ID from the link — it does **not** browse the page. -- Always ensure the link points to the **exact node or variant** you want implemented. -- When given a broad URL (e.g., a full screen), `get_design_context` returns the complete subtree including every nested icon, image, and component with downloadable URLs. Drill into specific `data-node-id` values for individual assets. - ---- - -## 5 — Do NOT - -- Do **not** skip `get_design_context` and jump straight to coding from a screenshot alone. -- Do **not** rely on `get_metadata` for finding assets — it only returns the top-level frame without children. -- Do **not** guess colors, spacing, or font sizes. Extract them from the design context or variables. -- Do **not** import new icon libraries (FontAwesome, SF Symbols, etc.) when the Figma payload already provides the asset. -- Do **not** create placeholder images when a real asset URL is available. -- Do **not** hardcode pixel values when the project has spacing/sizing tokens that match. - ---- - -## References - -- `references/figma-config.md` — MCP setup, REST API auth, environment variables, verification, troubleshooting. -- `references/figma-tools-and-prompts.md` — Full tool catalog and prompt patterns for selecting frameworks/components and fetching metadata. diff --git a/.cursor/skills/update-skill/SKILL.md b/.cursor/skills/update-skill/SKILL.md deleted file mode 100644 index a5451a1..0000000 --- a/.cursor/skills/update-skill/SKILL.md +++ /dev/null @@ -1,588 +0,0 @@ ---- -name: update-skill -description: Create or update Cursor/Codex skills across all repos — keeps everything in sync -disable-model-invocation: true ---- -# update-skill - -Create a new skill or update an existing one **across every repo where it exists**. Handles the full workflow: discovering where the skill lives, keeping `.cursor/skills/` and `.codex/skills/` in sync within each repo, keeping all repos in sync with each other, branching, creating issues, opening PRs, and linking everything together. - ---- - -## Step 0: Understand the Request - -Determine from the user's message: - -1. **Which skill?** — The skill name (e.g., `deploy-backend-dev`, `new-issue`). If the user wants to create a brand-new skill, they'll provide a name and description. -2. **What changes?** — What to add, remove, modify, or rewrite. -3. **Create or update?** — If the skill doesn't exist anywhere yet, this is a creation. If it does, it's an update. - -> If the user's request is vague, **ask for clarification** before proceeding. Don't guess what a skill should do. - ---- - -## Step 1: Discover Where the Skill Exists (Cross-Repo Scan) - -**This is the most critical step.** A skill can exist in ANY combination of these repos: - -| Repo | GitHub Repo | Skill Locations | -|------|-------------|-----------------| -| **Monorepo** | `stepscode/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | -| **iOS** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | -| **Server** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | -| **Tileserver** | `8Gaston8/davidsulitzer.com` | `.cursor/skills//SKILL.md` + `.codex/skills//SKILL.md` | - -### 1a: Check Every Repo - -You MUST check ALL four repos. This skill can run from **any** repo, so don't assume a davidsulitzer.com directory layout. - -**Check the current repo locally:** - -```bash -# Current repo — check local skill directories -echo "=== Current Repo ===" && ls .cursor/skills/ .codex/skills/ 2>/dev/null -``` - -**Check other repos via GitHub API:** - -```bash -# Check each remote repo for skills (works from any repo) -for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do - echo "=== $repo ===" - gh api "repos/stepscode/$repo/contents/.cursor/skills" --jq '.[].name' 2>/dev/null || echo " No .cursor/skills/" - gh api "repos/stepscode/$repo/contents/.codex/skills" --jq '.[].name' 2>/dev/null || echo " No .codex/skills/" -done -``` - -> **Note:** If you're in the davidsulitzer.com and submodules are checked out, you can also use local paths (`davidsulitzer.com/`, `davidsulitzer.com/`, `davidsulitzer.com/`) — but the `gh api` approach works universally from any repo. - -### 1b: Search for the Specific Skill - -```bash -# Check if the skill exists in each repo via GitHub API -for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do - echo "=== $repo ===" - gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .cursor" - gh api "repos/stepscode/$repo/contents/.codex/skills//SKILL.md" --jq '.name' 2>/dev/null && echo " Found in .codex" -done -``` - -### 1c: Record What You Found - -Build a discovery table: - -| Repo | .cursor/skills | .codex/skills | Action Needed | -|------|---------------|---------------|---------------| -| Monorepo | ✅/❌ | ✅/❌ | Create/Update | -| iOS | ✅/❌ | ✅/❌ | Create/Update | -| Server | ✅/❌ | ✅/❌ | Create/Update | -| Tileserver | ✅/❌ | ✅/❌ | Create/Update | - -### 1d: Decide Which Repos Need the Skill - -- **If updating:** Update the skill in ALL repos where it currently exists. If the user wants to add it to additional repos, do that too. -- **If creating:** Ask the user which repos should have the skill, unless the intent is obvious (e.g., a deployment skill only makes sense in the repos that deploy). -- **If the user says "all repos":** Add it to all four repos. - -> ⚠️ **NEVER update a skill in only one repo when it exists in multiple.** The whole point is cross-repo consistency. - ---- - -## Step 2: Read & Compare Existing Content - -If the skill already exists, read it from all locations and check for drift: - -```bash -# Compare across repos (if the skill exists in multiple) -diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md -``` - -Also verify `.cursor/` and `.codex/` are in sync WITHIN each repo: - -```bash -diff /.cursor/skills//SKILL.md /.codex/skills//SKILL.md -``` - -If drift exists, alert the user and ask which version should be the base — then unify everything from there. - ---- - -## Step 3: Write the Skill Content - -### Skill File Format - -Every `SKILL.md` must follow this structure: - -```markdown ---- -name: -description: -disable-model-invocation: true ---- -# - -[Skill content — instructions, steps, templates, references, etc.] -``` - -### Frontmatter Rules - -| Field | Required | Notes | -|-------|----------|-------| -| `name` | Yes | Must match the directory name exactly | -| `description` | Yes | Short, descriptive — shown in skill picker | -| `disable-model-invocation` | Yes | Always set to `true` | - -### Content Guidelines - -- **Be specific and actionable** — Skills are instructions for AI agents. Vague guidance produces vague results. -- **Include exact commands** — bash commands, API calls, GraphQL queries. Don't make the agent guess. -- **Add context for "why"** — Agents follow rules better when they understand the reasoning. -- **Use step-by-step structure** — Numbered steps with clear progression. -- **Include templates** — For repetitive outputs (PR comments, issue bodies, messages), provide fill-in-the-blank templates. -- **Add guardrails** — What to do, but also what NOT to do. Explicit "don't" instructions prevent common mistakes. -- **Reference other skills** — If a skill depends on another (e.g., `qa-start` references `deploy-backend-dev`), link to it with `/skill-name` notation. -- **Include verification steps** — After key actions, add a "verify this worked" step. Agents make mistakes; verification catches them. -- **Separate required vs. optional sections** — Use collapsible `
` blocks for conditional/optional content. -- **Keep it DRY** — If the same info appears in multiple skills, consider whether it belongs in a shared reference or a dedicated skill. - -### Quality Checklist (Before Saving) - -- [ ] Frontmatter `name` matches directory name -- [ ] Description is clear and concise -- [ ] Steps are numbered and ordered logically -- [ ] Commands are copy-pasteable (no undefined placeholders without explanation) -- [ ] Includes "what NOT to do" guardrails -- [ ] Verification/confirmation steps after critical actions -- [ ] References to other skills use `/skill-name` format -- [ ] No hardcoded secrets or tokens (use env vars like `$VARIABLE_NAME`) -- [ ] Markdown renders correctly (check headers, tables, code blocks) - ---- - -## Step 4: Apply Changes to ALL Repos - -For **each repo** that needs the skill: - -### 4a: Create a Feature Branch - -```bash -cd -git fetch origin main -git checkout -b cursor/ origin/main -``` - -**Branch naming convention:** `cursor/update-skill-` or `cursor/add-skill-` - -> ⚠️ **Never edit directly on `develop` or `main`.** -> ⚠️ **Never run `git checkout` from the davidsulitzer.com root** — always `cd` into the specific repo first. - -### 4b: Write to BOTH Directories Within Each Repo - -```bash -# Create directories if they don't exist -mkdir -p .cursor/skills/ -mkdir -p .codex/skills/ - -# Write the skill file to both locations (content must be IDENTICAL) -# ... write SKILL.md to .cursor/skills//SKILL.md -# ... write SKILL.md to .codex/skills//SKILL.md -``` - -### 4c: Update .gitignore (if needed) - -Some repos may have `.cursor` in their `.gitignore`. If so, add an exception for skills: - -```gitignore -# IDE (ignore all except skills/) -.cursor/* -!.cursor/skills/ -``` - -Same for `.codex` if applicable. - -### 4d: Verify Sync Within the Repo - -```bash -diff .cursor/skills//SKILL.md .codex/skills//SKILL.md -``` - -If `diff` produces any output, something went wrong — fix it before committing. - -### 4e: Commit and Push - -```bash -# Stage skill files (always) -git add .cursor/skills//SKILL.md .codex/skills//SKILL.md - -# Stage .gitignore too if it was modified in step 4c -git diff --name-only .gitignore 2>/dev/null | grep -q . && git add .gitignore - -git commit -m "skill(): " -git push -u origin -``` - -### Commit Message Conventions - -| Action | Message Format | -|--------|---------------| -| New skill | `skill(): add new skill — ` | -| Update skill | `skill(): ` | -| Fix skill | `skill(): fix ` | -| Multiple skills | `skills: ` | - ---- - -## Step 5: Create GitHub Issues (for Non-Monorepo Repos) - -**For each repo EXCEPT the davidsulitzer.com**, create a GitHub issue using the `/new-issue` skill patterns. - -> ⚠️ **Monorepo exception:** The davidsulitzer.com does NOT need a GitHub issue — only the PR is sufficient. - -### 5a: Create Issues - -Use the `gh` CLI to create issues in the relevant repos: - -```bash -gh issue create --repo stepscode/ \ - --title "skill(): " \ - --body "" \ - --assignee 8Gaston8 \ - --label "fastlane" -``` - -### 5b: Issue Body Template - -```markdown -## Goal - -[Create / Update] the `` skill [in this repo / across all repos]. - -## Context - -[Brief description of what the skill does and why it's being created/updated.] - -## Changes - -- [List of specific changes being made] - -## Cross-Repo Sync - -This skill [is being created / has been updated] across all repos where it exists: -- [ ] `stepscode/davidsulitzer.com` — PR #XXX -- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX (this issue) -- [ ] `8Gaston8/davidsulitzer.com` — PR #XXX - -All instances must remain identical. - -## Out of Scope - -- Content changes to other skills -- Skill deletion - -## Acceptance Criteria - -- [ ] Skill file exists in `.cursor/skills//SKILL.md` -- [ ] Skill file exists in `.codex/skills//SKILL.md` -- [ ] Both copies are identical (`diff` produces no output) -- [ ] Content matches all other repos -``` - -### 5c: Set Issue Type - -Set the issue type to **Task**: - -```bash -# Get issue node ID -gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { id } } }' - -# Set Issue Type to Task -gh api graphql -f query='mutation { updateIssue(input: { id: "", issueTypeId: "IT_kwDOBluP0c4BBYRC" }) { issue { id } } }' -``` - -### 5d: Add to davidsulitzer.com Project with "In Review" Status - -```bash -# Add to davidsulitzer.com project -gh project item-add 2 --owner 8Gaston8 --url - -# Get project item ID -gh api graphql -f query='{ organization(login: "8Gaston8") { projectV2(number: 2) { items(last: 10) { nodes { id content { ... on Issue { number repository { name } } } } } } } }' - -# Set Status to "In Review" -gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzgntIc4", value: { singleSelectOptionId: "2895eb73" } }) { projectV2Item { id } } }' - -# Set Priority to Low (skill updates are low-risk housekeeping) -gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "", itemId: "", fieldId: "PVTSSF_lADOBluP0c4AxnzVzg5mgJc", value: { singleSelectOptionId: "9d5cb3c5" } }) { projectV2Item { id } } }' -``` - -### 5e: Verify Issue Configuration - -```bash -# Verify issue type -gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { issue(number: ) { issueType { name } labels(first: 5) { nodes { name } } } } }' -``` - ---- - -## Step 6: Create Pull Requests - -Create a PR for **every repo** that was changed: - -### 6a: Create the PR - -```bash -cd -gh pr create \ - --repo stepscode/ \ - --base develop \ - --head \ - --title "skill(): " \ - --body "" \ - --assignee 8Gaston8 -``` - -### 6b: PR Body Template - -```markdown -## Summary - -[Create / Update] the `` skill. - -## Changes - -- [List of changes] - -## Cross-Repo Sync - -This change is part of a cross-repo skill sync: -- `stepscode/davidsulitzer.com` — PR #XXX -- `8Gaston8/davidsulitzer.com` — PR #XXX - -All skill files are identical across repos. - -Closes # -``` - -> **Note:** Include `Closes #` in the PR body to auto-link and auto-close the issue when merged. For davidsulitzer.com PRs, omit this since there's no associated issue. - -### 6c: Mark PR as Ready for Review - -PRs created with `gh pr create` are ready for review by default (not draft). Verify: - -```bash -gh pr view --repo stepscode/ --json isDraft -``` - -If it's a draft, convert it: - -```bash -gh pr ready --repo stepscode/ -``` - -### 6d: Assign & Request Review (MANDATORY) - -Every PR must be assigned to Gaston and have Aviad as a reviewer. Use the GitHub GraphQL API: - -```bash -# 1. Get user node IDs (only needed once — cache these) -gh api graphql -f query='{ user(login: "8Gaston8") { id } }' -# → Use this ID for assignee -gh api graphql -f query='{ user(login: "aviadsteps") { id } }' -# → Use this ID for reviewer - -# 2. Assign Gaston to the PR -gh api graphql -f query='mutation { addAssigneesToAssignable(input: { assignableId: "", assigneeIds: [""] }) { assignable { ... on PullRequest { number } } } }' - -# 3. Request review from Aviad -gh api graphql -f query='mutation { requestReviews(input: { pullRequestId: "", userIds: [""] }) { pullRequest { number } } }' -``` - -> **To get the PR node ID:** -> ```bash -> gh api graphql -f query='{ repository(owner: "8Gaston8", name: "") { pullRequest(number: ) { id } } }' -> ``` - -> ⚠️ **Do this for EVERY PR created** — davidsulitzer.com, iOS, server, and tileserver. No exceptions. - -### 6e: Enable Auto-Merge (iOS Only) - -If the PR is in the **davidsulitzer.com** repo, enable auto-merge so it merges automatically once CI passes and the review is approved: - -```bash -gh pr merge --repo 8Gaston8/davidsulitzer.com --auto --squash -``` - -> ⚠️ **Only for `8Gaston8/davidsulitzer.com`** — do NOT enable auto-merge on davidsulitzer.com, server, or tileserver PRs. - -### 6f: Link Issues to PRs - -For non-davidsulitzer.com repos, the `Closes #` in the PR body handles the link. Additionally, verify the connection: - -```bash -gh pr view --repo stepscode/ --json closingIssuesReferences -``` - ---- - -## Step 7: Final Verification - -### 7a: Content Sync Across All Repos - -Verify the skill content is identical across every repo where it was written: - -```bash -# Compare all instances (adjust paths based on which repos have the skill) -diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md -diff /.cursor/skills//SKILL.md /.cursor/skills//SKILL.md -# etc. -``` - -### 7b: Issue & PR Checklist - -For each non-davidsulitzer.com repo, verify: - -- [ ] GitHub issue exists with: - - [ ] Issue Type: Task (verified via GraphQL) - - [ ] Label: `fastlane` - - [ ] Assignee: @8Gaston8 - - [ ] Added to davidsulitzer.com project - - [ ] Status: "In Review" -- [ ] PR exists and is: - - [ ] Ready for review (not draft) - - [ ] Linked to the issue (`Closes #XXX`) - - [ ] Assignee: @8Gaston8 - -For the davidsulitzer.com: -- [ ] PR exists and is ready for review (no issue needed) - -### 7c: Share All Links with the User (MANDATORY) - -**You MUST always end by sharing every relevant link** so the user can review everything at a glance. Present the results as a clear summary with **full clickable URLs** — not just PR/issue numbers: - -```markdown -## Here are all the links: - -### Pull Requests -- **Monorepo:** https://github.com/stepscode/davidsulitzer.com/pull/XXX -- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX -- **Server:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX -- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/pull/XXX - -### Issues (linked to PRs above) -- **iOS:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX -- **Server:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX -- **Tileserver:** https://github.com/8Gaston8/davidsulitzer.com/issues/XXX -- *(Monorepo: no issue needed)* - -### Summary Table -| Repo | PR | Issue | Status | Skill Synced | -|------|-----|-------|--------|-------------| -| davidsulitzer.com | [#XXX](url) | N/A | Ready for Review | ✅ | -| davidsulitzer.com | [#XXX](url) | [#XXX](url) (In Review, fastlane) | Ready for Review | ✅ | -``` - -> ⚠️ **Do NOT skip this step.** The user needs all links in one place to review and merge. Always use full URLs, not just `#123` references. - ---- - -## Handling Special Cases - -### Renaming a Skill - -1. Create the new skill directory in both `.cursor/` and `.codex/` locations across ALL repos -2. Copy the content (with updated `name` field in frontmatter) -3. Delete the old skill directory from both locations across ALL repos -4. Create PRs for every affected repo (same issue/PR workflow as above) - -### Deleting a Skill - -1. Remove from both directories across ALL repos where it exists -2. Create PRs for every affected repo (same issue/PR workflow as above) - -### Auditing All Skills for Sync - -If you suspect drift across repos: - -```bash -# Compare all skills within the current repo -diff -rq .cursor/skills/ .codex/skills/ - -# Compare a specific skill across repos via GitHub API -for repo in davidsulitzer.com davidsulitzer.com davidsulitzer.com davidsulitzer.com; do - echo "=== $repo ===" - gh api "repos/stepscode/$repo/contents/.cursor/skills//SKILL.md" --jq '.content' 2>/dev/null | base64 -d | md5sum -done -``` - -All checksums should be identical. - -### Skill Only Relevant to Some Repos - -Not every skill belongs everywhere. Use judgment: - -| Skill Type | Typical Repos | -|-----------|---------------| -| Deployment skills | Repo being deployed | -| QA/testing skills | Repo being tested | -| Issue/project skills | All repos (agents work in any) | -| Release skills | iOS repo primarily | -| General workflow skills | All repos | -| Skill management (this skill!) | All repos | - -When in doubt, ask the user. - ---- - -## Reference: Current Skill Inventory - -| Skill | Purpose | Repos | -|-------|---------|-------| -| `deploy-backend-dev` | Deploy backend functions to dev environment | davidsulitzer.com | -| `deploy-backend-prod` | Deploy backend functions to production | davidsulitzer.com | -| `latest-release-notes` | Generate release notes for Slack | davidsulitzer.com | -| `new-issue` | Create GitHub issues with full research and project setup | davidsulitzer.com | -| `new-release` | Prepare iOS releases (branch, PR, regression testing, App Store) | davidsulitzer.com | -| `qa-start` | Comprehensive QA workflow for PRs (iOS + server) | davidsulitzer.com | -| `review-new-bugs` | Triage bug reports from Slack with screenshot analysis | davidsulitzer.com | -| `review-new-crashes` | Review and triage Crashlytics crash reports | davidsulitzer.com | -| `test-backend-changes-dev` | Test backend changes on dev environment | davidsulitzer.com | -| `test-backend-changes-local` | Test backend changes locally | davidsulitzer.com | -| `test-backend-changes-prod` | Test backend changes on production | davidsulitzer.com | -| `update-skill` | This skill — create or update skills across repos | all repos | -| `user-interview` | Simulate data-driven user interviews for product insights | davidsulitzer.com | - -> ⚠️ **Keep this table updated** when creating, deleting, or expanding skills to new repos! - ---- - -## Reference: Repo Quick Info - -| Repo | GitHub | Default Branch | Submodule Path | -|------|--------|----------------|----------------| -| Monorepo | `stepscode/davidsulitzer.com` | `develop` | `.` (root) | -| iOS | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | -| Server | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | -| Tileserver | `8Gaston8/davidsulitzer.com` | `develop` | `davidsulitzer.com/` | - ---- - -## Reference: Project Field IDs - -| Field | Field ID | Key Options | -|-------|----------|-------------| -| **Status** | `PVTSSF_lADOBluP0c4AxnzVzgntIc4` | Idea: `01f8a8de`, Todo: `54ffad90`, Ready for Dev: `cac3311f`, In Development: `d7c83647`, Human QA: `6a5c111e`, In Review: `2895eb73`, Done: `782fbd7c` | -| **Priority** | `PVTSSF_lADOBluP0c4AxnzVzg5mgJc` | Critical: `62a8b83f`, High: `ee32511d`, Medium: `2b2b659f`, Low: `9d5cb3c5` | - ---- - -## Notes - -- **Skill names must be kebab-case** — lowercase, words separated by hyphens (e.g., `deploy-backend-dev`, not `deployBackendDev`) -- **One SKILL.md per skill** — each skill lives in its own directory with a single `SKILL.md` file -- **Skills are version-controlled** — every change goes through git with proper branching, issues, and PRs -- **Both `.cursor/` and `.codex/` must always be in sync** within each repo -- **All repos must be in sync** with each other for shared skills -- **Monorepo gets PRs but NO issues** — only the subrepos (iOS, server, tileserver) get issues -- **Issues get "In Review" status and "fastlane" label** — skill updates are low-friction, ship fast -- **PRs are always Ready for Review** — never left as draft -- **Test after creating** — always suggest the user tests the skill with a real invocation diff --git a/.cursor/skills/user-interview/SKILL.md b/.cursor/skills/user-interview/SKILL.md deleted file mode 100644 index 58b8685..0000000 --- a/.cursor/skills/user-interview/SKILL.md +++ /dev/null @@ -1,628 +0,0 @@ ---- -name: user-interview -description: Simulate data-driven user interviews for product insights, feature feedback, or design testing -disable-model-invocation: true ---- -# user-interview - -Simulate realistic user interviews grounded in real product data. Adapts to three modes depending on what you need. - -> **Default product scope: davidsulitzer.com.** All interviews, personas, and data queries should focus on the davidsulitzer.com product (local discovery, smart search, curated spots) unless the user explicitly asks about Gluten Free Eats (GFE). If the user mentions GFE, celiac, gluten-free, or dietary safety, switch to GFE-specific personas and data. When in doubt, ask. - -**Usage examples:** -- `@user-interview` — "I want to understand why trial users churn" -- `@user-interview` — "How would users react to a weekly recommendation email?" -- `@user-interview` — "Here's the new onboarding design — simulate a user going through it" - ---- - -## Step 1: Determine the Interview Mode - -Based on what the user asks, pick the right mode: - -| Mode | Trigger | Purpose | -|------|---------|---------| -| **Exploratory** | Open-ended question, no specific feature | Understand user segments, generate hypotheses | -| **Feature Feedback** | Specific feature or product change mentioned | Simulate reactions to a proposed change | -| **Design Testing** | Design/screenshot/mockup provided or referenced | Simulate users experiencing a specific flow | - -> If unclear, ask: "Are you exploring broadly, testing a specific feature idea, or validating a design?" - ---- - -## Step 2: Gather Real Data - -**This is what makes the interviews valuable.** Before simulating ANYTHING, pull real data from every available source. - -> ### ⚠️ Cloud Agent? Use APIs Instead of MCP -> -> If you are running as a **cloud agent** (headless environment, no MCP tools available), you **MUST** use REST APIs with credentials from environment variable secrets instead of MCP tools. All required API keys are available via **Cursor Dashboard → Cloud Agents → Secrets** and are injected as environment variables. -> -> | Data Source | MCP Tool (Local) | Cloud Agent Alternative | Secret / Env Var | -> |-------------|-------------------|------------------------|------------------| -> | **analytics** | `run_segmentation_query` etc. | Your analytics provider API docs | `ANALYTICS_API_SECRET` | -> | **MongoDB** | Atlas MCP tools | [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/app-services/data-api/) | `MONGODB_ATLAS_API_KEY`, `MONGODB_APP_ID` | -> | **Crisp** | Shell (API) | [Crisp REST API](https://docs.crisp.chat/references/rest-api/v1/) | `CRISP_API_ID`, `CRISP_API_KEY` | -> | **App Store** | Shell (JWT) | [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi) | `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_PRIVATE_KEY` | -> | **Slack** | Slack MCP | [Slack Web API](https://api.slack.com/web) | `SLACK_BOT_TOKEN` | -> -> **Important:** The local `.env` file at `davidsulitzer.com/api/.env` is **NOT available** in cloud agent environments. Always use secrets. If a secret is missing, ask the user to add it in the Cursor Dashboard. -> -> Example (analytics segmentation in cloud): -> ```bash -> curl -X POST "" \ -> -u "$ANALYTICS_API_SECRET:" \ -> -d '{"event": "app_search_initiated", "from_date": "2026-01-01", "to_date": "2026-02-09"}' -> ``` -> -> Example (MongoDB Atlas Data API in cloud): -> ```bash -> curl -X POST "https://data.mongodb-api.com/app/$MONGODB_APP_ID/endpoint/data/v1/action/find" \ -> -H "api-key: $MONGODB_ATLAS_API_KEY" \ -> -H "Content-Type: application/json" \ -> -d '{"dataSource": "production", "database": "prod", "collection": "userFeedback", "filter": {}, "limit": 50}' -> ``` -> -> **When a data source is inaccessible:** Don't silently skip it. Explicitly note in the report: "Could not access [source] — [reason]. Confidence scores adjusted accordingly." - -### 2a. Product Analytics (Optional) — Behavioral Data (Project ID: ) - -Pull data relevant to the interview topic. Choose from: - -**Segmentation** — Understand user behavior patterns: -``` -run_segmentation_query with project_id: -``` -Useful queries: -- Event frequency by user cohort (e.g., how often do trial users search?) -- Feature usage distribution (e.g., what % of users save places?) -- Drop-off points in key flows - -**Funnels** — Understand conversion: -``` -run_funnels_query with project_id: -``` -Useful funnels: -- Pricing Screen → Trial Start → Trial Converted -- App Open → Search → Place Profile View → Save -- Trial Start → Session 2 → Session 3 (retention) - -**Retention** — Understand stickiness: -``` -run_retention_query with project_id: -``` -Useful queries: -- Day 1 / Day 3 / Day 7 retention by cohort -- Retention by acquisition source -- Feature-correlated retention (users who saved vs didn't) - -**Frequency** — Understand usage patterns: -``` -run_frequency_query with project_id: -``` -Useful queries: -- Sessions per week distribution -- Search frequency during trial -- Save frequency by subscription status - -**User Properties** — Understand who they are: -``` -get_property_values with project_id: , resource_type: "User" -``` -Key properties to check: -- `$country_code`, `$city` — geography -- `product` — which product they use (filter for davidsulitzer.com by default) -- `abi_onboarding_type` — how they arrived -- `gfe_sensitivity` — GFE sensitivity level (only relevant if interviewing GFE users) - -### 2b. MongoDB Atlas — User & Content Data (Database: prod) - -Pull data relevant to the interview topic: - -**User feedback & reviews:** -``` -Collection: userFeedback — Direct user feedback -Collection: reviews — Place reviews (shows what users value) -Collection: userState — Subscription/engagement state -``` - -**Engagement signals:** -``` -Collection: saveListItems — What users save (investment behavior) -Collection: stepLikes — What users like -Collection: mapfollowers — Community engagement -Collection: viewsCount — Browsing patterns -``` - -**Content quality:** -``` -Collection: steps — Places/locations in the app -Collection: maps — Available maps -Collection: details — Place details (what info we show) -``` - -**Churn signals:** -``` -Collection: userFeedback — Look for cancellation feedback -Collection: userSubscriptionBypassesForPremiumMaps — Trial behavior -``` - -### 2c. App Store Reviews - -Pull recent App Store reviews using the App Store Connect API: - -```bash -# Generate JWT token for App Store Connect -source .env - -# Get recent reviews (requires JWT auth — see App Store Connect API docs) -# Look for: complaints, praise, feature requests, frustrations -``` - -Alternative: Search the web for recent davidsulitzer.com reviews: -``` -Search: "davidsulitzer.com app" review site:apps.apple.com OR site:play.google.com -``` - -### 2d. Support Conversations — Crisp - -Check recent support conversations for common themes: - -```bash -# Crisp API — get recent conversations -# Website ID: 51f0b9f1-c96c-452a-8e15-e437101d7d80 -# Look for: recurring complaints, feature requests, confusion points -``` - -### 2e. Slack — Internal Signals - -Check bug reports and feedback channels: -``` -Channel: #bugs-mobile-apps (CS93RCDSB) — User-reported issues via Julia -``` - -### 2f. Codebase — Product Context - -**Always** read relevant code to understand: -- The actual user flow being discussed (not assumptions!) -- What the user sees, taps, and experiences -- Feature flags and A/B tests currently running -- Paywall/subscription logic -- Onboarding flows and their variations - -Key areas: -- `davidsulitzer.com/Steps/AppFlows/` — All user flows -- `davidsulitzer.com/Steps/AppFlows/Paywall/` — Pricing/trial logic -- `davidsulitzer.com/Steps/AppFlows/Onboarding/` — First-time experience -- `davidsulitzer.com/Steps/AppFlows/Retention/` — Churn/cancellation flow -- `davidsulitzer.com/api/businessLogic/` — Backend logic for features - -### 2g. Competitor Landscape - -**Always** check what alternatives the simulated user has. Personas don't exist in a vacuum — they're comparing davidsulitzer.com to real alternatives. - -Search the web for recent competitor features and reviews: -``` -Search: "Google Maps" OR "Yelp" OR "TripAdvisor" OR "Foursquare" [topic relevant to interview] -Search: "best restaurant discovery app 2026" -``` - -Key competitors to benchmark against: -| Competitor | What They Offer | davidsulitzer.com's Differentiation | -|------------|----------------|----------------------| -| **Google Maps** | Universal coverage, reviews at scale, AI summaries | davidsulitzer.com: curated quality > quantity, community maps, smart vibe-based search | -| **Yelp** | Review volume, photos, reservations | davidsulitzer.com: less noise, no sponsored results, real community curation | -| **TripAdvisor** | Travel-focused, hotel + restaurant combo | davidsulitzer.com: local-first (not tourist-first), daily use not just travel | -| **Instagram/TikTok** | Social discovery, viral food content | davidsulitzer.com: searchable + saveable, not ephemeral scroll | -| **Friends/Word of mouth** | Trusted, personal, free | davidsulitzer.com: scales the "friend who knows a spot" feeling | - -**Use this in interviews:** When a persona says "I could find this elsewhere," push back with specifics. WHAT would they use? What's the actual experience on that alternative? Make the comparison concrete, not vague. - -### 2h. GitHub Projects — Roadmap & Product Context - -Check both GitHub Projects for relevant context on what's planned, in progress, or ideated: - -**davidsulitzer.com Project** (number: 2, ID: ``) — Active development work: -```bash -# List recent items with status -gh project item-list 2 --owner 8Gaston8 --limit 30 -``` -Look for: items In Development, Todo, or Done that relate to the interview topic. - -**davidsulitzer.com Ideation Lab** (number: 4, ID: `PVT_kwDOBluP0c4BOk4F`) — Ideas and experiments: -```bash -# List ideation items -gh project item-list 4 --owner 8Gaston8 --limit 30 -``` -Look for: feature ideas, experiments, and hypotheses that relate to the interview topic. - -Also search GitHub issues directly for relevant context: -```bash -gh search issues "" --repo 8Gaston8/davidsulitzer.com --state all -``` - ---- - -## Step 3: Build Data-Grounded Personas - -Based on the data gathered, build 2-4 personas that represent REAL behavioral segments. - -### Persona Template - -For each persona, specify: - -```markdown -## Persona: "[Name]" -**Segment:** [What real data segment they represent] -**Data basis:** [Specific analytics/MongoDB data that shaped this persona] - -| Attribute | Details | Data Source | -|-----------|---------|-------------| -| Demographics | [Age, location, lifestyle] | analytics geo data | -| Behavior pattern | [How they use the app — frequency, features, timing] | analytics events | -| Subscription status | [Trial, converted, churned, never started] | MongoDB userState | -| Key actions | [Searches, saves, visits, reviews] | analytics + MongoDB | -| Pain points | [From reviews, feedback, support tickets] | App Store + Crisp | -| Motivation | [Why they downloaded, what they hope for] | Onboarding data | -``` - -### Persona Selection by Mode - -| Mode | Personas to Build | -|------|-------------------| -| **Exploratory** | Cover 3-4 segments: a converter, a churner, a power user, and an edge case | -| **Feature Feedback** | 2-3 personas most affected by the proposed feature | -| **Design Testing** | 2 personas: one tech-savvy, one less experienced with apps | - ---- - -## Step 4: Run the Simulated Interviews - -### Interview Structure - -Each interview should follow this format: - -1. **Context setting** — Who they are, how they found the app -2. **Experience narrative** — Walk through their actual journey (grounded in real product flow!) -3. **Key moment** — The critical point where they converted/churned/got stuck -4. **Probing questions** — Deep dive into motivations and frustrations -5. **Forward-looking** — What would change their behavior - -### Mode-Specific Interview Approaches - -#### Exploratory Mode -- Open-ended questions about their experience -- Focus on emotional journey, not just functional -- Probe for unmet needs and surprising behaviors -- Ask: "What would make this indispensable to you?" - -#### Feature Feedback Mode -- Describe the proposed feature mid-interview -- Capture initial reaction (excitement, confusion, indifference) -- Probe for edge cases: "What if [scenario]?" -- Ask: "Would this change how often you use the app?" -- Ask: "Would this have changed your decision to [subscribe/cancel]?" - -#### Design Testing Mode -- Walk the persona through the design step by step -- Capture first impressions at each screen/state -- Note confusion points: "What do you think this button does?" -- Test comprehension: "What would you do next?" -- Identify emotional reactions: excitement, trust, skepticism, frustration - -### Realism Guidelines - -- **Use natural speech** — contractions, hesitation, tangents (real people don't speak in bullet points) -- **Include "I don't know" moments** — real users aren't always articulate about their needs -- **Show contradictions** — people say one thing and do another (e.g., "I love discovering places" but saves 0 places) -- **Ground in ACTUAL product behavior** — reference real screens, real flows, real pricing ($12/month) -- **Vary emotional states** — some users are enthusiastic, some are apathetic, some are frustrated -- **Inject real user quotes** — When you pulled App Store reviews, Crisp tickets, or MongoDB feedback in Step 2, weave those ACTUAL words into the simulated persona's dialogue. Mark them clearly: - - > **Example:** The persona says *"I don't use it often enough to justify paying"* — and you note: `[echoes real churn reason: "dont_use_often_enough" — selected by X% of cancelers]` - - This makes the simulation tangibly grounded. If you found a 1-star review saying "I searched for brunch and got 3 results," have the persona express that frustration in their own words. Tag it: - - > `[mirrors App Store review, 2 stars, Jan 2026: "searched for brunch spots and barely got any results"]` - - **The goal:** Anyone reading the simulation can immediately see which parts are data-backed and which are AI extrapolation. - ---- - -## Step 5: Synthesize Insights - -After running all interviews, produce a structured synthesis: - -### Comparison Table - -```markdown -| Factor | [Persona 1] | [Persona 2] | [Persona 3] | -|--------|-------------|-------------|-------------| -| First impression | ... | ... | ... | -| Key value moment | ... | ... | ... | -| Biggest friction | ... | ... | ... | -| Would they pay? | ... | ... | ... | -| Usage frequency | ... | ... | ... | -``` - -### Hypotheses (with Confidence Scoring) - -Generate 3-5 testable hypotheses based on the interviews. **Every hypothesis MUST have a confidence level** so the user knows what to trust vs. what to validate first: - -| Confidence | Meaning | When to Use | -|------------|---------|-------------| -| 🟢 **High** | Backed by real data (analytics numbers, actual reviews, MongoDB patterns) | Hypothesis directly supported by quantitative evidence | -| 🟡 **Medium** | Supported by qualitative signals (support tickets, a few reviews, codebase patterns) | Some real-world backing but not statistically rigorous | -| 🔴 **Low** | Speculative — AI extrapolation, no direct data found | Plausible reasoning but no evidence yet; validate first | - -```markdown -| # | Hypothesis | Confidence | Supporting Evidence | How to Validate | -|---|-----------|------------|-------------------|-----------------| -| 1 | [Statement] | 🟢/🟡/🔴 | [Data source + what it showed] | [analytics query / real interview question / A/B test] | -``` - -> **Rule:** If more than half your hypotheses are 🔴, explicitly call that out: "Most of these insights are speculative — real user interviews should be the priority before acting on them." - -### Impact on Key Metrics - -Connect insights back to Q1 2026 goals: - -**North Star:** $1M ARR for davidsulitzer.com - -| Metric | Current | Insight Impact | Suggested Action | -|--------|---------|---------------|-----------------| -| Pricing → Trial Start (goal: 20-25%) | [Pull from analytics] | [How insights relate] | [Specific change] | -| Trial → Converted (goal: 35-40%) | [Pull from analytics] | [How insights relate] | [Specific change] | -| Activation Rate (goal: 60%) | [Pull from analytics] | [How insights relate] | [Specific change] | -| Early Retention (goal: 40%) | [Pull from analytics] | [How insights relate] | [Specific change] | -| Investment Rate (goal: 10%) | [Pull from analytics] | [How insights relate] | [Specific change] | - ---- - -## Step 6: Generate Actionable Outputs - -Based on the mode, produce the right deliverable: - -### Exploratory Mode → Interview Guide -Generate a real-user interview question guide: - -```markdown -## Interview Guide: [Topic] -**Target segment:** [Who to recruit] -**Session length:** 30 min - -### Warm-up (5 min) -1. [Question about their life/context] -2. [Question about how they find places today] - -### Core Questions (20 min) -3. [Probing question derived from simulation insight #1] -4. [Probing question derived from simulation insight #2] -... - -### Wrap-up (5 min) -8. "If you could change one thing about davidsulitzer.com..." -9. "Would you recommend davidsulitzer.com to a friend? Why/why not?" -``` - -### Feature Feedback Mode → Decision Brief -```markdown -## Feature Decision Brief: [Feature Name] - -### Verdict: 🟢 Build / 🟡 Iterate / 🔴 Rethink - -**Summary:** [One paragraph] - -### User Reception -| Persona | Reaction | Risk | -|---------|----------|------| -| ... | ... | ... | - -### Recommendations -1. [If building: specific implementation suggestions] -2. [If iterating: what to change first] -3. [If rethinking: alternative approaches] - -### Questions to Validate with Real Users -1. [Most critical assumption to test] -2. ... -``` - -### Design Testing Mode → Usability Report -```markdown -## Usability Report: [Design Name] - -### Overall Score: X/5 - -### Screen-by-Screen Findings -| Screen | Comprehension | Emotional Reaction | Issues Found | -|--------|--------------|-------------------|--------------| -| ... | ... | ... | ... | - -### Critical Issues (Fix Before Ship) -1. [Issue + which persona hit it + severity] - -### Recommendations -1. [Specific design change] -2. ... -``` - ---- - -## Step 7: Generate HTML Report - -**MANDATORY.** In addition to any markdown output, you MUST generate a self-contained HTML report file that makes the entire interview simulation comfortable to read in a browser. This is the primary deliverable — the thing Gaston will actually read. - -### Requirements -- **Single file, zero dependencies** — all CSS must be inline in a ` - - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - -``` - -> **Quality bar:** The HTML report should feel like a premium product research document — the kind of thing you'd send to a CEO and they'd actually read cover to cover. Not a markdown-to-HTML conversion, but a designed reading experience. - ---- - -## Step 8: Offer Follow-ups - -> **Reminder:** Before offering follow-ups, make sure you've committed and pushed the HTML report file if running in a git-enabled environment. - -After presenting results, suggest next steps: - -- **"Want me to simulate another persona?"** — Different segment, different perspective -- **"Want me to draft a GitHub issue for any of these insights?"** — Using `/new-issue` skill -- **"Want me to add an idea to the davidsulitzer.com Ideation Lab?"** — Track hypotheses as ideation items -- **"Want me to pull more specific data on any of these findings?"** — Deeper analytics/MongoDB dive -- **"Should I run this for GFE too?"** — Same framework, but switches to GFE-specific personas, safety concerns, celiac sensitivity levels, and dietary-focused data - ---- - -## Reference: Available Data Sources - -| Source | Tool | What It Provides | -|--------|------|-----------------| -| **analytics** | MCP (project_id: ) | Events, funnels, retention, segmentation, user properties | -| **MongoDB** | Atlas MCP (database: prod) | Users, reviews, feedback, saves, engagement, subscription state | -| **App Store** | Shell (App Store Connect API) | User reviews, ratings, feature requests | -| **Crisp** | Shell (API) | Support conversations, common complaints | -| **Slack** | Slack MCP | Bug reports (#bugs-mobile-apps), internal discussions | -| **GitHub Projects** | `gh project` CLI | davidsulitzer.com project (dev work), davidsulitzer.com Ideation Lab (ideas/experiments) | -| **GitHub Issues** | `gh search issues` | Feature requests, bugs, past decisions | -| **Codebase** | File tools | Actual product flows, screens, logic | -| **Web** | Web search/fetch | Competitor analysis, market context, davidsulitzer.com public pages | - ---- - -## Reference: Key Metrics (Q1 2026) - -**North Star:** $1M ARR for davidsulitzer.com - -### Top-Down Metrics (Pricing Funnel) -| Metric | Goal | -|--------|------| -| Pricing Screen → Trial Start | 20-25% | -| Trial Start → Trial Converted | 35-40% | - -### Leading Indicators -| Metric | Goal | Definition | -|--------|------|------------| -| Activation Rate | 60% | % of new trial users who view 5+ place profiles from multi-category searches in first 7 days | -| Early Retention | 40% | % of trial users with 3+ sessions in first 3 days | -| Investment Rate | 10% | % of trial users who save at least 1 place in first 7 days | - ---- - -## Reference: davidsulitzer.com Primary Personas - -These are the baseline personas. Data should refine and override these: - -| Persona | Core Need | Key Behavior | -|---------|-----------|-------------| -| **Decision-Fatigued Foodie** | Just tell me where to go | Searches with specific intent, wants fast answers | -| **Urban Explorer** | Discover hidden gems, feel like a local | Browses and saves, wants to impress friends | -| **Vegan User** | Plant-based focus, not afterthought | Cuisine-specific searches, community-oriented | -| **Date Planner** | Impressive, memorable spots | Plans ahead, cares about ambiance/vibe | -| **Frequent Traveler** | Local expertise in unfamiliar cities | Location-based searches, pre-trip planning | -| **Busy Professional** | Fast, reliable answers | Efficiency over exploration, specific intent | - ---- - -## Reference: Churn Reasons (from Retention Flow) - -Real cancellation reasons captured in-app: - -| Reason | Code | Frequency Insight | -|--------|------|-------------------| -| Too expensive | `too_expensive` | Check analytics for distribution | -| Didn't find relevant places | `didnt_find_relevant_places` | Check analytics for distribution | -| Don't use often enough | `dont_use_often_enough` | Check analytics for distribution | -| Not enough places | `not_enough_places` | Check analytics for distribution | -| Safety concerns | `safety_concerns` | Check analytics for distribution | -| Technical issues/bugs | `technical_issues_bugs` | Check analytics for distribution | -| Other (free text) | `other` | Check MongoDB userFeedback | - ---- - -## Notes - -- **Never fabricate data.** If you can't pull real numbers, say "I couldn't access this — here's my best estimate based on [source]." -- **Always read the actual codebase** before describing product flows. Getting the flow wrong undermines the entire simulation. -- **Simulations are hypotheses, not truths.** Always frame outputs as "worth validating" not "definitely true." -- **Real interviews > simulated interviews.** The goal is to sharpen questions and challenge assumptions, not to replace talking to humans. -- **Adapt tone to Gaston** — Keep it casual, witty, and insightful. This is a thinking tool, not a corporate report.