Skip to content

Repository files navigation

Candor

Feedback that builds better teams.

Replace numerical surveys with natural language. People write freely about their work; Claude reads, scores, and asks the follow-up questions you would have asked.

Live demo · Report a bug


Why

Five-point scales tell you that something is wrong. They don't tell you what. Candor flips that: respondents write a paragraph, and an LLM extracts the metrics you defined — happiness, engagement, "would you recommend" — with a confidence score and a one-line rationale. If the response is thin, Claude asks one or two clarifying questions before submitting.

The result is structured data you can chart, plus the actual words your team used.

How it works

A walkthrough of the four moments that matter — from the first visit to the insight that changes a decision.

1 · Land

The pitch in one screen: feedback that builds better teams, an explicit three-step model (Create → Collect → Analyze), and the features that make it work — natural language, smart follow-ups, real-time dashboard, AI insights, exports.

Candor landing page

2 · Respond

Respondents see a single textarea with a contextual opener (e.g. "Q1 Team Pulse Check"). No 1–5 scales, no required fields, no "select one" — just a placeholder inviting them to write about what's going well and what could be better. Behind the scenes, Claude reads the paragraph, scores each metric the creator defined, and decides whether to ask one or two clarifying questions before submitting.

Respond page — single textarea, no rating scales

3 · Measure

Once responses come in, the creator opens the survey and sees the metrics they defined — extracted, aggregated, and tied back to the underlying responses. In this run of Q1 Team Pulse Check with 11 responses: average Happiness 3.8 / 5 at 89% confidence, and 82% would recommend the company to a friend (9 of 11). One click ships a public share link, a PDF, or a CSV.

Per-survey dashboard with metric scores and share / PDF / CSV exports

4 · Understand

Numbers tell you what; AI Insights tells you why. The same survey detail page surfaces a one-paragraph executive summary, key themes pulled from the corpus, and a strengths-vs-concerns split — "compelling mission and meaningful work driving motivation" on one side, "tight deadlines leading to overtime, stress, and coordination difficulties" on the other. This is the panel a manager screenshots into the next leadership meeting.

AI Insights panel — summary, key themes, strengths and concerns

Features

  • Natural-language responses — no Likert scales shown to the user
  • AI-extracted metrics — Claude returns score, confidence, reasoning per metric defined by the creator
  • Adaptive follow-ups — up to N (default 2) clarifying questions per respondent, generated in-context
  • Two access modes — invite-only (email allowlist) or public link
  • Encrypted at rest — response payloads pass through src/lib/crypto.ts before storage
  • Real-time dashboard — survey list, response counts, per-survey detail with charts (bar / pie / radar via recharts)
  • AI-generated insights — themes and recommendations on top of aggregated metrics
  • PDF exportjspdf + html2canvas to ship a report
  • Light + dark themenext-themes

Tech stack

Layer Choice
Framework Next.js 16 (App Router) + React 19 + TypeScript
Styling Tailwind CSS 4 + shadcn/ui-style components + Framer Motion
Auth + DB Supabase (Postgres, RLS, magic-link + Google OAuth, SSR via @supabase/ssr)
AI Anthropic Claude (@anthropic-ai/sdk), Vercel AI SDK
Charts Recharts
Hosting Vercel

Architecture

Creator                                Respondent
   │                                       │
   ▼                                       ▼
/create  ── defines metrics ──►  surveys ──┐
                                           │
                            (invite/link)  │
                                           ▼
                                        /respond
                                           │
                                           ▼
                              POST /api/analyze ──► Claude
                                           │
                       extracted metrics ◄─┘
                                           │
                                  follow-up needed?
                                       │     │
                                      yes    no
                                       │     │
                                       ▼     ▼
                                  ask again  store in Supabase
                                                   │
                                                   ▼
                                              /dashboard ──► insights, charts, PDF

Key types (src/types)

  • SurveyMetric — what to measure: name, description, type (rating | boolean), scale_min, scale_max
  • ExtractedMetric — Claude's output: score, confidence, reasoning
  • FollowUpQuestion — question/answer pair for clarification

Project structure

src/
├── app/
│   ├── api/
│   │   ├── analyze/                 # Claude extraction endpoint
│   │   ├── generate-question/       # Contextual opener + follow-ups
│   │   ├── insights/                # Aggregate themes / recommendations
│   │   └── surveys/[id]/            # check-response, invite, participants, submit, review
│   ├── auth/                        # Magic-link callback + login page
│   ├── create/                      # Creator flow + success
│   ├── dashboard/                   # Creator's survey list + per-survey detail
│   ├── my-surveys/                  # Surveys a respondent has been invited to
│   ├── respond/                     # Public response page
│   └── survey/[id]/                 # Tokenised survey link target
├── components/
│   ├── charts/                      # bar / pie / radar wrappers
│   └── ui/                          # button, card, input, textarea, skeleton, …
├── lib/
│   ├── anthropic.ts                 # Claude client
│   ├── crypto.ts                    # Response payload encryption
│   └── supabase/                    # browser, server, admin, middleware clients
├── middleware.ts                    # Route guarding via Supabase SSR
└── types/
    ├── database.ts                  # Generated Supabase types
    └── index.ts                     # Domain types
supabase/
└── migrations/                      # SQL migrations (orgs, anon insights, etc.)

Getting started

1. Clone with submodules

git clone --recursive https://github.com/ronakv/candor.git
cd candor
# or, if already cloned:
git submodule update --init --recursive

The .claude/skills/gstack submodule provides browse / review / ship / retro slash commands when working in Claude Code.

2. Install

npm install

3. Configure environment

Copy .env.example.env.local and fill in:

NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
SUPABASE_SERVICE_ROLE_KEY=<service-role-key>   # server-only
ANTHROPIC_API_KEY=<sk-ant-…>                   # server-only
ANON_HASH_SALT=<32+ random chars, never rotate in prod>

4. Apply Supabase schema

# from a Supabase project with the CLI installed
supabase db push
# or paste files in supabase/migrations/ via the SQL editor

5. Run

npm run dev      # http://localhost:3000
npm run build    # production build
npm run start    # production server
npm run lint     # ESLint
npm test         # Jest (covers crypto.ts at minimum)

Deployment

The app is deployed to Vercel at https://candor-smoky.vercel.app. Because NEXT_PUBLIC_* vars are baked into the client bundle at build time, a Vercel rebuild is required any time those variables change (e.g. after restoring a paused Supabase project to a new URL).

License

Private — © 2024 Candor Inc.

About

AI-powered workforce feedback platform — natural language responses analyzed by Claude

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages