diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 000000000..d8fd0c650 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,27 @@ +{ + "extends": ["@commitlint/config-conventional"], + "rules": { + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "chore", + "ci", + "revert" + ] + ], + "type-case": [2, "always", "lowercase"], + "type-empty": [2, "never"], + "subject-empty": [2, "never"], + "subject-full-stop": [2, "never", "."], + "subject-case": [2, "never", ["start-case", "pascal-case", "upper-case"]], + "header-max-length": [2, "always", 100] + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..cd0153ca9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,141 @@ +# CI — NossoCRM +# +# Jobs: +# check — lint + typecheck + tests (runs on every PR and push to main/feature branches) +# build — Next.js production build (runs only on push to main) +# +# Cache strategy: +# - pnpm store: keyed on pnpm-lock.yaml hash via actions/setup-node cache: "pnpm" +# - .next/cache: keyed on source hash, restores on prefix match +# +# Security: no untrusted event inputs are interpolated into run: commands. + +name: CI + +on: + push: + branches: + - main + - "feature/**" + - "fix/**" + pull_request: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + NODE_VERSION: "20" + PNPM_VERSION: "9" + +jobs: + # ─── commitlint ──────────────────────────────────────────────────────────── + # Validates commit messages follow conventional commits format + # Runs only on PRs to catch format issues before merge + commitlint: + name: Validate Conventional Commits + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install commitlint + run: npm install --save-dev @commitlint/cli @commitlint/config-conventional --legacy-peer-deps + + - name: Validate commits + run: | + npx commitlint \ + --from ${{ github.event.pull_request.base.sha }} \ + --to ${{ github.event.pull_request.head.sha }} + + # ─── check ──────────────────────────────────────────────────────────────── + # Runs precheck:fast: ESLint (--max-warnings 0) + tsc --noEmit + vitest run + # Fast path — no Next.js build required. Runs on all triggering events. + check: + name: Lint / Typecheck / Tests + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint (ESLint — zero warnings) + run: pnpm lint + + - name: Typecheck (tsc --noEmit) + run: pnpm typecheck + + - name: Tests (Vitest) + run: pnpm test:run + + # ─── build ──────────────────────────────────────────────────────────────── + # Full Next.js production build. Runs only on pushes to main. + # Depends on check passing to avoid wasting build minutes on broken code. + build: + name: Build (Next.js) + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: check + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Restore Next.js build cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.next/cache + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.css') }} + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}- + ${{ runner.os }}-nextjs- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + env: + # Next.js build requires these to be present even if not used at runtime. + # Real values come from Vercel project settings — these are build-time only. + NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} + NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY }} diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 000000000..3b8d12962 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,50 @@ +# Preview — NossoCRM +# +# Triggers a Vercel Preview deployment on every PR. +# The deployment URL is posted as a PR comment via Vercel's GitHub integration. +# +# Prerequisites (set in repo Settings → Secrets and variables → Actions): +# VERCEL_TOKEN — personal access token from vercel.com/account/tokens +# VERCEL_ORG_ID — from .vercel/project.json or `vercel env pull` +# VERCEL_PROJECT_ID — from .vercel/project.json or `vercel env pull` +# +# This job does NOT run the build itself — Vercel handles the build on its +# infrastructure using the same Next.js config. The CI check job (ci.yml) +# is the quality gate; preview deploy runs in parallel to save time. +# +# Security: no untrusted event inputs are interpolated into run: commands. + +name: Preview Deploy + +on: + pull_request: + branches: + - main + +concurrency: + group: preview-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy-preview: + name: Deploy Preview to Vercel + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy to Vercel (Preview) + id: deploy + uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + # Do not promote to production on this workflow + vercel-args: "--no-wait" + github-comment: true + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..7c6ccfa2f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,96 @@ +# Release — NossoCRM +# +# Triggered when a semantic version tag (vX.Y.Z) is pushed to main branch. +# Creates a GitHub Release with auto-generated release notes from merged PRs. +# +# Usage: +# npm run release:prepare # Analyze commits and suggest version +# npm run release:draft # Preview changelog +# npm run release:tag # Create tag and push +# git push origin main +# git push origin vX.Y.Z # This triggers this workflow + +name: Release + +on: + push: + tags: + - "v*.*.*" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + # ─── release ────────────────────────────────────────────────────────────── + # Create GitHub Release from pushed semantic version tag + release: + name: Create Release + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history for changelog generation + + - name: Extract version from tag + id: version + env: + REF_NAME: ${{ github.ref_name }} + run: | + TAG="${REF_NAME}" + VERSION="${TAG#v}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "Release version: ${VERSION}" + + - name: Get previous release + id: prev_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + LATEST=$(gh release list --limit 1 --json tagName --jq -r '.[] | .tagName' 2>/dev/null || echo "") + if [ -z "$LATEST" ]; then + PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + else + PREV_TAG="${LATEST}" + fi + echo "previous_tag=${PREV_TAG}" >> $GITHUB_OUTPUT + + - name: Extract changelog for version + id: changelog + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + if [ -f "CHANGELOG.md" ]; then + # Extract section from ## [VERSION] to next ## [ + # Using a safe shell command to avoid injection + awk "/^## \[${VERSION}\]/,/^## \[/" CHANGELOG.md | head -n -1 > /tmp/changelog.txt + if [ -s /tmp/changelog.txt ]; then + # Convert to GitHub Actions multiline format + { + echo 'NOTES<> $GITHUB_ENV + fi + fi + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: Release ${{ steps.version.outputs.version }} + body: | + ${{ env.NOTES }} + + --- + + **Full Diff**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_release.outputs.previous_tag }}...${{ steps.version.outputs.tag }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..55cad8088 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,117 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- (Upcoming features will appear here) + +### Changed +- (Upcoming changes will appear here) + +### Fixed +- (Upcoming fixes will appear here) + +### Removed +- (Upcoming removals will appear here) + +--- + +## [0.1.0] - 2026-04-09 + +### Added + +#### Evolution API Integration +- End-to-end Evolution API WhatsApp provider support +- Display WhatsApp phone number from Evolution channel +- Support for Evolution API webhooks with multi-tenant authentication +- Evolution API option in channel setup wizard +- Capture outbound messages from WhatsApp app as sent messages + +#### AI Agent Enhancements +- `agent_goal_stage_id` field — autonomous agent scope per funnel +- Visual feedback for out-of-scope stages in goal stage config +- MCP server tools for AI agent stress-testing (`crm.ai.simulate.*`) +- Log handoff actions in `ai_conversation_log` +- Await `processIncomingMessage()` in dev mode for proper execution + +#### Settings & Configuration +- Dynamic AI model list from provider APIs +- Telegram integration with auto-detect chat_id via polling (zero-config UX) +- Test message button for Telegram validation + +#### Testing & Monitoring +- Vitest coverage for `agent_goal_stage_id` scope validation + +### Fixed + +#### AI Configuration +- Fix 3 silent bugs in AI configuration by stage +- Await `processIncomingMessage` execution in dev mode + +#### Evolution API +- Fix 3 Evolution code review issues +- Security improvements for Evolution webhook processing +- Content type handling and missing event handlers +- Fix 2 Evolution code review bugs + +#### Simulation & Reliability +- Fix reliability of S2/S3/S6 simulation scenarios +- Improve AI logging in simulation mode + +#### Webhook Processing +- Extract `channelId` by UUID regex to support `webhookByEvents` mode +- Remove non-existent columns from deals insert +- Fix 4 inbox bugs found by ultraplan audit + +#### Messaging +- Show outbound messages sent from phone in inbox +- Fix email channel realtime updates + +#### Telegram Integration +- Support groups in Telegram integration +- Fix Telegram disconnect handling +- Polish connected state display +- Fix Telegram notification sending on all handoff paths +- Fix CSPRNG usage in crypto operations + +### Removed +- References to OpenAI and Anthropic providers (100% consolidation to Google Gemini) +- Voice feature (ElevenLabs Conversational AI + WhatsApp Business Calling API) — tables preserved in database + +### Changed + +#### Provider Consolidation +- Consolidated to 100% Google Gemini for AI operations +- Removed OpenAI and Anthropic provider code + +### Refactored + +- Remove remaining references to OpenAI/Anthropic +- Clean up provider abstraction layer + +--- + +## Release Notes + +**Version 0.1.0** is the first development release of NossoCRM with core messaging and AI agent capabilities. + +### Status +- ✅ Messaging MVP complete (WhatsApp via Meta & Evolution, Email via Resend, Telegram, Instagram) +- ✅ AI Agent MVP complete (autonomous stage advancement with HITL, briefing generation) +- ⏳ Public API: Planned for v0.2.0 or v1.0.0 + +### Next Milestone (v0.2.0) +- Public API for message ingestion +- GraphQL API for CRM data +- Webhook signature verification hardening + +### Path to v1.0.0 +- Stabilize public APIs +- Security audit and penetration testing +- Performance optimization +- Comprehensive documentation diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..bf11bdeb6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,177 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +```bash +npm run dev # Dev server (porta 3000; se ocupada: fuser -k 3000/tcp) +npm run build # Build de produção +npm run lint # ESLint com zero warnings +npm run typecheck # TypeScript (tsc --noEmit) +npm run test # Vitest em watch mode +npm run test:run # Vitest single run +npm run precheck # lint + typecheck + test:run + build (pré-PR) +npm run precheck:fast # lint + typecheck + test:run (sem build) +npm run stories # Rodar test/stories/ (testes de comportamento) +``` + +Teste específico: +```bash +npx vitest run path/to/file.test.ts +``` + +## Stack + +Next.js 16 (App Router) · React 19 · TypeScript · Supabase (PostgreSQL + Auth + Edge Functions) · TanStack Query v5 · Zustand v5 · Tailwind CSS v4 · Radix UI · Zod v4 · AI SDK v6 (multi-provider: Anthropic/OpenAI/Google) + +## Arquitetura + +### Estrutura de Diretórios + +``` +app/ # Next.js App Router + (app)/ # Rotas autenticadas (layout principal) + (protected)/ # Rotas protegidas por auth + api/ # API Routes (ai/, messaging/, contacts/, settings/, etc.) +features/ # Módulos por domínio de negócio + activities/ boards/ contacts/ dashboard/ deals/ inbox/ + messaging/ settings/ ai-hub/ decisions/ reports/ +components/ # Componentes React compartilhados (não feature-specific) + ui/ # Primitivos UI (button, modal, badge, etc.) + ai/ # UIChat, chat-related +lib/ # Utilitários e serviços compartilhados + ai/ # AI agent, briefing, few-shot, HITL, tools + messaging/ # Providers (Meta, Evolution, Resend, Zapi) + query/ # Query keys factory + hooks TanStack Query + supabase/ # Clients e helpers Supabase (ver seção abaixo) + stores/ # Zustand stores (somente UI state efêmero) +context/ # React context providers — fachadas sobre TanStack Query + AuthContext.tsx # Fornece user, profile, organizationId, signOut +supabase/ + functions/ # Edge Functions (webhooks de mensageria) + migrations/ # Migrations SQL +proxy.ts # Auth proxy Next.js 16+ (NÃO é middleware.ts) +``` + +### Auth e Routing (Next.js 16+) + +**`proxy.ts` (não `middleware.ts`)**: No Next.js 16+, o arquivo de proxy chama-se `proxy.ts` (raiz do projeto). Ele apenas faz refresh de sessão Supabase SSR e redirect para `/login`. **Não intercepta `/api/*`** — Route Handlers respondem 401/403 diretamente (redirect 307 quebraria `fetch`). + +```typescript +// proxy.ts usa: +import { updateSession } from '@/lib/supabase/middleware' +``` + +### Clientes Supabase + +Há três clientes com propósitos distintos: + +| Cliente | Arquivo | Uso | +|---------|---------|-----| +| Browser SSR | `lib/supabase/client.ts` | Componentes client-side; pode retornar `null` sem `.env` | +| Server SSR | `lib/supabase/server.ts` | Route Handlers e Server Components (usa `server-only`) | +| Service Role | `lib/supabase/staticAdminClient.ts` | IA/ferramentas sem cookies — ignora RLS, sempre filtrar por `organization_id` | + +**Importar sempre de `@/lib/supabase`** (barrel export) — nunca de subcaminhos diretamente. + +### Variáveis de Ambiente + +Supabase introduziu novo formato de chaves (Nov 2025) com fallback de compatibilidade: + +``` +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY → fallback: NEXT_PUBLIC_SUPABASE_ANON_KEY +SUPABASE_SECRET_KEY → fallback: SUPABASE_SERVICE_ROLE_KEY +``` + +`SUPABASE_SECRET_KEY` é server-only — nunca expor no client. + +### Padrões Críticos + +**cn utility**: importar de `@/lib/utils` (não `@/lib/utils/cn`) + +**Auth**: `useAuth()` de `@/context/AuthContext` retorna `{ user, profile, organizationId, signOut }` + +**Query Keys**: todas as queries usam o factory em `lib/query/queryKeys.ts` +```typescript +queryClient.invalidateQueries({ queryKey: queryKeys.deals.all }) +queryClient.invalidateQueries({ queryKey: queryKeys.deals.list({ boardId }) }) +``` + +**Deals — source of truth única**: +```typescript +// DEALS_VIEW_KEY = [...queryKeys.deals.lists(), 'view'] +// Usar esta key em TODOS os pontos de escrita (mutations, Realtime, otimismo) +queryClient.setQueryData(DEALS_VIEW_KEY, updater) // preferível a invalidate +``` +Nunca usar `queryKeys.deals.list({ filter })` para optimistic updates — são caches separados. + +**AI SDK v6**: usar `generateText + Output.object({ schema })`, resultado em `result.output` +```typescript +// CORRETO +const result = await generateText({ ...options, output: Output.object({ schema: MySchema }) }) +result.output // typed result + +// ERRADO — API antiga +await generateObject({ ... }) +``` + +**Chaves de API do AI**: ficam em `organization_settings` (banco), não em env vars +```typescript +const config = await getOrgAIConfig(orgId) // lê ai_google_key, ai_openai_key, ai_anthropic_key +const model = getModel(config.provider, config.apiKey, config.model) +``` + +**Realtime**: invalidação targeted em `lib/realtime/useRealtimeSync.ts` — nunca invalidar globalmente. UPDATE/DELETE usam debounce; INSERT não. + +**Sanitize**: usar `sanitizePostgrestValue()` e `sanitizeUrl()` de `lib/utils/sanitize.ts` + +**RLS defense-in-depth**: todas as queries filtram por `organization_id` além do RLS — especialmente crítico com service role (IA/tools). + +**maybeSingle() vs single()**: usar `.maybeSingle()` para lookups que podem retornar 0 rows; `.single()` lança erro se não encontrar. + +**Schema Supabase**: tabela `board_stages` (não `stages`), coluna `"order"` (não `position`) + +### AI — Fluxo de Dados + +Dois caminhos distintos: + +1. **Chat interativo (streaming)**: `UIChat` → `POST /api/ai/chat` → `lib/ai/crmAgent.ts` → ferramentas em `lib/ai/tools.ts` +2. **Tasks / structured output**: `lib/ai/tasksClient.ts` → `app/api/ai/tasks/**/route.ts` + +**HITL (Human-in-the-Loop)**: +- `confidence >= hitlThreshold` (default 0.85) → avança automaticamente +- `0.70 <= confidence < hitlThreshold` → cria `ai_pending_stage_advances` (aprovação humana) +- `confidence < 0.70` → não sugere avanço + +**Segurança de prompt**: todo conteúdo de mensagem do usuário vai dentro de `` tags — nunca interpolar diretamente no system prompt. + +### Supabase Edge Functions + +Webhooks de mensageria são Edge Functions (não API Routes): +- `messaging-webhook-evolution` — Evolution API (WhatsApp) +- `messaging-webhook-meta` — Meta Cloud API (WhatsApp + Instagram) +- `messaging-webhook-resend` — Email via Resend +- `messaging-webhook-zapi` — Z-API (WhatsApp) + +Webhooks retornam HTTP 200 mesmo em erros de processamento (evita retry storms). + +### Credenciais de Canal + +Credenciais nunca retornam ao client em list queries — só no detail query para edição, mascaradas. + +### Feature Flags + +Controladas por `instanceFlags` (operador) via `queryKeys.instanceFlags.byOrg(orgId)`. + +### Testes + +- Testes unitários: arquivos `.test.ts(x)` ao lado do código-fonte (features/components) +- Testes de comportamento (user stories): `test/stories/` +- Testes de integração/agent: diretamente em `test/` +- Setup: `test/setup.ts` (carrega `.env.local`, mock `server-only`) + `test/setup.dom.ts` (jest-dom, polyfills) +- Ambiente padrão: `happy-dom` (todos os testes rodam com DOM) + +### Migrations + +Migrations em `supabase/migrations/` com timestamp `YYYYMMDDHHMMSS`. Sempre idempotentes (`IF NOT EXISTS`, `ON CONFLICT DO NOTHING`). Não deletar migrations históricas — tabelas legadas (`voice_calls`, `whatsapp_calls`) existem no banco sem código correspondente. diff --git a/RELEASE-SETUP.md b/RELEASE-SETUP.md new file mode 100644 index 000000000..26d4ed4a5 --- /dev/null +++ b/RELEASE-SETUP.md @@ -0,0 +1,260 @@ +# Release Engineering Setup — Complete + +✅ **Release engineering for NossoCRM is now fully configured.** + +This document summarizes what was set up and how to use it. + +## What Was Implemented + +### 1. Conventional Commits Validation +- **File**: `.commitlintrc.json` +- **Type**: Configuration for commitlint +- **Rules**: Validates commit format on all PRs (GitHub Actions job) +- **Format**: `type(scope): description` + +**Types supported**: +- `feat` — New feature +- `fix` — Bug fix +- `refactor` — Code reorganization +- `docs` — Documentation +- `test` — Tests +- `chore` — Maintenance +- `ci` — CI/CD +- `perf` — Performance +- `style` — Formatting + +### 2. Changelog Management +- **File**: `CHANGELOG.md` +- **Format**: Keep a Changelog +- **Initial content**: 0.1.0 release with all recent features and fixes +- **Auto-update**: Updated by `npm run release:tag` + +### 3. Release Automation Scripts +- **File**: `scripts/release.mjs` +- **Functions**: + - `npm run release:prepare` — Analyze commits and suggest version + - `npm run release:draft` — Preview changelog + - `npm run release:tag` — Create tag and update CHANGELOG.md + +**Example output**: +``` +📊 Release Analysis +Current version: 0.1.0 +Latest tag: v1.0.0 + +📈 Suggested version bump: MINOR + 0.1.0 → 0.2.0 + +📝 Changes breakdown: + Breaking: 0 + Features: 64 + Fixes: 62 + Refactors: 13 + Docs: 5 +``` + +### 4. GitHub Actions Workflows + +#### Commitlint Job (in `.github/workflows/ci.yml`) +- **Trigger**: Every PR to main +- **Action**: Validates all commits follow conventional format +- **Failure**: PR shows failed check if commits don't match format + +#### Release Workflow (`.github/workflows/release.yml`) +- **Trigger**: Tag push with `vX.Y.Z` format +- **Action**: Creates GitHub Release +- **Notes**: Auto-extracted from CHANGELOG.md +- **Link**: Full diff between releases + +### 5. Documentation +- **`RELEASE.md`** — Detailed release workflow and troubleshooting +- **`docs/release-engineering.md`** — Setup guide and best practices +- **`RELEASE-SETUP.md`** — This file + +## Quick Start Workflow + +### 1. Develop Features +```bash +git checkout -b feature/add-new-api +git commit -m "feat(api): add new endpoints" +git push origin feature/add-new-api +gh pr create --title "feat: Add new API" +``` + +### 2. Review & Merge +```bash +# After approval +gh pr merge 123 --squash +``` + +### 3. Prepare Release +```bash +npm run release:prepare +npm run release:draft # Review changes +npm run release:tag # Create tag +``` + +### 4. Push to GitHub +```bash +git push origin main +git push origin v0.2.0 +``` + +**Result**: GitHub Actions automatically creates the GitHub Release. + +## Files Created + +| File | Purpose | Size | +|------|---------|------| +| `.commitlintrc.json` | Commitlint configuration | 581 B | +| `CHANGELOG.md` | Project changelog | 3.5 KB | +| `RELEASE.md` | Release process guide | 7.9 KB | +| `scripts/release.mjs` | Release automation script | 9.8 KB | +| `.github/workflows/release.yml` | GitHub Release creation | 3.1 KB | +| `.github/workflows/ci.yml` (updated) | Added commitlint job | — | +| `package.json` (updated) | Added release scripts | — | +| `docs/release-engineering.md` | Setup & best practices | 7.9 KB | + +## Version Strategy + +**Current Phase**: 0.x.y (Development) + +| Version | Timeline | Status | +|---------|----------|--------| +| 0.1.x | Current | MVP released | +| 0.2.x | Q2 2026 | Public API planned | +| 1.0.0 | Future | GA planned | + +**Release to 1.0.0 when**: +- All messaging providers stabilized (Meta, Evolution, Email, Telegram, Instagram) +- Public REST/GraphQL APIs frozen +- Security audit passed +- Documentation complete + +## Semantic Versioning + +``` +MAJOR.MINOR.PATCH + +MAJOR: Breaking changes (feat!) +MINOR: New features (feat) +PATCH: Bug fixes (fix) +``` + +## Commit Format Examples + +```bash +# Feature +git commit -m "feat(api): add v2 REST endpoints" + +# Bug fix +git commit -m "fix(webhook): handle concurrent processing" + +# Breaking change +git commit -m "feat(api)!: rename /deals to /opportunities" + +# With body +git commit -m "feat(messaging): add email support + +Adds Resend integration. + +Closes #456" +``` + +## Running Release Commands + +### Prepare Release +```bash +npm run release:prepare +``` +Analyzes commits since last tag and suggests version bump. + +### Preview Changelog +```bash +npm run release:draft +``` +Shows what will be added to CHANGELOG.md. + +### Create Release +```bash +npm run release:tag +``` +1. Updates CHANGELOG.md +2. Updates package.json version +3. Creates commit and tag +4. Prints next steps + +### Finalize (Push) +```bash +git push origin main +git push origin v0.2.0 +``` + +## CI Validation + +### On Pull Requests +- ✅ Commitlint: Validates conventional commit format +- ✅ ESLint: Zero warnings +- ✅ TypeScript: No type errors +- ✅ Tests: All passing + +### On Tag Push +- ✅ GitHub Actions: Creates release from CHANGELOG.md + +## Key Rules + +1. **Commit format is mandatory** — All commits must follow `type(scope): description` +2. **Squash merge to main** — Keep main branch clean with one commit per feature +3. **Tag from main** — Never release from feature branches +4. **CHANGELOG.md is canonical** — Release notes come from this file +5. **Pre-releases supported** — Can create v1.0.0-alpha.1, v1.0.0-beta.1, v1.0.0-rc.1 + +## Troubleshooting + +### Commitlint fails on PR +Commits don't follow format. +```bash +git rebase -i origin/main +# Edit commits to follow "type(scope): description" +git push --force-with-lease +``` + +### Need to redo release +Delete local and remote tag: +```bash +git tag -d v0.2.0 +git push origin :refs/tags/v0.2.0 +npm run release:prepare +npm run release:tag +``` + +### No changes to release +Only `chore`, `ci`, `style`, `test` commits since last tag. +Need at least one `feat:` or `fix:` commit. + +## Standards Followed + +- **Semantic Versioning 2.0.0** — https://semver.org/ +- **Conventional Commits 1.0.0** — https://www.conventionalcommits.org/ +- **Keep a Changelog** — https://keepachangelog.com/ + +## Next Steps + +1. ✅ Commit release setup to main branch +2. ✅ Create first release with `npm run release:tag` +3. ✅ Tag and push to GitHub +4. ✅ Verify GitHub Release is created automatically +5. ✅ Update CI/CD pipeline to use releases (deployment-manager) + +## Related Documentation + +- [RELEASE.md](RELEASE.md) — Detailed release process +- [docs/release-engineering.md](docs/release-engineering.md) — Setup guide +- [CHANGELOG.md](CHANGELOG.md) — Project changelog +- [.commitlintrc.json](.commitlintrc.json) — Validation rules + +--- + +**Setup completed on**: 2026-04-09 +**Initial version**: 0.1.0 +**Suggested next release**: 0.2.0 diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 000000000..15734947b --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,330 @@ +# Release Process — NossoCRM + +This document describes the release workflow for NossoCRM, from commit to GitHub Release. + +## Overview + +Release engineering for NossoCRM follows **semantic versioning** (MAJOR.MINOR.PATCH) with **conventional commits** for automated changelog generation. + +- **Semantic Versioning**: https://semver.org/ +- **Conventional Commits**: https://www.conventionalcommits.org/ +- **Keep a Changelog**: https://keepachangelog.com/ + +## Version Strategy + +NossoCRM uses `0.x.y` versioning during active development: + +| Version | Status | Notes | +|---------|--------|-------| +| 0.1.x | Current MVP | Messaging + AI Agent | +| 0.2.x | Q2 2026 | Public REST API | +| 1.0.0 | Future | Stable APIs + GA | + +**Release to 1.0.0 when:** +- Messaging APIs stabilized (Meta, Evolution, Email, Telegram) +- AI Agent HITL workflow proven in production +- Public REST/GraphQL APIs documented and tested +- Security audit completed + +## Workflow + +### 1. Develop Features (Normal Workflow) + +Push to `feature/*` branches with conventional commits: + +```bash +git checkout -b feature/add-api-v2 +git commit -m "feat(api): add v2 REST endpoints for deals" +git push origin feature/add-api-v2 +``` + +**Commit Format**: +``` +type(scope): description + +[optional body] +[optional: BREAKING CHANGE: description] +``` + +**Types** (from `.commitlintrc.json`): +- `feat`: New feature → **MINOR** version bump +- `fix`: Bug fix → **PATCH** version bump +- `feat!`: Breaking change → **MAJOR** version bump +- `refactor`: Code reorganization (no new features) +- `docs`: Documentation only +- `chore`: Maintenance, dependencies +- `ci`: CI/CD configuration +- `test`: Test additions/fixes +- `perf`: Performance improvements +- `style`: Code style (formatting, semicolons) + +### 2. Create Pull Request + +On GitHub, open a PR against `main`: + +```bash +gh pr create --title "feat: Add public API v2" --body "..." +``` + +**Required checks**: +- ✅ Commitlint (validates conventional format) +- ✅ Lint (ESLint zero warnings) +- ✅ Tests (Vitest all passing) +- ✅ Typecheck (tsc --noEmit) + +### 3. Merge to Main + +After review and approval, **squash + merge** to `main`: + +```bash +gh pr merge 123 --squash +``` + +This ensures a clean history with one commit per feature. + +### 4. Prepare Release + +When ready to release, analyze commits since last tag: + +```bash +npm run release:prepare +``` + +Output: +``` +📊 Release Analysis +Current version: 0.1.0 +Latest tag: v0.1.0 + +📈 Suggested version bump: MINOR + 0.1.0 → 0.2.0 + +📝 Changes breakdown: + Breaking: 0 + Features: 3 + Fixes: 5 + Refactors: 2 + Docs: 1 +``` + +### 5. Preview Changelog + +Review what will be released: + +```bash +npm run release:draft +``` + +Output: +``` +📋 Changelog Preview for v0.2.0 + +## [0.2.0] - 2026-04-15 + +### Added +- feat(api): add v2 REST endpoints for deals +- feat(messaging): support Telegram thread replies +- feat(ai): add few-shot learning for sales qualification + +### Fixed +- fix(webhook): handle concurrent message processing +- fix(email): fix bounce event parsing + +### Changed +- refactor(auth): simplify session management +- refactor(db): optimize deal query indexes + +This will be prepended to CHANGELOG.md +Run "npm run release:tag" to create the release +``` + +### 6. Create Release + +When satisfied with the changelog: + +```bash +npm run release:tag +``` + +This: +1. ✅ Updates `CHANGELOG.md` with new version section +2. ✅ Updates `package.json` version +3. ✅ Commits changes (`chore(release): vX.Y.Z`) +4. ✅ Creates annotated git tag +5. 📋 Prints next steps + +Output: +``` +✅ Created tag v0.2.0 + +Next steps: + git push origin main + git push origin v0.2.0 + +GitHub Actions will create the release automatically. +``` + +### 7. Trigger GitHub Release + +Push the tag to GitHub: + +```bash +git push origin main +git push origin v0.2.0 +``` + +The `.github/workflows/release.yml` workflow automatically: +1. Extracts version from tag +2. Finds previous release +3. Reads changelog section from `CHANGELOG.md` +4. Creates GitHub Release with formatted notes +5. Links to full diff + +## GitHub Release Output + +Example release on GitHub: + +``` +# Release 0.2.0 + +## [0.2.0] - 2026-04-15 + +### Added +- feat(api): add v2 REST endpoints for deals +- feat(messaging): support Telegram thread replies + +### Fixed +- fix(webhook): handle concurrent message processing + +--- + +**Full Diff**: [v0.1.0...v0.2.0](https://github.com/thaleslaray/nossocrm/compare/v0.1.0...v0.2.0) +``` + +## Key Files + +| File | Purpose | +|------|---------| +| `CHANGELOG.md` | Manually maintained changelog in "Keep a Changelog" format | +| `package.json` | Version source of truth | +| `.commitlintrc.json` | Conventional commit validation rules | +| `scripts/release.mjs` | Release automation script | +| `.github/workflows/ci.yml` | Commitlint validation on PRs | +| `.github/workflows/release.yml` | GitHub Release creation on tag push | + +## CI Validation + +### On Pull Request +- ✅ **Commitlint**: Validates all commits follow conventional format +- ✅ **Lint**: ESLint with zero warnings +- ✅ **Tests**: Vitest all passing +- ✅ **Typecheck**: TypeScript compilation + +### On Tag Push +- ✅ **GitHub Release**: Auto-creates release from CHANGELOG.md + +## Pre-release Workflow (Alpha/Beta) + +For early testing, create pre-release tags: + +```bash +# Create alpha tag (does not update latest) +git tag -a v0.2.0-alpha.1 -m "Release 0.2.0-alpha.1" +git push origin v0.2.0-alpha.1 + +# Later: create beta when stabilized +git tag -a v0.2.0-beta.1 -m "Release 0.2.0-beta.1" +git push origin v0.2.0-beta.1 + +# Finally: create release candidate +git tag -a v0.2.0-rc.1 -m "Release 0.2.0-rc.1" +git push origin v0.2.0-rc.1 + +# Then: final release +npm run release:tag # Creates v0.2.0 +``` + +The release workflow automatically marks pre-releases (contains `alpha`, `beta`, `rc`) as pre-releases on GitHub. + +## Hotfixes + +For urgent fixes to production (if deployed): + +```bash +git checkout -b fix/urgent-bug-in-v0.1.0 +git commit -m "fix: Critical fix for issue #456" +git push origin fix/urgent-bug-in-v0.1.0 + +# Create PR against main +gh pr create --title "fix: Critical security patch" + +# Merge to main +gh pr merge 789 --squash + +# Now release patch version +npm run release:prepare # → suggests 0.1.1 +npm run release:draft +npm run release:tag +git push origin main +git push origin v0.1.1 +``` + +## Troubleshooting + +### "No changes to release" + +All commits since last tag are `chore`, `ci`, `style`, or `test` (no features or fixes). + +**Solution**: Features must be commits with `feat:` or `fix:` prefix. + +### Commitlint validation fails on PR + +Commits don't follow conventional format (e.g., "Fixed login bug" instead of "fix: login bug"). + +**Solution**: Rebase and amend commits to follow format: +```bash +git rebase -i origin/main +# Edit commits to follow "type(scope): description" +git push --force-with-lease +``` + +### Tag already exists + +A tag with that version is already pushed. + +**Solution**: Create a new patch version: +```bash +npm run release:prepare # Check next suggested version +npm run release:draft +npm run release:tag +``` + +### Need to update CHANGELOG.md manually + +For major releases or special cases, edit `CHANGELOG.md` directly before tagging: + +```bash +# Edit CHANGELOG.md by hand +git add CHANGELOG.md +git commit -m "chore: Update changelog for v1.0.0" +git tag -a v1.0.0 -m "Release v1.0.0" +git push origin v1.0.0 +``` + +## Best Practices + +1. **Commit often**: Small commits are easier to review and revert +2. **Use conventional format**: Enables automation and clear history +3. **Link to issues**: Use `Closes #123` in PR/commit bodies +4. **Test before release**: Run `npm run precheck` locally +5. **Update CHANGELOG.md**: Keep "Unreleased" section current +6. **Tag from main**: Releases should only come from main branch +7. **Create release notes**: Use GitHub UI to add deployment notes +8. **Archive old releases**: Close old milestones, keep one active + +## Related Documentation + +- [Conventional Commits](https://www.conventionalcommits.org/) +- [Semantic Versioning](https://semver.org/) +- [Keep a Changelog](https://keepachangelog.com/) +- [GitHub Release API](https://docs.github.com/en/rest/releases/releases) diff --git a/app/(protected)/layout.tsx b/app/(protected)/layout.tsx index 6cd40f877..e81501e78 100644 --- a/app/(protected)/layout.tsx +++ b/app/(protected)/layout.tsx @@ -1,5 +1,3 @@ -'use client' - import ProtectedShell from './ProtectedShell' import Script from 'next/script' @@ -13,7 +11,7 @@ export default function ProtectedLayout({ {/* lamejs loaded globally to avoid Turbopack CJS interop issues. Mp3Encoder uses internal vars (MPEGMode) that Turbopack tree-shakes when imported as ESM. Script tag runs in original scope, preserving closures. */} -