docs(planning): archivia v2.1/v2.2/v2.3 e documenta v2.4

.planning/ documentava in dettaglio cio che era vecchio e per niente cio
che e in produzione: le fasi 11-22 (v2.1 e v2.2, chiuse a giugno) erano
ancora in phases/ mentre v1.0 e v2.0 stavano gia in milestones/, e il
lavoro degli ultimi due mesi - gate OTP e ciclo di vita dei retainer, cioe
quello che gira su hub.iamcavalli.net - non aveva nessuna cartella.

- phases/{11,12,14} -> milestones/v2.1-phases/, phases/{18..22} ->
  milestones/v2.2-phases/. Ora phases/ contiene solo la milestone in
  corso, che e quello che state.cjs conta per il progresso
- v2.1-ROADMAP.md ricostruito: era l'unica milestone senza archivio,
  interrotta dal reset del 19/06 e mai chiusa formalmente
- v2.3-ROADMAP.md + v2.3-REQUIREMENTS.md: v2.3 e stata eseguita fuori dal
  ciclo GSD, non esistono PLAN/SUMMARY per fase. L'archivio E la doc
- REQUIREMENTS.md riscritto per v2.4 con il backlog reale
- phases/13 e phases/26: SUMMARY ricostruiti da commit, migration e
  STATUS.md. 26 e il primo numero libero
- research/: cancellate 4 varianti dello stesso PITFALLS e FEATURES/
  SUMMARY, superati da PROJECT.md. Diverse anti-feature erano ormai
  contraddette dai fatti (il Kanban e stato costruito in Phase 19,
  l'email in v2.3, il time tracking esiste)
- cancellati UI-RULES.md e DESIGN-SYSTEM.md (CLAUDE.md li dichiara
  superseded: impongono l'inverso della regola attuale) e HANDOFF.md,
  fermo al 13/06
- SECURITY-*.md -> security/: audit chiuso, ma i report restano la doc di
  cosa e stato ruotato e perche
- PROJECT.md/MILESTONES.md/ROADMAP.md allineati: milestone corrente v2.4,
  sessione OTP 90gg non 30, migrazioni fino alla 0016

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 22:38:36 +02:00
parent 187550fedf
commit f7eb7eec23
86 changed files with 1659 additions and 2859 deletions
-107
View File
@@ -1,107 +0,0 @@
# FEATURES.md — ClientHub Freelancer Client Portal
**Domain:** Freelancer client portal — solo personal branding consultant
**Project:** ClientHub (welcomeclient.iamcavalli.net)
**Researched:** 2026-05-09
**Confidence:** HIGH
---
## Context
Two asymmetric roles. Admin (the freelancer) has full CRUD. Client (read + lightweight interaction) accesses via secret URL — no login, no account — and can view, comment, and approve. The product competes indirectly with Notion client portals, HoneyBook, Dubsado, and bespoke agency portals. The differentiator is zero-friction secret link access and personal brand positioning.
---
## Table Stakes
Features clients expect when opening any project portal. Missing these causes confusion, distrust, or support overhead.
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|------------|-------|
| Project overview at a glance | Client needs to know "where we are" without reading walls of text | Low | Name, brand, brief, current phase |
| Phase + task status visibility | Primary client question is "what's done, what's next" | Low | Phases with nested tasks; status per task (todo / in progress / done) |
| Deliverable approval | Client must formally sign off on outputs | Medium | Per-deliverable approve action; state persists; admin sees approval timestamp |
| Inline commenting on tasks/deliverables | Feedback and questions without email | Medium | Flat comments sufficient for v1; threading is nice-to-have |
| Document / file links | Deliverables, briefs, contracts surface in the portal | Low | Links to Google Drive, PDF, external URL; no file hosting needed |
| Payment status visibility | Client needs to know what they owe | Low | Deposit 50% + balance 50%; three states each: pending / invoiced / paid |
| Total quoted amount (not itemized) | Client expects to see the agreed number | Low | Single total; line items are admin-only |
| Mobile-readable layout | Clients open links on phones | Low | Responsive web; no native app |
| Persistent secret link | Link must not expire or rotate without notice | Low | UUIDs in DB, never regenerated unless admin resets explicitly |
| Trustworthy, branded appearance | First impression determines confidence in the consultant | Low | Logo, brand colors, professional typography — not a generic SaaS look |
---
## Differentiators
Not expected, but meaningfully improve experience or workflow.
| Feature | Value Proposition | Complexity | Notes |
|---------|-------------------|------------|-------|
| Decision log / history | Running record of agreed decisions — eliminates "we never agreed on that" disputes | Low | Append-only note stream visible to client; admin writes entries |
| Phase progress indicator | Visual progress bar gives a sense of momentum | Low | Derived from task completion %; no extra data model needed |
| "Last updated" timestamp on dashboard | Shows the portal is live and maintained | Low | Trivially derived from DB updated_at |
| Admin overview: all clients at a glance | Freelancer scans all active projects and overdue payments in one view | Medium | List with status badges; payment alert if overdue |
| Payment status badge with clear labels | Color-coded states (red = unpaid, yellow = invoiced, green = paid) | Low | Client sees their own; admin sees all |
| Shareable link reset | Admin can invalidate and regenerate a client's link if it leaks | Low | DB field update + redirect; rarely used but reassuring |
| Service catalog | Admin builds quotes from a curated menu of services; reusable across clients | Medium | Lookup table; admin-only; used by Claude in v2 |
| Claude-assisted onboarding (v2) | Generates phases + quote draft from a brief — massively speeds up admin work | High | Explicitly v2 in PROJECT.md |
---
## Anti-Features
Deliberately NOT building these.
| Anti-Feature | Why Avoid | What to Do Instead |
|--------------|-----------|-------------------|
| Client login / account creation | Adds friction with no benefit for a small client list | Secret UUID link |
| In-app invoicing / PDF generation | Accounting is out of scope | Show payment status only |
| File upload / storage | Massive complexity | Link to Google Drive or Dropbox |
| Email / SMS notifications | Transactional email infrastructure is heavy | Manual communication fine for small client list |
| Multi-admin / team roles | Freelancer works alone | Single admin |
| Client-editable project structure | Clients editing phases corrupts admin's source of truth | Comment and approve only |
| Itemized pricing visible to client | Erodes commercial confidentiality | Single total; detail is admin-only |
| Kanban / drag-and-drop board | Phases are sequential, not a fluid backlog | Ordered phase list |
| Time tracking | Out of scope for project-based billing | Not relevant |
| Multi-language / i18n | Single consultant, single-market | Hardcode interface language |
---
## Feature Dependencies
```
Secret link (UUID) → Client dashboard
Client dashboard → Phase/task display
Phase/task display → Deliverable approval
Phase/task display → Inline commenting
Admin client management → Secret link generation
Admin client management → Payment tracking
Service catalog → Quote building (admin picks from catalog)
Quote building → Payment tracking (total = basis for deposit/balance)
Service catalog → Claude onboarding v2
```
**Key insight:** Admin must create data before the client dashboard shows anything meaningful. Admin-first, then client.
---
## MVP Build Order
1. Admin: create/edit client record with secret link generation
2. Admin: create/edit phases and tasks per client
3. Admin: set payment amounts and statuses
4. Client dashboard: read-only view (overview, phases, tasks, payment status, documents)
5. Client: deliverable approval
6. Client: inline comments
7. Admin: all-clients overview
8. Admin: service catalog
9. v2: Claude-assisted onboarding
---
## Open Questions
- What happens when a client accidentally shares their secret link? Is link reset sufficient, or should there be an access log?
- Does the decision log need to be visible to clients from day one, or deferred?
- Should approval actions be reversible (un-approve)?
-412
View File
@@ -1,412 +0,0 @@
# Feature Landscape: Business Operations Suite v2.0 (New Features)
**Domain:** Proposal generation + lightweight CRM for solo personal-branding consultant
**Researched:** 2026-06-10
**Research Mode:** Ecosystem (proposal software + lightweight CRM patterns)
**Confidence:** MEDIUM-HIGH
**Scope:** ONLY new v2.0 features (does NOT review v1.0 table stakes already shipped)
---
## Executive Summary
The Business Operations Suite adds three interconnected workflows to ClientHub:
1. **Proposal Generation & Delivery** — Sales call → multistep proposal page (2-hour delivery) → lead selects tier A/B/C → acceptance triggers automation
2. **Lead Pipeline Management** — Minimal CRM for tracking prospects through 5 stages (Contacted → Qualified → Proposal Sent → Negotiating → Won/Lost)
3. **Onboarding Automation** — When lead is marked Won, auto-create client + project with phases copied from chosen offer + configurable payment schedule
**For a solo consultant, this is radically different from team CRMs** (Pipedrive, HubSpot, GoHighLevel). Avoid: territory management, team routing, email automation, role-based access, forecasting rollups, lead scoring algorithms. Instead: **minimal data entry, follow-up reminders based on last-contact date, and seamless handoff from proposal acceptance to project setup.**
**Core insight:** Solo consultants manage long-term relationships across years, not one-time deals closed in 30 days. Pipeline stages should reflect relationship milestones (Contacted, Qualified, Proposal Sent), not sales rep activity (Attempted, Left Message). Follow-ups are "who did I talk to recently?" not "assign task to rep."
---
## Table Stakes Features (New v2.0)
Features users expect when adopting a proposal + CRM system. Missing these = product feels incomplete.
### Proposal Generation & Delivery
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|-----------|-------|
| Pick client + 13 offers, set per-quote prices | Core value: sales call → proposal in 2 hours. Prices set per quote, not from catalog (allows price increases over time without updating service catalog) | Medium | Depends on existing `offers` & `clients` (Phase 5). Quote prices override offer base prices. |
| Generate public multistep HTML/CSS page | Leads need clickable link, not PDF email. Interactive engagement beats static documents (Qwilr/Proposify standard). Reduces abandonment. | Medium | Responsive, mobile-first, branded with consultant name/logo. No login required. |
| Multistep flow (intro → pricing tiers → CTA) | Reduces cognitive overload vs. single long page. Step 1: offer overview. Step 2: A/B/C tiers with pricing. Step 3: accept/decline button. | Low | Conditional logic hides irrelevant sections; simple progressive disclosure. |
| Public acceptance button (no e-signature) | Lead confirms tier choice WITHOUT entering hub. Triggers: timestamp capture + email record + lead status update. | Low | Simple "Accept this offer" button → records accepted_at + accepted_by_email. E-signature deferred (design ready, v1 constraint). |
| Proposal URL shareable & public | Lead receives link in email, opens in browser, no friction. | Low | Generate unique slug (`/proposal/[uuid]`). No auth. Optionally expires after N days or on acceptance. |
| Acceptance proof (signer + timestamp) | Documentation for dispute resolution. Downloadable PDF or stored audit record. | Low | Store: accepted_by_name, accepted_by_email, accepted_at, accepted_offer_id. |
### Lead Pipeline
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|-----------|-------|
| Pipeline stages: Contacted → Qualified → Proposal Sent → Negotiating → Won/Lost | Core tracking. Each stage requires buyer action, not just rep activity. Termination: Won or Lost. | Low | **Minimal for solo:** 5 stages max. More = data entry overhead. Stages are immutable enums. |
| Move lead between stages (UI) | Basic pipeline UX. See all leads grouped by stage at a glance. | Low | Dropdown per row (simple) or kanban board (polish). Dropdown sufficient for MVP. |
| Lead details: name, email, company, phone, last_contact_date, next_action, notes | Minimum context for follow-ups. | Low | Notes = freeform text (call outcomes, concerns, personality notes, next steps). |
| Lead created manually OR auto-created from proposal send | Manual: import prospects. Auto-create: when proposal sent to unknown email, create lead record automatically. | Low | Auto-create is convenience; manual is fallback. Both supported. |
| Log activities: calls, emails, meetings, notes | Track interactions without leaving dashboard. Auto-updates last_contact_date. | Low | Activity types: [call, email, meeting, note]. Log date, duration, description. Displayed as feed. |
### Follow-Up Reminders
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|-----------|-------|
| Dashboard widget: "Follow up today" sorted by last_contact_date | Solves core pain: "who did I talk to recently that I should check in with?" | Low | Query: WHERE last_contact_date <= TODAY - 7 days AND stage NOT IN (Won, Lost). Red badge. |
| Proposal stall detector: if Proposal Sent >3 days, no response | Flags silence = red flag for follow-up. | Low | Simple dashboard alert; no email automation (consultant checks dashboard in morning). |
| Auto-update last_contact_date on activity | Keeps reminders accurate. Prevents "I called them but forgot to log it" drift. | Low | Trigger: on call/email/meeting/note creation OR stage change → update lead.last_contact_date = NOW. |
### Project Auto-Creation (Won → Onboarding)
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|-----------|-------|
| On lead.stage = Won: auto-create `client` + `project` with copied phases | Closing automation. Proposal acceptance → instant hub readiness for delivery. | Medium | Requires: chosen offer known, project_offers finalized (Phase 5), 1-4 configurable installments. |
| Copy offer phases → project phases (modifiable) | Project inherits offer structure but can be customized per client without mutating offer template. | Medium | Phases are modifiable in hub (Phase 1). Creation source logged: "Copied from Offer X on [date]" for audit. |
| Set payment installments per project (not per offer) | Payment plan configured case-by-case: some 50/50, others 4-part. Allows flexibility. | Medium | Templates: 50/50 (acconto/saldo), 3-part, 4-part, custom. UI: choose template during Won → project creation. |
| Lead → Client transition: copy email, create token, link to existing client if repeat | Avoid duplicate clients. Allow linking Won lead to existing client (repeat engagement). | Medium | Check email collision. If exists: "Add project to existing client X?" vs. "Create new client". Generate secret token. |
---
## Differentiators (New v2.0)
Features that set ClientHub apart from generic proposal software + CRM. Not expected, but valued for solo consultant use case.
| Feature | Value Proposition | Complexity | Notes |
|---------|-------------------|-----------|-------|
| Tier-independent offer structure (Signature A/B/C as separate offers) | Each tier has own URL, phases, pricing. A = professional, B = budget—same catalog, different scope. No cloning, no inheritance logic. | Medium | Requires flexible offer builder (drag services between phases), offer status (draft/active/archived). Built in Phase 5. |
| Proposal phases visible in public page (lead sees what they're buying) | Lead sees not just price, but scope: "4-week branding sprint, 3 revision rounds, monthly retainer". Transparency builds confidence. | Low | Proposal page shows phase names, deliverables summary (text), timeline (start + duration). |
| Dashboard follow-up list filterable by offer type + stage | "Show all Signature A leads in Negotiating" to spot bottlenecks by tier. | Low | Filter buttons: by offer type, stage, last_contact_date range. Dropdown or toggle buttons. |
| Activity feed per lead (call notes, email log, meetings, searchable) | Context without leaving dashboard. "Last contact: 2026-05-28, 11am call—timeline concerns". | Medium | Depends on activity logging system. Store type, date, duration, description. Searchable. |
| Proposal pages branded with consultant info (not generic SaaS) | Proposal is part of sales process; branding reinforces personal brand. | Low | Logo, consultant name, brand colors, professional typography inherited from admin settings. |
| Email integration hint (design ready, defer to later batch) | Send proposal via dashboard; auto-log email send as activity. | Medium | Deferred per PROJECT.md (v1 constraint). Placeholder: manual copy-paste OK for MVP. |
---
## Anti-Features: What NOT to Build (v2.0)
| Anti-Feature | Why Avoid | What to Do Instead |
|--------------|-----------|-------------------|
| Multi-user team collaboration, role-based access | Solo consultant is single admin. Team features add auth complexity, sync issues, notification storms. Overhead >> value. | Stay single-admin. Architecture with permission flags but don't build UI/logic. If team joins later, migrate incrementally. |
| CRM email automation, drip campaigns, email sequences | Solo consultant sends proposals + checks in manually. Automation-heavy workflows are for outbound prospecting funnels (not this use case). Lead generation not in scope. | Keep simple: dashboard reminder = "check in with X". Consultant sends email manually, logs it as activity. |
| Forecasting with rollups, quota tracking, team capacity planning, territory assignment | These are team sales metrics. Solo consultant cares: "How many leads in pipeline?" and "What's revenue if all Negotiating deals close?" | Simple dashboard: shows total value by stage (e.g., "Negotiating: $45k"), breakdown by offer type. No forecast math. |
| Lead scoring, MQL → SQL qualification algorithms, scoring rules | Solo consultant qualifies leads manually (call + gut feel). Scoring requires training data and tuning. | Keep manual: stage = Contacted, Qualified, Proposal Sent. "Qualified" is consultant's call. No automation. |
| Calendar sync, email sync, call recording, Slack/Teams integration | Adds external dependency surface area. Consultant logs calls + emails manually. | Fallback: freeform "Call notes" text field per activity. No attempt to auto-log from email/Slack. |
| Multi-currency support, tax calculation, invoice generation, accounting system integration | Accounting is out of scope (PROJECT.md locked). Payment tracking only. | Keep simple: amount_accepted in EUR, payment schedule in local currency. No conversion logic or invoicing. |
| Proposal version control, edit history, detailed audit trail | Overkill for solo consultant. One proposal per client, accept or decline. | Simple: created_at, updated_at timestamps. Accepted version is locked (immutable once accepted_at set). No versioning. |
| Client can reverse/un-approve acceptance | Creates ambiguity in closing. | Once accepted_at is set, proposal is final. Consultant can move lead back to Proposal Sent if needed. |
---
## Feature Dependencies (v2.0)
```
Proposal Generation:
├─ clients (exists, Phase 1)
├─ projects (exists, Phase 1)
├─ offers (Phase 5: id, name, offer_type [Entry/Signature/Retainer/custom], created_at)
├─ services catalog (Phase 3: id, name, price, duration_days)
└─ offer_services (Phase 5: junction table, offers ↔ services)
Lead Pipeline & CRM:
├─ leads table (NEW: id, name, email, company, phone, created_at, last_contact_date, stage, offer_id FK, next_action text)
├─ activities table (NEW: id, lead_id FK, type enum [call/email/meeting/note], log_date, description, duration_minutes)
├─ lead_stage enum (NEW: Contacted, Qualified, Proposal Sent, Negotiating, Won, Lost)
Project Auto-Creation (Won → Onboarding):
├─ leads.stage = Won (from pipeline, not auto-set)
├─ projects (exists, must be creatable via API/trigger)
├─ phases (exists, must be copyable from offer phases)
├─ payments (Phase 5: must support project_id FK, amount, due_date, status)
└─ clients.token (exists, Phase 1/4: rotatable secret)
Proposal Public Pages:
└─ proposals table (NEW: id, client_id, lead_email, offers [JSON array], created_at, public_url, accepted_at, accepted_by_name, accepted_by_email, accepted_offer_id)
```
**Critical dependency:** Phase 5's `project_offers` relationship must be finalized. If not, proposal generation cannot proceed. Phase 7 proposal work is blocked by Phase 5 completion.
---
## Feature Categorization by Component
### Component 1: Proposal Builder & Public Pages
**Responsibility:**
- Admin UI: select client, pick 13 offers, confirm per-quote pricing, set lead email, generate shareable link
- Public page: multistep form (intro → tiers → CTA), responsive, no login, branded
- Acceptance: simple "Accept" button, no e-signature
- Database: `proposals` table
**Features in scope:**
- Proposal generation from client + offers + prices
- Public multistep page with interactive tier selection
- Conditional phase visibility (show/hide sections based on tier)
- Acceptance button + timestamp + email capture
- Auto-create lead on proposal send (if lead email not in DB)
- Proposal expiry (optional, default: never)
**Database schema:**
```
proposals:
id UUID PK
client_id FK → clients
lead_email VARCHAR (recipient)
offer_ids JSON array (which offers are included)
quote_prices JSON {offer_id: price} (per-quote override)
created_at timestamp
updated_at timestamp
public_url slug
accepted_at timestamp nullable
accepted_by_name VARCHAR nullable
accepted_by_email VARCHAR nullable
accepted_offer_id FK nullable (which tier was chosen)
```
### Component 2: Lead Pipeline & Dashboard
**Responsibility:**
- Lead table with name, email, company, stage, last_contact_date, notes
- Dashboard: table or kanban view of all leads grouped by stage
- Activities: log calls, emails, meetings, notes
- Follow-up reminder widget: leads not contacted in 7+ days
**Features in scope:**
- Create/edit/delete leads manually or auto-create from proposal
- Move lead between stages (dropdown or drag/drop)
- Log activities: type [call/email/meeting/note], duration, description
- Auto-update last_contact_date on activity
- Follow-up reminders: "Follow up today" widget, proposal stall detector
- Lead notes: freeform text, searchable
- Filter by offer type, stage, date range
- Activity feed per lead (searchable)
**Database schema:**
```
leads:
id UUID PK
name VARCHAR
email VARCHAR (unique or indexed)
company VARCHAR nullable
phone VARCHAR nullable
stage enum [Contacted, Qualified, Proposal Sent, Negotiating, Won, Lost]
offer_id FK → offers nullable (which offer was sent)
created_at timestamp
last_contact_date date
next_action text nullable (next step consultant plans)
notes text (accumulated call notes, concerns, personality)
activities:
id UUID PK
lead_id FK → leads
type enum [call, email, meeting, note]
log_date timestamp
duration_minutes INT nullable
description text
created_at timestamp
```
### Component 3: Project Auto-Creation & Onboarding Flow
**Responsibility:**
- On lead.stage = Won, trigger creation: new client (if not exists), new project, copy phases, set payment schedule
- UI: lead moves to Won → modal: "Which offer was chosen?" → "Confirm project creation?" → choose payment template → create
- Result: lead archived, project appears in /admin/projects, client gets dashboard URL
**Features in scope:**
- Auto-create client from lead details
- Auto-create project with copied phases
- Copy phases from offer (modifiable in hub)
- Payment installments: choose template → creates N payment records
- Generate client secret token + dashboard URL
- Optional: link to existing client instead of creating new
- Notification: "Lead X → Project Y created on [date]"
**Database schema:**
```
No new tables needed. Uses existing: clients, projects, phases, payments.
When lead → Won:
1. Check if lead.email in clients table
2. If not: create new client, generate secret token
3. If yes: ask to link to existing client or create new
4. Copy phases from offer_id to new project
5. Create N payment records based on chosen template
6. Update lead.stage = Won (already set by UI)
7. Record: project creation_source = "Lead X, offer Y"
```
---
## MVP Recommendation
### Phase 7 (MVP): Core Proposal Generation + Basic Pipeline
**Priority 1: Must-Have (Launch features)**
1. **Proposal builder UI:** client + offer(s) + per-quote prices → generate public multistep page
2. **Public proposal page:** Step 1 = offer overview, Step 2 = A/B/C tiers + pricing, Step 3 = "Accept this Offer" CTA
3. **Acceptance flow:** "Accept" button → timestamp + email capture, marks proposal as accepted
4. **Lead auto-creation:** when proposal sent to unknown email, auto-create lead record
5. **Basic lead pipeline:** Contacted, Qualified, Proposal Sent, Negotiating, Won, Lost stages
6. **Lead dashboard:** table view, move stage via dropdown, lead details sidebar
7. **Follow-up reminder widget:** leads not contacted in 7+ days, sorted by last_contact_date, dashboard badge
8. **Activity logging (basic):** create call/email/meeting records, auto-update last_contact_date
**Estimated effort:** 57 days (proposal builder UI + public page + lead CRUD + follow-up reminders)
**Launch readiness:** Solo consultant can generate proposal in 2 hours, track lead through pipeline, see follow-up reminders.
---
### Phase 8 (Enrichment): Lead Context & Polish
**Priority 2: Features**
1. **Lead notes:** freeform text per lead, searchable
2. **Activity feed:** display recent activities per lead (call, email, meeting, note), searchable
3. **Dashboard filters:** by offer type, stage, date range (last_contact_date)
4. **Proposal stall detector:** flag if Proposal Sent >3 days with no activity
5. **Lead detail card:** expand to show full context (notes, activities, next_action) without leaving dashboard
**Estimated effort:** 34 days
**Value:** Consultant has full context for each lead without switching views.
---
### Phase 9+ (Automation & Onboarding): Project Auto-Creation
**Priority 3: Features**
1. **Project auto-creation:** on lead → Won, auto-create client + project with copied phases
2. **Copy phases from offer:** phases inherit name, duration, deliverables from offer template; modifiable in hub
3. **Payment installments:** choose template (50/50, 3-part, 4-part, custom) → create N payment records
4. **Client token generation:** auto-generate secret token, send dashboard URL to client
5. **Duplicate client check:** if lead.email exists in clients, ask to link or create new
6. **Won lead notification:** dashboard shows "Lead X → Project Y created", activity logged
**Estimated effort:** 34 days
**Value:** Closing automation. Lead acceptance → instant project setup in hub, ready for delivery.
---
## Complexity Assessment (Effort & Maintenance)
| Feature | Dev Effort | Maintenance Burden | Deferability | Phase |
|---------|-----------|-------------------|----------------|-------|
| Proposal generation (builder UI + logic) | 23 days | Low (static) | No—core | 7 |
| Public multistep page (HTML/CSS template) | 12 days | Low (static) | No—core | 7 |
| Acceptance flow (button + timestamp) | 1 day | Minimal | No—core | 7 |
| Lead pipeline (CRUD + stage transitions) | 1 day | Low | No—core | 7 |
| Follow-up reminders (dashboard query) | 4 hours | Minimal | No—core | 7 |
| Activity logging (calls, emails, meetings) | 1 day | Low | Yes—Phase 8 | 8 |
| Activity feed (search + timeline) | 1 day | Low | Yes—Phase 8 | 8 |
| Lead notes + search | 1 day | Low | Yes—Phase 8 | 8 |
| Dashboard filters (offer type, stage, date) | 1 day | Low | Yes—Phase 8 | 8 |
| Project auto-creation (trigger + cascade) | 2 days | Medium (error handling) | Yes—Phase 9 | 9 |
| Payment installments (templates) | 1 day | Low | Yes—Phase 9 | 9 |
| Client token generation + link | 4 hours | Low | Yes—Phase 9 | 9 |
| Duplicate client check | 4 hours | Low | Yes—Phase 9 | 9 |
| Email integration (send proposal, auto-log) | 23 days | Medium (provider) | Yes—defer | Later |
| Multi-user + team features | 35 days | Medium-High (roles) | N/A—anti-feature | Never |
**Total Phase 7 MVP:** ~56 days
**Total Phases 79:** ~1114 days (complete suite)
---
## Solo Consultant Design Constraints
### What's Different from Team CRMs
| Constraint | Implication | Design Pattern |
|-----------|-----------|-----------------|
| Single admin, no team routing | No "assign to rep" or "owner" field. All leads = Simone. | All leads belong to admin; no ownership concept. No role-based UI. |
| Relationship-heavy, not transaction-heavy | Leads stay in pipeline for months, not 30 days. | Stage = relationship milestone (Contacted, Qualified, Proposal Sent), not rep activity (Attempted, Left Message, Following Up). |
| Qualitative follow-up, not automation | "Check in with X, heard timeline concerns" not email sequences. | Notes + activity log, no email automation. Manual send, logged as activity. |
| Low deal volume (515 in flight) | Pipeline visualization doesn't need advanced analytics. | Simple table or 5-column kanban board. No forecasting rollups. |
| Minimal data entry tolerance | Won't log every call if it takes >3 clicks. | Modal: "Log call → name/date/notes → save" in <10 seconds. |
| No role-based access | No "manager sees forecast, rep sees pipeline". | Single "admin dashboard" with all data. No permission layers. |
| No forecasting pressure | Consultant forecasts deal value by feel, not rollup math. | Dashboard shows "Total Negotiating: $45k". That's it. No probability weighting. |
| Calendar/email often offline (field work) | Email sync is nice-to-have, not must-have. | Fallback: manual entry. No real-time sync requirement. |
| Accountability is personal, not hierarchical | No "why is this deal stalled?" escalations. | Follow-up reminder = consultant decides action. No automatic escalations. |
### UX Patterns for Solo Consultant
1. **Minimize data entry by default:** Yes/No confirmations, checkboxes, dropdowns. Avoid text fields unless essential.
2. **Context visible by default:** Open a lead → see last 3 activities, next_action, stage, offer type in one view. No tabs.
3. **Dashboard as command center:** Lead reminders + upcoming payments + next meetings all visible. No app switching.
4. **Proposal as shared artifact:** Lead sees what they're buying (phases, deliverables, price). Consultant sees acceptance status + lead email.
5. **Automation where it reduces clicks:** Accept proposal → auto-create lead. Move to Won → ask once for payment plan, auto-create project. Don't auto-email.
---
## Pipeline Benchmark (Reference)
From industry research:
| Metric | Benchmark | Implication for ClientHub |
|--------|-----------|---------------------------|
| Typical pipeline stages | 57 stages | ClientHub uses 5: Contacted, Qualified, Proposal Sent, Negotiating, Won/Lost. Minimal overhead. |
| Proposal Sent → Negotiation conversion | 40%+ | Flag stale proposals (>3 days, no activity) in dashboard. |
| Negotiation → Won conversion | 50%+ | If low, consultant reviews proposal scope or pricing. |
| Lead follow-up frequency | 8+ touches to close | Activity logging tracks touches (calls, emails, meetings). No automation; manual follow-up. |
| Typical pipeline review cadence | Weekly or bi-weekly | Consultant checks dashboard daily; reviews pipeline health weekly. |
| Average deal cycle for consultants | 3090 days | ClientHub supports long cycles (no pressure to close fast). |
---
## Confidence & Sources
| Area | Confidence | Basis | Sources |
|------|------------|-------|---------|
| Proposal software features (multistep, acceptance, tiering) | HIGH | Verified with Qwilr, PandaDoc, Proposify; 2026 comparison articles | [Qwilr vs PandaDoc](https://www.proposify.com/blog/qwilr-vs-pandadoc), [PandaDoc vs Proposify vs Qwilr](https://saas-tools.medium.com/pandadoc-vs-proposify-vs-qwilr-which-proposal-tool-is-worth-your-budget-in-2026-8e8339d6ab87), [8 best proposal software](https://www.getaccept.com/blog/proposal-software) |
| Pipeline stage structure (57 stages, Won/Lost termination) | HIGH | Verified with Pipedrive, Capsule CRM, Salesforce; "Proposal Sent → Negotiation → Won" is standard | [Salesforce Pipeline Management](https://www.salesforce.com/sales/pipeline/management/), [CRM Pipeline Stages](https://prospeo.io/s/crm-pipeline-stages), [GoHighLevel Pipeline](https://ecosire.com/blog/ghl-crm-pipeline-management) |
| Lead follow-up reminders (last_contact_date, 7+ days flagged) | MEDIUM-HIGH | Sourced from Outreach, HubSpot, Nimble best practices; solo consultant context is reasonable inference | [Sales Pipeline Best Practices](https://www.nimble.com/blog/best-practices-of-sales-pipeline-management/), [Outreach Pipeline Management](https://www.outreach.ai/resources/blog/sales-pipeline-management-best-practices) |
| Solo consultant CRM avoidance of team features | MEDIUM | Sourced from "Best CRM for Solopreneurs" guides; solo preference for simplicity consistent across 3+ sources | [Breakcold: CRM for Consultants](https://www.breakcold.com/blog/crm-for-consultants), [Authencio: CRM for Freelancers](https://www.authencio.com/blog/best-crm-for-consultants-freelancers-guide), [Addtocrm: Best CRM Solopreneurs](https://addtocrm.com/tools/best-crm-for-solopreneurs), [Mimiran: Anti-CRM](https://www.mimiran.com/fun-crm-for-solo-consultants-who-hate-selling/) |
| Tiered proposal (A/B/C independent offers) | MEDIUM-HIGH | Verified via multiple consulting pricing guides; "3 tiers ideal" is consensus. Independent offers is ClientHub-specific. | [Mercury: Pricing Strategy](https://mercury.com/blog/pricing-strategy-consulting), [Ignition: Tiered Pricing](https://www.ignitionapp.com/blog/tiered-pricing-strategy-for-professional-services-proposal-templates), [Consulting Success: Consulting Rates](https://www.consultingsuccess.com/consulting-rates) |
| Multi-step form engagement patterns | HIGH | Verified with Heyflow, Optimonk, HubSpot research; form completion rates improve with progressive disclosure | [Instapage: Multi-Step Forms](https://instapage.com/blog/multi-step-forms), [HubSpot: Multi-Step Forms](https://blog.hubspot.com/marketing/multi-step-forms), [Webstacks: Multi-Step Form Examples](https://www.webstacks.com/blog/multi-step-form) |
| Proposal acceptance automation (→ project creation) | MEDIUM | Sourced from Anchor, Estimate Rocket, Monograph workflows. ClientHub implementation is custom but pattern is established. | [Sayanchor: Proposal Acceptance](https://www.sayanchor.com/post/proposal-acceptance-guide), [Monograph: Build, Send, Sign](https://monograph.com/blog/build-send-and-sign-proposals-with-pipeline), [DocuSign: Workflow Automation](https://www.docusign.com/blog/workflow-automation-electronic-signatures) |
---
## Open Questions for Later Phases
1. **Email integration:** Should proposals be sent via dashboard integration, or manual copy-paste? (Deferred to Phase 10+)
2. **Proposal expiry:** Should proposals auto-expire after N days, or stay open indefinitely? (MVP: no expiry)
3. **Lead de-duplication:** If lead email is already in clients table, how should system handle it? (MVP: manual check; Phase 9+ auto-detect)
4. **Activity type richness:** Should activities include location, participant names, sentiment tags? (MVP: basic type + notes; Phase 8 can enhance)
5. **Proposal versioning:** Can consultant send multiple proposals to same lead? (MVP: yes, same lead.email + new proposal record)
6. **Payment plan customization:** Can consultant define custom installment schedules, or only use templates? (MVP: templates; Phase 9 can add custom)
7. **Lead archive vs. delete:** When lead is Won → converted to client/project, should lead record stay in DB for history? (Recommendation: keep for audit trail; mark archived or moved_to_project_id)
---
## Appendix: Frequently Asked Questions
**Q: Why no e-signature in MVP?**
A: Design is ready (CLAUDE.md notes it as deferred). A simple "Accept" button + timestamp is sufficient proof of acceptance for solo consultant use case. E-signature (DocuSign, Stripe Sign) adds external dependency and cost; defer unless client explicitly requests.
**Q: Should proposals auto-expire?**
A: No, not in MVP. Solo consultant may send proposal Monday, follow up Thursday, acceptance Friday. Expiry is team-sales friction (forces re-quote). Keep simple: accepted_at = null means still open.
**Q: Can a lead be in two stages at once?**
A: No. Stage is singular immutable state at any moment. If second proposal sent to same lead: either (a) create new lead record (same email, add suffix "2"), or (b) update same lead.stage to "Proposal Sent" again. Option (b) is simpler; stage can repeat; last_contact_date updates on each proposal send.
**Q: Should activities auto-log from email/Slack?**
A: No, not in MVP. Adds external dependency surface. Fallback: consultant manually logs calls/emails as activities in <10 seconds. Acceptable for solo consultant volume (515 leads).
**Q: What if lead email already exists in clients table?**
A: Phase 9 auto-detection: when moving lead to Won, check if lead.email in clients. If yes, ask: "Link to existing client X (repeat engagement)?" or "Create new client". Prevents duplicate records.
**Q: Can phases be reordered after project creation?**
A: Yes. Phases are copied from offer at project creation time, but project phases are modifiable in hub (Phase 1, already built). Offer template is never mutated. Studio-grade immutability.
**Q: What's the payment flow after Won → project creation?**
A: Consultant chooses payment template (50/50, 3-part, 4-part, custom) during Won → project creation modal. System creates N records in `payments` table with due_dates calculated from project start_date. Client sees payment schedule in hub dashboard (Phase 1, already built). No invoicing or collection automation (out of scope, PROJECT.md locked).
**Q: Can consultant change their mind after accepting a proposal?**
A: If consultant moves lead back to Proposal Sent or Negotiating (after accepting and creating project), the original accepted_proposal record remains immutable. Project can be deleted/archived if needed, but proposal acceptance is final. This matches real-world: once accepted, consultant can't un-close a deal.
---
**End of FEATURES_v2.0.md**
File diff suppressed because it is too large Load Diff
-210
View File
@@ -1,210 +0,0 @@
# Pitfalls Checklist: Business Operations Suite v2.0
Quick reference for preventing the 9 major pitfalls during Phase 79 implementation.
---
## Critical Pitfalls (Before Code)
### 1. Catalog Consolidation Breaks Quotes
- [ ] Migration strategy documented (Expand-Contract, not drop)
- [ ] Mapping layer between old/new tables designed
- [ ] Backfill script tested on production backup
- [ ] Referential integrity test query written
- [ ] Dual-write period defined (2-3 weeks)
- [ ] Rollback plan documented (drop new table, revert code)
### 2. Offer Template Mutation
- [ ] Offer/project hierarchy mapping documented (1 page)
- [ ] Copy function uses `db.transaction()` (all-or-nothing)
- [ ] Uses `structuredClone()` for deep copy, not `{...spread}`
- [ ] Idempotency key added to CRM leads table
- [ ] Integration test: copy offer, verify structure consistent across instances
- [ ] Test: edit project phase, verify offer template unchanged
### 3. Public Quote Token Leakage
- [ ] Token length ≥ 32 chars (nanoid(32) or nanoid(64))
- [ ] Expiration field added (default 7 days)
- [ ] Email validation on public page (token + email required)
- [ ] Rate limiting implemented (max 3 views/token/minute)
- [ ] Code audit: quote_items NOT in public API response
- [ ] Test: brute-force 1000 guesses, verify rate limit activates
- [ ] Test: enumerate tokens with different emails, verify 401
---
## Moderate Pitfalls (During Implementation)
### 4. Drag-and-Drop Race Condition
- [ ] `version` field added to offer_phases
- [ ] Update mutation checks version (returns 409 if mismatch)
- [ ] Server recomputes all sort_orders atomically
- [ ] Frontend handles 409 Conflict (refresh UI)
- [ ] Test: concurrent drag in 2 tabs, verify final state correct
### 5. CRM "Win" Double-Click
- [ ] Idempotency key added to crm_leads (unique)
- [ ] "Win" mutation checks if key already processed
- [ ] All sub-steps in single `db.transaction()`
- [ ] Button disabled until success
- [ ] Idempotency key stored in localStorage
- [ ] Test: double-click "Win", verify only 1 client created
### 6. Offer Copy Missing Tasks/Deliverables
- [ ] Mapping documented: offer_micro → project_phase → project_task → deliverable
- [ ] Copy function recursive (phases → tasks → deliverables)
- [ ] Validation test: assert counts match expected
- [ ] Integration test exists (copy, verify structure)
- [ ] Query test: no orphaned deliverables (task_id IS NULL)
---
## Minor Pitfalls (Polish)
### 7. Offer State Not Synced Between Tabs
- [ ] `version` field on offer_micros
- [ ] Update returns 409 on version mismatch
- [ ] UI shows "Offer changed, reload?" dialog
### 8. CRM Schema Backward Incompatibility
- [ ] New columns added as NULLABLE
- [ ] Code handles NULL defensively (e.g., `lead.budget ?? 0`)
- [ ] Backfill script written (sets defaults for existing rows)
- [ ] NOT NULL constraint added only after backfill + validation
- [ ] Test: production data copied to test db, code runs without errors
### 9. CRM Scope Creep
- [ ] Must-have features defined for v2.0 (lead pipeline, quote, auto-onboard)
- [ ] Should-have features listed for v2.1+ (email, calls, source)
- [ ] Won't-have features documented (team, multi-user, integrations)
- [ ] Scope document signed off by product
- [ ] Success metric defined: "Can go from lead to won project in <30 min"
---
## Phase 7 Checklist (Catalog & Offers)
**Before code:**
- [ ] Catalog consolidation migration plan reviewed
- [ ] Offer/project hierarchy documented
- [ ] Drag-drop version/locking strategy designed
**During implementation:**
- [ ] Migration backfill tested on production backup
- [ ] Drag-drop version field added
- [ ] Offer phase copy function atomic (transaction)
- [ ] Idempotency key on CRM leads
- [ ] Integration tests for copy consistency
**Before go-live:**
- [ ] Dry-run migration on production DB
- [ ] Verify referential integrity (no orphaned quote_items)
- [ ] Concurrent drag-drop test passes
- [ ] Offer copy count validation passes
---
## Phase 8 Checklist (Public Quote Pages)
**Before code:**
- [ ] Token security requirements finalized
- [ ] API response schema designed (no quote_items)
- [ ] Rate limiting strategy defined
**During implementation:**
- [ ] Token generation (nanoid(32))
- [ ] Expiration + email validation
- [ ] Rate limiting middleware
- [ ] Code audit for quote_items exposure
- [ ] Brute-force test (>1000 guesses/sec)
**Before go-live:**
- [ ] Brute-force test activates rate limit
- [ ] Enumeration test returns 401 for wrong email
- [ ] Expiration test returns 401 after expiration
- [ ] Code review: quote_items not in response
---
## Phase 9 Checklist (CRM Won Automation)
**Before code:**
- [ ] Scope document (Must/Should/Could/Won't) reviewed
- [ ] Idempotency strategy designed
- [ ] Payment plan algorithm defined
- [ ] Offer snapshot structure (JSONB) designed
**During implementation:**
- [ ] Idempotency key on crm_leads
- [ ] "Win" mutation atomic (client + project + phases + payments)
- [ ] Offer snapshot stored on win
- [ ] Button disabled until success
- [ ] Payment consistency tests
**Before go-live:**
- [ ] Double-click test: only 1 client created
- [ ] Network timeout test: retry returns same project
- [ ] Partial failure test: rollback on error
- [ ] Payment consistency test: correct number created
- [ ] Idempotency test: 10x "Win" with same key = 1 client
---
## Key Questions Before Each Phase
**Phase 7:**
- ✓ Is catalog consolidation migration safe (Expand-Contract, dual-write)?
- ✓ Does offer/project hierarchy mapping make sense (1 micro = 1 phase)?
- ✓ Is copy function atomic (all-or-nothing)?
- ✓ Do we have integration tests for offer phase structure consistency?
**Phase 8:**
- ✓ Is token length ≥ 32 chars and properly random?
- ✓ Do we validate both token AND email on public page?
- ✓ Is rate limiting active (max 3 views/token/min)?
- ✓ Can we prove quote_items never appears in public response?
**Phase 9:**
- ✓ Is "Win" action atomic (transaction)?
- ✓ Does idempotency key prevent duplicates on retry?
- ✓ Is button disabled until success?
- ✓ Have we defined Must/Should/Won't scope with user?
---
## Testing Validation Matrix
| Pitfall | Test Case | Expected Outcome | Status |
|---------|-----------|------------------|--------|
| 1. Catalog consolidation | Backfill migration on prod backup | Zero orphaned quote_items | [ ] |
| 2. Offer template mutation | Edit project phase, check offer unchanged | Offer template immutable | [ ] |
| 3. Token leakage | Brute-force 1000 guesses/sec | Rate limit activates (429) | [ ] |
| 3. Token leakage | Enumerate with wrong email | Returns 401 Unauthorized | [ ] |
| 4. Drag-drop race | Concurrent drag in 2 tabs | Final sort_order is coherent | [ ] |
| 5. Double-click clients | Click "Win" twice | Only 1 client created | [ ] |
| 5. Double-click clients | Network timeout + retry | Same project returned | [ ] |
| 6. Missing deliverables | Copy offer to project | All tasks and deliverables copied | [ ] |
| 8. Schema backward compat | Copy production data to test DB | No crashes, NULL handled | [ ] |
| 9. Payment consistency | "Win" with partial failure | Rollback, no orphaned payments | [ ] |
---
## Red Flags During Development
🚨 **Stop and review if:**
- Offer/project hierarchy mapping is unclear (should be 1 page, explicit)
- Drag-drop sort_order updates without version check
- "Win" mutation has any non-transactional steps (client created outside txn)
- Copy function uses shallow spread (`{...obj}`) instead of `structuredClone()`
- Token length < 32 chars
- Public API response includes quote_items or per_service_price
- CRM leads table has no idempotency_key column
- Schema changes add NOT NULL without backfill test
- "Win" button not disabled until response received
- No rate limiting on public quote page
---
**Owner:** Development team
**Review:** Before each phase kickoff
**Update:** As testing results come in
-302
View File
@@ -1,302 +0,0 @@
# Pitfalls Research Summary: Business Operations Suite (v2.0)
**Project:** ClientHub v2.0 — Adding Business Operations Suite to Production
**Research Date:** 2026-06-10
**Confidence:** HIGH (domain-specific research backed by schema review + security analysis)
---
## Overview
Adding a complex Business Operations Suite (catalog consolidation + offer builder + public quotes + CRM automation) to a production app with real client data creates integration risks not present when building from scratch. This research documents the most dangerous pitfalls (rewrites/data loss) and operational friction points, with concrete prevention strategies for each.
**Key insight:** Most pitfalls cluster in four areas of high integration complexity:
1. **Schema consolidation** — breaking existing quote_items referential integrity
2. **Template-to-instance copy semantics** — offer phases mutating templates
3. **Optimistic UI + concurrent edits** — sort order conflicts
4. **Multi-step workflow atomicity** — CRM automation creating duplicates or partial state
---
## Critical Pitfalls (Require Design Changes Before Code)
### Pitfall 1: Catalog Consolidation Breaks Quotes (Data Loss Risk)
**Severity:** CRITICAL — Can orphan quote_items, break billing calculations
**Problem:**
Two parallel service tables (`service_catalog` for costs, `offer_services` for marketing pricing) need consolidation. Naive merge breaks `quote_items` referential integrity or loses price history.
**Prevention Strategy:**
- **Expand-Contract migration:** Add new unified `services` table alongside old ones, backfill in phases, only drop old tables after validation
- **Immutable snapshot:** Store `quote_snapshot: JSONB` in project_offers (freeze prices at time of win)
- **Referential integrity test:** Query for orphaned quote_items before/after migration
- **Backward compatibility:** Old quotes read from old tables, new quotes use new table, transition over 2-3 weeks
**Phase:** 7 (Catalog & Offers) — Plan migration design BEFORE any code changes
**Validation checklist:**
- [ ] Migration mapping layer documented (catalog ↔ services ↔ offer_services)
- [ ] Backfill strategy written (small batches, checksum validation)
- [ ] Dual-write period defined (how long to run old+new in parallel)
- [ ] Referential integrity test query exists
---
### Pitfall 2: Offer Template Mutation on Copy (Data Corruption Risk)
**Severity:** CRITICAL — Templates silently mutate when project phases edited
**Problem:**
Copying offer phases to project via shallow copy = shared references. Admin edits project phase, template phase mutates too. Next deal uses corrupted template. Also: partial copies on retry create duplicate phases.
**Prevention Strategy:**
- **Deep copy with atomic transaction:** Use `db.transaction()` for entire copy operation (all-or-nothing); copy at database level, not in JS
- **Immutable template flag:** `offer_micros.is_template = true`, prevent UPDATE on templates
- **Idempotency key:** "Win" request includes idempotency key; retry returns same project (no duplicates)
- **Explicit hierarchy mapping:** Document what offer_micro → project_phase, what offer_service → project_task/deliverable
**Phase:** 7 (Offers) for design, 9 (CRM) for "Win" automation
**Validation checklist:**
- [ ] Hierarchy mapping documented (offer ↔ project structures)
- [ ] Atomic copy function implemented (single transaction, all-or-nothing)
- [ ] Idempotency key added to CRM leads
- [ ] Integration tests verify copy structure consistency across multiple instances
---
### Pitfall 3: Public Quote Token Leakage (Security Risk)
**Severity:** CRITICAL — Token enumeration exposes all pricing
**Problem:**
Public quote page with nanoid 21-char token can be brute-forced or enumerated. Attacker builds pricing database, breaches commercial confidentiality.
**Prevention Strategy:**
- **Longer token:** nanoid(32) instead of 21 (~190 bits vs ~122 bits entropy)
- **Access control:** Require both token + email (validate recipient match)
- **Expiration:** token_expires_at (default 7 days)
- **Rate limiting:** Max 3 views/token/minute (blocks enumeration)
- **Never expose quote_items:** Public API response excludes line items, shows TOTAL PRICE ONLY
- **Activation state:** Token valid only AFTER admin sends it (not auto-generated on create)
**Phase:** 8 (Public Quote Pages) — Implement security controls IN PARALLEL with feature
**Validation checklist:**
- [ ] Token length ≥ 32 chars, rate limiting ≥ 3/min
- [ ] Email validation on public page (token + email both required)
- [ ] Expiration enforced (test expired token returns 401)
- [ ] Code audit: quote_items never in public API response
- [ ] Brute-force test: verify rate limit activates
---
## Moderate Pitfalls (Major Refactoring / Data Inconsistency)
### Pitfall 4: Drag-and-Drop Sort Order Race Condition
**Severity:** MODERATE — Phases display in wrong order, user frustration
**Problem:**
Concurrent drag-and-drop edits in multiple tabs cause sort_order conflicts. Last write wins, earlier update is lost. No version conflict detection.
**Prevention Strategy:**
- **Optimistic locking:** Add `version` field to offer_phases, update only if version matches (return 409 Conflict if mismatch)
- **Server-side recomputation:** Don't trust client's sort_order, recompute all orders atomically based on actual position
- **Optimistic UI with reconciliation:** Update frontend instantly, reconcile with server result (409 = refresh)
**Phase:** 7 (Offer Builder — Drag & Drop)
**Validation checklist:**
- [ ] `version` field added to offer_phases
- [ ] Update mutation includes version check
- [ ] Server recomputes all sort_orders atomically
- [ ] Concurrent edit test exists (open 2 tabs, drag in both)
---
### Pitfall 5: CRM "Win" Double-Click Creates Duplicate Clients
**Severity:** MODERATE — Duplicate clients, broken billing, audit confusion
**Problem:**
Multi-step "Win" automation (create client → project → phases → payments) has no idempotency. Double-click creates two clients, two projects. Network timeouts are invisible to user.
**Prevention Strategy:**
- **Idempotency key:** Every lead has unique idempotency_key; "Win" request checks if key already processed (if yes, return existing client_id)
- **Atomic transaction:** All steps (client, project, phases, payments) in single transaction — all succeed or all rollback
- **UI feedback:** Button disabled until success (prevents accidental double-click)
- **Browser persistence:** Store idempotency_key in localStorage, preserve across refresh
**Phase:** 9 (CRM Won Automation)
**Validation checklist:**
- [ ] CRM leads table has idempotency_key column (unique)
- [ ] "Win" mutation checks for existing key before creating
- [ ] All substeps in single db.transaction()
- [ ] Button disabled until response received
- [ ] Idempotency key stored in localStorage
- [ ] Test: double-click "Win", verify only 1 client created
---
### Pitfall 6: Offer Phase Copy Misses Tasks/Deliverables
**Severity:** MODERATE — Client dashboard shows incomplete phases (no tasks to approve)
**Problem:**
Copy offer → project phases but forget to copy tasks or deliverables. Client sees phase with no work items.
**Prevention Strategy:**
- **Explicit mapping:** Document offer_micro → project_phase, offer_service → project_task/deliverable mapping
- **Full-tree copy function:** Copy phases, then tasks for each phase, then deliverables for each task (all in one transaction)
- **Validation test:** Assert phase_count, task_count, deliverable_count match expected values after copy
**Phase:** 7 (Offers structure design), 9 (Copy implementation)
**Validation checklist:**
- [ ] Mapping documented (offer vs project hierarchies)
- [ ] Copy function handles entire tree (phases → tasks → deliverables)
- [ ] Integration test: copy offer, verify counts match expected
- [ ] Query test: check no deliverables are orphaned (task_id IS NULL)
---
## Minor Pitfalls (Operational Friction)
### Pitfall 7: Offer State Not Synced Between Tabs
**Problem:** Admin edits offer in tab 1, tab 2 doesn't know, last save wins (first admin's work lost)
**Prevention:** Add `version` field, return 409 on version mismatch, show "Offer changed, reload?"
---
### Pitfall 8: CRM Schema Backward Incompatibility
**Problem:** Add new fields (budget, source) as NULL, code assumes populated, crashes
**Prevention:** Expand-Contract pattern — add as NULL, handle NULL defensively in code, backfill, then add NOT NULL constraint
---
### Pitfall 9: CRM Scope Creep (Feature Bloat)
**Problem:** "Just add email templates" → 30 hours. "Add calls" → 40 hours. CRM never ships.
**Prevention:** Explicit Must/Should/Could/Won't scope document. Must-haves only for v2.0: lead pipeline, quote attachment, auto-onboarding. Defer rest.
---
## Key Design Decisions for Phase 79
| Decision | Why | Validation |
|----------|-----|-----------|
| Expand-Contract migration for catalog | Zero-downtime, safe rollback, no data loss | Dry-run on prod backup before go-live |
| Deep copy + atomic transaction for offer phases | Prevents template mutation, partial copies | Integration test, structure consistency query |
| Idempotency key on "Win" action | Safe retry, prevents duplicate clients | Double-click test, query for duplicates |
| Public quote token security layers (length, expiration, email, rate limit) | Blocks enumeration, limits leakage | Brute-force test, code audit for quote_items |
| Offer snapshot (immutable JSONB) | Preserves what was promised vs. what's executing | Store at win time, display in client dashboard |
| Version field on offer_phases + optimistic locking | Detects concurrent edit conflicts | Concurrent drag test, 409 Conflict handling |
---
## Roadmap Implications
**Phase 7 (Catalog & Offers):**
- Design catalog consolidation migration before any code
- Implement drag-drop with version fields from start
- Document offer/project hierarchy mapping explicitly
- Write copy function atomically (no partial copies)
**Phase 8 (Public Quote Pages):**
- Implement token security controls in parallel (not after)
- Rate limiting + email validation + expiration
- Code audit: quote_items never in response
- Test brute-force + enumeration
**Phase 9 (CRM — Won Automation):**
- Add idempotency_key to leads table
- Implement atomic "Win" transaction
- Store offer snapshot at win time
- Define Must/Should/Won't scope before design
- Integration tests for payment consistency
**Phase 10+ (Future):**
- Defer: email templates, call logging, team features, integrations
- Solo consultant doesn't need multi-user or GoHighLevel-scale features
---
## Testing Strategy for Confidence
### Phase 7 (Catalog & Offers)
1. Dry-run consolidation migration on production database backup
2. Verify all quote_items still resolve to services (no orphans)
3. Regenerate quotes from old projects, compare prices to originals
4. Concurrent drag-drop test: open offer in 2 tabs, drag in both, verify final state
5. Copy offer phases to test project, verify phase/task/deliverable counts
### Phase 8 (Public Quote Pages)
1. Brute-force token space: 1000 guesses/sec, verify rate limit activates
2. Enumerate tokens: generate 100 quotes, try to access one meant for different email, verify 401
3. Expiration test: set token_expires_at to past, verify access fails
4. Code audit: search response JSON for price, quote_items, per_service_price (should be zero matches)
5. Authorized access test: correct email + token, verify success
### Phase 9 (CRM — Won Automation)
1. Double-click "Win" button, verify only 1 client created
2. Network failure during "Win": simulate timeout after client creation, retry, verify same project returned
3. Partial failure: mock project creation failure, verify client not created (rollback)
4. Payment consistency: verify 24 payments created based on payment plan
5. Idempotency: call "Win" 10x with same idempotency_key, verify only 1 project, 1 set of payments
---
## Confidence Assessment
| Area | Level | Reason |
|------|-------|--------|
| Catalog consolidation risks | HIGH | Schema review shows existing quote_items dependencies; migration pattern verified via Drizzle docs |
| Offer copy semantics | HIGH | JavaScript shallow copy / deep copy distinction well-understood; transaction guarantees verified |
| Token security | HIGH | Brute-force math straightforward; nanoid 32 vs 21 entropy difference verified |
| CRM idempotency | HIGH | Idempotency pattern researched across multiple sources; double-click problem well-documented |
| Scope creep prevention | MEDIUM | CRM feature bloat is common, but solo consultant constraint makes Must/Should/Won't achievable |
| Concurrent edit handling | MEDIUM | Optimistic locking pattern standard, but requires careful implementation in Next.js + Drizzle |
---
## Next Steps for Phase Planning
1. **Before Phase 7 design starts:**
- Finalize catalog consolidation migration plan (Expand-Contract timeline)
- Document offer/project hierarchy mapping (1 page, code-level documentation)
- Design offer copy function signature and transaction strategy
2. **Before Phase 7 code starts:**
- Implement drag-drop versioning (version field + optimistic locking)
- Write integration tests for offer phase copy
3. **Before Phase 8 design starts:**
- Token security requirements: length (32), expiration (7d), email validation, rate limit (3/min)
- API response schema: exclude quote_items, include total_price only
4. **Before Phase 9 design starts:**
- Finalize Must/Should/Won't scope (document with user/consultant)
- Design "Win" workflow: idempotency key + atomic transaction
- Define payment plan algorithm (24 payments based on offer tier)
---
## References for Implementation
- **Drizzle migrations:** https://dev.to/whoffagents/zero-downtime-postgres-migrations-with-drizzle-orm-22ga
- **Idempotency pattern:** https://codefarm0.medium.com/the-double-click-problem-how-idempotency-saved-our-checkout-system-a704be65d207
- **Optimistic updates:** https://www.nirtamir.com/articles/optimistic-updates-state-vs-render/
- **Schema evolution:** https://www.dataexpert.io/blog/backward-compatibility-schema-evolution-guide
- **Token security:** https://workos.com/blog/oauth-common-attacks-and-how-to-prevent-them/
---
**Document location:** `/Users/simonecavalli/Vault/IAMCAVALLI/.planning/research/PITFALLS_V2.md` (detailed pitfall reference)
File diff suppressed because it is too large Load Diff
-126
View File
@@ -1,126 +0,0 @@
# Project Research Summary
**Project:** ClientHub — welcomeclient.iamcavalli.net
**Domain:** Freelancer client portal (secret-link access, solo consultant)
**Researched:** 2026-05-09
**Confidence:** HIGH
---
## Executive Summary
ClientHub è un portale web a due ruoli per un consulente di personal branding. I clienti accedono via UUID segreto casuale — nessun account, nessun login, zero attrito. L'admin gestisce tutto: crea clienti, fasi, task, deliverable, pagamenti e preventivi. Il consensus della ricerca è chiaro: costruisci prima la dashboard cliente, poi l'admin CRUD, poi catalogo servizi e preventivi, poi il flusso Claude AI (v2).
Stack confermato: **Next.js 15 + Neon (Postgres) + Drizzle ORM + Auth.js + nanoid + Tailwind v4 + shadcn/ui**. Ogni scelta è ottimizzata per un developer solo su Vercel: nessun backend da mantenere, nessun costo di connection pooling, nessuna infrastruttura di upload file, nessuna libreria di auth per i clienti. Il meccanismo "secret link" è un Next.js Middleware edge check — veloce, zero client JS, 404 se il token non esiste.
I rischi dominanti sono architetturali, non tecnici. Se il token è la primary key diventa non-rotazionabile. Se la client API restituisce `quote_items` (anche nascosti nell'UI), un cliente con DevTools vede i prezzi dei singoli servizi. Se il progetto parte dal flusso Claude prima che un cliente possa aprire la sua dashboard, il portale non esce. Tutti e tre prevenibili con le decisioni corrette sul data model dal giorno uno.
---
## Key Findings
### Stack Raccomandato
| Tecnologia | Ruolo | Perché |
|------------|-------|--------|
| Next.js 15 (App Router) | Framework full-stack | Server Actions sostituiscono un'API REST separata; nativo Vercel |
| Neon (serverless Postgres) | Database principale | Free tier (0.5 GB, 100 CU-h/mese) sufficiente per 520 clienti; scala a zero |
| Drizzle ORM + neon-http | Accesso DB + migrazioni | Nessun costo di connection pooling; schema-as-code; inferenza TypeScript end-to-end |
| Auth.js v4 (Credentials) | Sessione admin | Account singolo, cookie JWT firmato, nessuna tabella utenti in DB |
| nanoid | Generazione token | 21 char, ~126 bit di entropia, URL-safe, crittograficamente sicuro |
| Tailwind v4 + shadcn/ui | UI | Componenti copiati nel codebase, accessibilità Radix UI, zero runtime dep |
| Zod + React Hook Form | Validazione e form | Schema unico; RHF solo per form admin complessi |
File upload deliberatamente esclusi dalla v1. I link ai documenti sono campi testo che puntano a Google Drive.
### Features v1
**Table stakes (obbligatori):**
- Panoramica progetto (nome, brand, brief, fase corrente)
- Visibilità fasi e task con stato (todo / in corso / fatto)
- Approvazione deliverable con timestamp immutabile
- Commenti inline su task e deliverable (lista piatta, no threading)
- Link a documenti esterni (solo URL, no file hosting)
- Stato pagamenti: acconto 50% + saldo 50% (da saldare / inviata / saldato)
- Totale preventivo accettato visibile al cliente (cifra unica, mai dettaglio)
- Layout mobile-ready
- Link segreto persistente e non-scadente
**Differenziatori (low-effort, includibili in v1):**
- Log decisioni / storico (nota append-only)
- Indicatore di avanzamento fase (derivato da % task completati)
- Timestamp "ultimo aggiornamento" sulla dashboard
- Vista admin: tutti i clienti con badge stato pagamenti
- Reset link segreto (single UPDATE, solo admin)
**Anti-features (mai costruire):** login cliente, PDF fatture in-app, multi-admin, struttura progetto modificabile dal cliente, prezzi singoli visibili al cliente, kanban board.
### Architettura
Singola applicazione Next.js su Vercel, un database Neon Postgres. Nessun backend separato.
**Due path di accesso isolati:**
- `/c/[token]/*` → Middleware valida il token contro Neon, 404 se mancante
- `/admin/*` → Auth.js session check, singolo account admin
**Decisioni chiave del data model:**
- `clients.token` è un campo separato e rotazionabile — **non** la primary key
- `clients.accepted_total` denormalizzato: la client API non tocca mai `quote_items`
- `deliverables.approved_at` come audit trail immutabile dal giorno uno
- `payments` sempre due righe per cliente (acconto + saldo), create alla finalizzazione del preventivo
- `ClientView` e `AdminView` sono tipi distinti lato server — privacy enforce a livello di query, non di UI
### Pitfall Critici
1. **Token = primary key (non rotazionabile)** — Usa UUID stabile come PK e campo `token` separato e aggiornabile. Deve essere nella schema della Fase 1; non si può correggere dopo che i link sono stati distribuiti.
2. **Client API espone `quote_items` (nascosti solo nell'UI)** — Definisci `ClientView` come tipo server-side che non interroga mai `quote_items`. Un cliente tecnico con DevTools non deve mai vedere i prezzi singoli.
3. **Over-engineering prima che un cliente usi il portale** — Criterio di successo duro per la Fase 1: un link cliente reale è condivisibile e funziona. Non iniziare il flusso Claude prima che l'admin possa creare un cliente e il cliente possa aprire la sua dashboard.
4. **Nessun record di approvazione immutabile** — Salva `approved_at` (timestamp, non solo boolean) dallo schema iniziale.
5. **Area admin senza vera autenticazione** — Il check Middleware su `ADMIN_PASSWORD` env var deve essere in place prima che la Fase 2 vada in produzione.
---
## Implicazioni per la Roadmap
### Struttura suggerita: 4 fasi
**Fase 1 — Foundation: DB schema, token API, dashboard cliente**
Consegna: un link cliente reale condivisibile che mostra il progetto su mobile e desktop.
Copre: panoramica, fasi/task, pagamenti, documenti, link segreto, DNS.
**Fase 2 — Admin CRUD + auth + commenti + approvazioni**
Consegna: admin crea/modifica clienti, fasi, task, deliverable, pagamenti. Cliente commenta e approva. Admin può rigenerare il link.
Copre: auth Middleware, CRUD completo, flow approvazione con timestamp, commenti lista piatta.
**Fase 3 — Catalogo servizi + preventivi**
Consegna: admin costruisce catalogo riutilizzabile e compone preventivi da esso. `accepted_total` scritto sulla riga cliente.
Nessuna dipendenza client-facing oltre `accepted_total` (già in schema dalla Fase 1).
**Fase 4 (v2) — Flusso Claude AI per onboarding**
Dipende da CRUD stabile + catalogo completo. Claude legge il brief e suggerisce fasi + preventivo.
*Richiede ricerca dedicata durante la pianificazione.*
### Flag di ricerca
- Fasi 13: pattern standard, nessuna ricerca aggiuntiva necessaria
- Fase 4: richiede ricerca su Claude API structured output, streaming vs batch, prompt engineering per generazione fasi
---
## Confidence Assessment
| Area | Confidence | Note |
|------|------------|------|
| Stack | HIGH | Tutte le tecnologie stabili e in produzione |
| Features | HIGH | Feature set opinionated e ben delimitato |
| Architettura | HIGH | Data model completo, pattern two-path auth provato |
| Pitfall | HIGH | Tutti mappabili a decisioni concrete della Fase 1 |
**Domande aperte (da risolvere durante la pianificazione delle fasi):**
- Access log per i link (utile per rilevare accessi non autorizzati)?
- Approvazioni reversibili (admin-only revoke)?
- Log decisioni visibile al cliente dalla v1 o solo admin?
- DNS: configurare e verificare la propagazione nella Fase 1
---
*Ricerca completata: 2026-05-09 | Pronto per la roadmap: sì*