|
1 | | -# CLAUDE.md |
| 1 | +# croissant.ai — Agent Guide |
2 | 2 |
|
3 | | -You are a senior full-stack engineer working on **Sol**, the monorepo for Rize — an automatic time tracking application. Read the relevant code before making changes. Quote the specific code you're modifying when explaining changes. |
| 3 | +Tool-agnostic reference for AI coding agents working in this repository. |
4 | 4 |
|
5 | | -## Project Overview |
| 5 | +## Stack |
6 | 6 |
|
7 | | -- **api/** — Rails 7.1 GraphQL backend (Ruby 3.3.5) |
8 | | -- **web/** — Next.js 14 React web app (Node 22) |
9 | | -- **electron/** — Electron desktop app (Node 22) |
10 | | -- **services/** — Bun-based TypeScript event consumers/workers |
11 | | -- **voyager/** — Marketing website and landing pages (Next.js) |
12 | | -- **scripts/** — Automation scripts (categorized by side-effect type) |
13 | | -- **puppet/** — Puppeteer server for images/PDFs |
14 | | -- **chrome/** — Chrome browser extension |
15 | | -- **docs/** — Docusaurus documentation site |
16 | | -- **zapier/** — Zapier integration |
| 7 | +Node.js / Express / PostgreSQL / Redis |
| 8 | +Railway deployment | Stripe / Salesforce / QuickBooks integrations |
17 | 9 |
|
18 | | -## Development Commands |
| 10 | +## Project Structure |
19 | 11 |
|
20 | | -```bash |
21 | | -# Start all services (requires iTerm2 on macOS) |
22 | | -./scripts/run-dev.sh |
23 | | - |
24 | | -# Or individually: |
25 | | -cd api && hivemind Procfile.dev # Rails + AnyCable + Sidekiq + Clockwork |
26 | | -cd web && npm run dev # Next.js dev server |
27 | | -cd electron && npm run dev # Electron with hot reload |
28 | | -cd services && hivemind Procfile.dev # Bun services |
29 | | -cd voyager && npm run dev # Marketing site (port 3003) |
30 | 12 | ``` |
31 | | - |
32 | | -### Docker (start first) |
33 | | -```bash |
34 | | -cd api && docker-compose up -d |
35 | | -# TimescaleDB :15432 | Redis :16379 | Kafka :9092 | MySQL :13306 |
| 13 | +src/ |
| 14 | + api/ # Route handlers |
| 15 | + core/ # monitoring-service, cache-service, queue-service, master-agent, api-validation |
| 16 | + features/ # Feature modules |
| 17 | + shared/ # Shared utilities |
| 18 | + integrations/ # Third-party connectors |
| 19 | +docs/ # Documentation |
| 20 | +scripts/ # Automation scripts |
| 21 | +docker/ # Container configs |
| 22 | +prompts/ # Externalized LLM prompt templates |
36 | 23 | ``` |
37 | 24 |
|
38 | | -### Testing |
39 | | -```bash |
40 | | -cd api && bundle exec rspec # Full API suite |
41 | | -cd api && bundle exec rspec spec/path/to/file_spec.rb # Single file |
42 | | -cd api && bundle exec rspec spec/path/to/file_spec.rb:42 # Single line |
43 | | -cd electron && npm test # Electron (Jest) |
44 | | -# Web — no active tests |
45 | | -``` |
| 25 | +## Commands |
46 | 26 |
|
47 | | -### Building |
48 | 27 | ```bash |
49 | | -cd api && bundle install && rake db:migrate |
50 | | -cd web && npm run build # gql-gen + tailwind + next build |
51 | | -cd electron && npm run build # Electron Forge make |
52 | | -cd services && bun install |
| 28 | +npm run dev # Start dev server |
| 29 | +npm run test # Run test suites (3 parallel Jest workers, maxWorkers=4) |
| 30 | +npm run lint # Lint check |
| 31 | +npm run migrate # Run DB migrations |
| 32 | +docker-compose up -d # Start local DBs |
53 | 33 | ``` |
54 | 34 |
|
55 | | -## Architecture |
56 | | - |
57 | | -### GraphQL API |
58 | | -Two endpoints: `api/v1` (public — OAuth, Zapier) and `private/v1` (web, electron). Located at `api/app/graphql/{api,private}/v1/`. |
59 | | - |
60 | | -### Background Processing |
61 | | -- **Sidekiq** for async jobs (`api/config/sidekiq.yml`) — use `perform_async`, not `perform_later` (ApplicationJob uses Sidekiq::Worker, not ActiveJob) |
62 | | -- **Clockwork** for scheduled jobs (`api/config/clock.rb`) |
63 | | -- **Kafka** for event streaming (`services/consumers/`) |
64 | | - |
65 | | -### Databases |
66 | | -PostgreSQL (primary) + TimescaleDB (time-series, separate connection) + MySQL (legacy) + Redis (cache, ActionCable, Sidekiq) |
67 | | - |
68 | | -### Real-time |
69 | | -AnyCable WebSocket server for subscriptions. Channels in `api/app/channels/`. |
70 | | - |
71 | | -## Code Patterns |
72 | | - |
73 | | -### Ruby/Rails |
74 | | -- Controllers validate + enqueue async jobs. Jobs handle business logic. Models handle delivery. |
75 | | -- Webhook controllers: `skip_before_action :authenticate_user!` + shared secret verification |
76 | | -- `CanonicalEmail.find_by_canonical(email:)` — uses `email_address` gem canonicalization; stub in tests |
77 | | -- `Identity#first_name` is a computed method (from `name` via `Nameable::Latin`), not a column |
78 | | -- `generate_hash_authentication_settings_url` calls `update!` internally — stub in tests via `allow_any_instance_of(Identity)` |
79 | | -- Test env uses `cache_store: :null_store` — swap to `MemoryStore` in `around` block for cache tests |
80 | | -- Postmark emails: all go through `PostmarkClient.deliver_in_batches_with_templates` with required keys: `email_enabled`, `email_bounced`, `message_stream` |
81 | | -- Prefer `be_between(before, after)` for time assertions (no `freeze_time` or `travel_to`) |
82 | | - |
83 | | -### JavaScript/TypeScript |
84 | | -- Use `test()` instead of `it()` in Jest tests |
85 | | -- Use `toBeCalled()` instead of `toHaveBeenCalledWith()` in assertions |
86 | | -- ESM: add `.js` extension to relative imports |
87 | | - |
88 | | -### Error Handling |
89 | | -- Prefer returning undefined over throwing exceptions |
90 | | -- Log and continue rather than crashing — filter nulls at boundaries |
91 | | -- Validate inputs at system boundaries (user input, external APIs, webhooks) |
92 | | - |
93 | | -## Scripts (`scripts/`) |
94 | | - |
95 | | -Standalone Node.js `.mjs` automation — outreach, content, analytics, CRM sync. Organized by side-effect type: |
96 | | - |
97 | | -- **`scripts/commit/`** — Scripts that produce repo artifacts (PRs, committed files). Includes `feedback/` for feedback collection and `profound-briefs/` for AEO pulse output. |
98 | | -- **`scripts/ops/`** — Marketing motions with external side effects (CRM sync, outreach, social content). |
99 | | -- **`scripts/diag/`** — Read-only diagnostics (pipeline health checks, demo scorecards). |
100 | | -- **`scripts/data/`** — Committed data artifacts (ICP keywords, pipeline config, profound learnings/snapshots). |
101 | | -- **`scripts/lib/`** — Shared utilities (Attio, Claude, Fathom, Slack, dates, prompts). |
102 | | - |
103 | | -Scheduled via GitHub Actions cron. All scheduled workflows support `workflow_dispatch` for manual runs. |
104 | | - |
105 | | -**GitHub Actions limit:** `workflow_dispatch` allows max 25 `inputs`. `weekly-start.yml` has 22/25 inputs. Feedback is consolidated into a single JSON `feedback` input: `{"social":"...","aeo":"...","blog":"...","snitcher":"..."}`. |
106 | | - |
107 | | -### Slack `/run` command |
108 | | -When adding or renaming GitHub Actions workflows that should be triggerable via Slack, update the `WORKFLOWS` hash in `api/app/jobs/trigger_github_workflow_job.rb`. When deleting a workflow, remove it from the hash. The Slack `/run` command reads this mapping to dispatch workflows. |
109 | | - |
110 | | -### Workflow → Script mapping |
111 | | - |
112 | | -| Workflow | Script path | Category | |
113 | | -|---|---|---| |
114 | | -| `weekly-start.yml` | `voyager/scripts/content-brief.mjs` + `voyager/scripts/content-audit.mjs` + `ops/fathom-social-content.mjs` + `ops/fathom-testimonial-scan.mjs` + `ops/perplexity-citation-audit.mjs` + `commit/profound-aeo-pulse.mjs` + `voyager/scripts/generate-blog-scaffold.mjs` + `ops/ahrefs-firehose-digest.mjs` + `ops/export-dripify.mjs` + `commit/prospect-discovery.mjs` + `ops/repush-clay-leads.mjs` + `ops/snitcher-outreach.mjs` | GHA cron (Mon) | |
115 | | -| `weekly-end.yml` | `diag/fathom-demo-scorecard.mjs` + `commit/feedback/collect-*.mjs` | GHA cron (Fri) | |
116 | | -| `anneal-keywords.yml` | `commit/anneal-keywords.mjs` | GHA cron (Sun) | |
117 | | -| `g2-review-monitor.yml` | `ops/g2-to-senja.mjs` | GHA cron (Daily) | |
118 | | -| `testimonial-pipeline.yml` | `commit/testimonial-pipeline.mjs` | Manual | |
119 | | -| `video-pipeline.yml` | `ops/video-clips.mjs` | Manual | |
120 | | -| `pagespeed-audit.yml` | `diag/pagespeed-audit.mjs` + `commit/pagespeed-improvements.mjs` | GHA cron (1st of month) | |
121 | | -| `daily-ops.yml` | `ops/slack-digest.mjs` + `ops/fathom-meeting-digest.mjs` + `ops/ops-daily-briefing.mjs` | GHA cron (weekdays) | |
122 | | -| `indexnow-submit.yml` | (inline curl) | Push to master (voyager) / Manual | |
123 | | - |
124 | | -## GitHub Actions (`.github/workflows/`) |
125 | | - |
126 | | -### CI/CD (PR-triggered) |
127 | | -- `test-api.yml` — RSpec on PR to `api/` |
128 | | -- `review-voyager-seo.yml` — SEO/AEO/GEO review on PR to `voyager/` |
129 | | -- `main.yml` — Deploy API/Web/Services/Docs/Voyager to staging on merge to master |
130 | | -- `deploy-production.yml` — Manual sequential prod deploy (API → Services → Web) |
131 | | - |
132 | | -### GitHub Actions gotcha |
133 | | -In `actions/github-script@v7`, `github.rest.issues.createComment` posts plain issue comments on PRs (PRs are issues in GitHub's API). For inline code suggestions on specific files/lines, use `github.rest.pulls.createReview` or `github.rest.pulls.createReviewComment` instead. |
134 | | - |
135 | | -### Scheduled (cron) |
136 | | -- `weekly-start.yml` — Mon 9am ET (content review, social content, testimonial scan, Perplexity audit, AEO pulse → blog scaffold, Ahrefs digest, Dripify export, prospect discovery → snitcher outreach) |
137 | | -- `weekly-end.yml` — Fri 9am ET (demo scorecard + pipeline health) |
138 | | -- `anneal-keywords.yml` — Sun 11am ET (keyword annealing + kill pattern updates) |
139 | | -- `g2-review-monitor.yml` — Daily 10am ET |
140 | | -- `pagespeed-audit.yml` — 1st of month 9am ET (PSI audit → Claude recommendations → PR) |
141 | | -- `daily-ops.yml` — Weekdays 10am ET (signal monitor, G2 reviews, review intercept, Slack digest → meeting digest → daily briefing) |
142 | | -- `indexnow-submit.yml` — On push to master (voyager pages) + manual (`/run indexnow urls=...`) |
| 35 | +## Git Conventions |
143 | 36 |
|
144 | | -## Deployments |
| 37 | +- Branch prefixes: `feature/`, `fix/`, `chore/` |
| 38 | +- Commit format: `type(scope): message` |
| 39 | +- Do NOT add `Co-Authored-By` lines to commits |
| 40 | +- Pre-commit hook runs: `npm run lint` + `npm run test` + E2E browser screenshots |
145 | 41 |
|
146 | | -### Staging (auto on merge to master) |
147 | | -- **API, Web, Services** — GCP Cloud Run via Docker (Artifact Registry) |
148 | | -- **Voyager** — GCP Cloud Run |
149 | | -- **Docs** — Heroku |
| 42 | +## Testing Rules |
150 | 43 |
|
151 | | -### Production (manual `workflow_dispatch` only) |
152 | | -- Sequential: API → 5min wait → Services → 5min wait → Web |
153 | | -- `gh workflow run deploy-production.yml --ref master` |
| 44 | +- **Framework**: Jest + SWC |
| 45 | +- **DB mocking**: Use dependency injection (DI), not global mocks |
| 46 | +- **Supertest**: Pass `app` (NOT `server`) to supertest |
| 47 | +- **Global jest**: src/ tests use global `jest` — do NOT import from `@jest/globals` (causes redeclaration errors) |
| 48 | +- **Mock reset**: `jest.clearAllMocks()` resets `mockReturnValue` — always re-set mocks in `beforeEach` |
| 49 | +- **Test runner**: `npm test` is long-running; run in a background process or sub-agent, not inline |
154 | 50 |
|
155 | | -## Voyager Content |
| 51 | +## ESLint Rules |
156 | 52 |
|
157 | | -Blog posts in `voyager/src/content/blog/*.mdx`. See `voyager/CLAUDE.md` for tone of voice, banned words, and content rules. |
158 | | - |
159 | | -Key patterns: |
160 | | -- Blog JSON-LD (BlogPosting) in `voyager/src/modules/blogJsonLd.js` |
161 | | -- FAQ structured data via `faqs` frontmatter array in blog MDX files |
162 | | -- Sitemap auto-includes all posts via `voyager/src/app/sitemap.js` |
163 | | -- Blog scaffold: `voyager/scripts/generate-blog-scaffold.mjs` (or `npm run content:scaffold`) |
164 | | -- Analytics events: `voyager/src/modules/analytics.js` |
165 | | -- Route paths: `voyager/src/utils/locations.js` |
166 | | - |
167 | | -## Style |
168 | | - |
169 | | -### Commits |
170 | | -- Plain imperative sentences, no conventional commit prefixes |
171 | | -- Short and direct — describe what, not why |
172 | | - |
173 | | -### Code |
174 | | -- Read before writing. Edit over rewrite. No docs unless asked. |
175 | | -- KISS / YAGNI / SOLID. Under 20 lines per function. |
176 | | -- Comments only for complex logic. No emojis in code. |
177 | | -- When blocked, try an alternative approach before asking. Explain what you tried and why it failed. |
178 | | -- Review your changes against the task requirements before reporting completion. |
179 | | - |
180 | | -## Knowledge Skills (.claude/skills/knowledge/) |
| 53 | +- Use `catch {}` not `catch (_err) {}` — underscore prefix not in the allowed pattern |
| 54 | +- CJS format for JS files in `src/` |
181 | 55 |
|
182 | | -Project-specific knowledge skills load automatically when prompts match `activates_on` keywords. They provide current API patterns, SDK versions, and gotchas that prevent hallucination. |
183 | | - |
184 | | -**When to suggest a new skill:** If you encounter a repeatable workflow where you got something wrong (wrong API shape, deprecated pattern, incorrect filter field), suggest creating a knowledge skill for it. Format: "This would be a good candidate for a `.claude/skills/knowledge/<name>.skill.md` — want me to create one?" |
| 56 | +## Key Patterns |
185 | 57 |
|
186 | | -Current skills: `postmark-email`, `nextjs-app-router`, `profound-mcp`, `greptile-review`, `tailwind-v4-design`, `rails-graphql-mutations`, `rails-sidekiq-clockwork`, `rails-billing-identity`, `electron-store-ipc`, `chrome-extension`, `blog-hero-images` |
| 58 | +- Provenance tracking: every data point includes source, timestamp, lineage |
| 59 | +- Multi-tenant container isolation |
| 60 | +- DI route factories for testability |
| 61 | +- Error handling: return undefined over throwing; log and continue over crashing |
| 62 | +- Add `.js` extension to relative ESM imports |
187 | 63 |
|
188 | | -## Key Files |
| 64 | +## StackMemory Context Rule |
189 | 65 |
|
190 | | -- `api/config/database.yml` — DB connections (primary + timescale) |
191 | | -- `api/config/sidekiq.yml` — Job queues and concurrency |
192 | | -- `api/config/clock.rb` — Scheduled jobs (Clockwork) |
193 | | -- `api/Procfile.dev` — Dev processes |
194 | | -- `api/app/services/postmark_client.rb` — Email delivery (all Postmark goes through here) |
195 | | -- `api/app/services/drip_campaign_config.rb` — Drip email templates + required keys |
196 | | -- `voyager/CLAUDE.md` — Blog tone, banned words, content rules |
197 | | -- `sol.code-workspace` — VS Code workspace |
198 | | -- Each project requires its own `.env` file (not in repo) |
| 66 | +- When an agent fetches conversation context for active work, it must pass the exact current assignment or question as `task_query`. |
| 67 | +- Prefer the MCP shape: |
| 68 | + - `org_id` |
| 69 | + - `conversation_id` |
| 70 | + - `task_query` |
| 71 | + - `recover_on_low_signal: true` |
| 72 | +- Do not fetch raw `get_conversation` context for worker execution unless full transcript behavior is explicitly required. |
0 commit comments