Carrots is a web application for managing conditional commitments within groups. Users can make commitments like "if X does at least Y of Z, I will do at least A of B" and the system calculates resulting liabilities.
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Frontend │────▶│ Backend │────▶│ Database │
│ (React) │◀────│ (Express) │◀────│ (PostgreSQL)│
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌──────────────┐
│ LLM Service │
│ (OpenAI) │
└──────────────┘
- Framework: React 18+ with TypeScript
- State Management: React Context API / Redux
- UI Components: Material-UI or shadcn/ui
- Routing: React Router
- HTTP Client: Axios
- Form Management: React Hook Form
- Runtime: Node.js 18+
- Framework: Express.js
- Language: TypeScript
- Authentication: JWT + bcrypt
- Validation: Zod or Joi
- ORM: Prisma or TypeORM
- API Documentation: OpenAPI/Swagger
- Primary Database: PostgreSQL 14+
- Schema: Normalized relational design
- Migrations: Prisma Migrate or TypeORM migrations
- LLM Integration: OpenAI API (GPT-4) for natural language processing
- Deployment: Docker containers
- id (UUID)
- username (unique)
- email (unique)
- passwordHash
- createdAt
- updatedAt
- id (UUID)
- name
- description
- creatorId (FK → User)
- createdAt
- updatedAt
- id (UUID)
- groupId (FK → Group)
- userId (FK → User)
- joinedAt
- role (creator | member)
- id (UUID)
- groupId (FK → Group)
- creatorId (FK → User)
- status (active | revoked)
- naturalLanguageText (original text)
- parsedCommitment (JSON structure with conditions array and promises array)
- createdAt
- updatedAt
- revokedAt
ParsedCommitment Structure (JSON):
{
"conditions": [
{
"targetUserId": "alice-uuid",
"action": "work",
"minAmount": 5,
"unit": "hours"
},
{
"targetUserId": "bob-uuid",
"action": "review",
"minAmount": 3,
"unit": "tasks"
}
],
"promises": [
{
"action": "work",
"baseAmount": 10,
"proportionalAmount": 0,
"maxAmount": 10,
"unit": "hours"
},
{
"action": "support",
"baseAmount": 0,
"proportionalAmount": 2,
"referenceUserId": "carol-uuid",
"referenceAction": "review",
"thresholdAmount": 5,
"maxAmount": 20,
"unit": "hours"
}
]
}Note: referenceAction (Zi) can differ from action (Yi), and maxAmount (Wi) caps proportional contributions.
- id (UUID)
- groupId (FK → Group)
- userId (FK → User)
- action (string)
- amount (numeric)
- unit (string)
- calculatedAt
- effectiveCommitmentIds (JSON array of commitment IDs)
POST /api/auth/register- Register new userPOST /api/auth/login- Login userPOST /api/auth/logout- Logout userGET /api/auth/me- Get current user
GET /api/users/:id- Get user by IDGET /api/users- List users (with search/filter)
POST /api/groups- Create new groupGET /api/groups- List user's groupsGET /api/groups/:id- Get group detailsPUT /api/groups/:id- Update groupDELETE /api/groups/:id- Delete groupPOST /api/groups/:id/join- Join groupPOST /api/groups/:id/leave- Leave groupGET /api/groups/:id/members- List group members
POST /api/commitments- Create new commitment (with NLP processing)GET /api/commitments- List commitments (filter by group, user, status)GET /api/commitments/:id- Get commitment detailsPUT /api/commitments/:id- Update commitmentDELETE /api/commitments/:id- Revoke commitmentPOST /api/commitments/parse- Parse natural language to structured commitment
GET /api/groups/:id/liabilities- Calculate and get current liabilities for groupGET /api/users/:id/liabilities- Get user's liabilities across all groups
Commitments support conjunctive conditions and affine linear promises:
interface ParsedCommitment {
conditions: CommitmentCondition[]; // Conjunction (AND)
promises: CommitmentPromise[]; // Multiple promises
}
interface CommitmentCondition {
targetUserId: string; // User Ai who must perform action
action: string; // Action Xi
minAmount: number; // Minimum amount Vi
unit: string;
}
interface CommitmentPromise {
action: string; // Action Yi to be performed
baseAmount: number; // W0 (constant term)
proportionalAmount: number; // Di (coefficient)
referenceUserId?: string; // Bi (reference user for proportional)
referenceAction?: string; // Zi (action to monitor - can differ from Yi)
thresholdAmount?: number; // Oi (threshold for "excess")
maxAmount?: number; // Wi (maximum cap to keep liabilities finite)
unit: string;
}Example: "If (Alice does ≥5 work) AND (Bob does ≥3 review), then I will do (≥10 work) AND (≥2 support per unit Carol does of review beyond 5, up to 20 support total)"
Based on the game-theoretic framework, the liability calculation finds the largest fixed point:
- Initialization: Set all liabilities to maximum values from promises (using maxAmount caps)
- Iterative Reduction: For each user-action pair (i, a):
- Compute L_i(a) = max { c_i(a, C_j) | all conditions of C_j are satisfied }
- Where c_i(a, C_j) includes base + capped proportional contributions:
c_i(a, C_j) = W0 + Σ_k min(Wk, Dk × max(0, L_Bk(Zk) - Ok))
- Convergence: Repeat until no liability changes (largest fixed point reached)
Key Properties:
- Conjunctive conditions: ALL conditions must be satisfied (AND logic)
- Affine linear promises with caps: Base amount + proportional terms (capped at maxAmount)
- Different action variables: Can monitor action Zi while promising action Yi
- Largest fixed point: Start at maximum caps, iterate down to stable equilibrium
- Monotonic: Liabilities never increase during iteration
- Finite: maxAmount caps ensure liabilities remain bounded
L_i(a) = max { c_i(a, C_j) | C_j created by i, all conditions of C_j satisfied }
Condition satisfaction:
∀k: L_Ak(Xk) ≥ Vk (all target users meet their thresholds)
Promise value (affine linear with cap):
c_i(Yi, C_j) = W0 + Σ_k min(Wk, Dk × max(0, L_Bk(Zk) - Ok))
- User enters natural language commitment
- Backend sends to LLM with structured prompt
- LLM extracts:
- Array of conditions (conjunctive): each with targetUserId, action, minAmount, unit
- Array of promises: each with action, baseAmount, proportionalAmount, referenceUserId (if proportional), referenceAction, thresholdAmount, unit
- If ambiguous, LLM requests clarification
- Backend validates and stores parsed commitment
Parse the following commitment into structured form:
"{user_input}"
Group members: {member_list}
Extract commitment of the form:
"If ((A1 does at least V1 of X1) AND ... AND (Ak does at least Vk of Xk))
then I will do (at least W0 of Y0) AND (at least D1 of Y1 for every unit that B1 does of Z1 in excess of O1, up to W1 of Y1 total) AND ..."
Output as JSON:
{
"conditions": [
{"targetUserId": "...", "action": "...", "minAmount": number, "unit": "..."}
],
"promises": [
{"action": "Yi", "baseAmount": number, "proportionalAmount": number,
"referenceUserId": "Bi", "referenceAction": "Zi", "thresholdAmount": number, "maxAmount": number, "unit": "..."}
]
}
Notes:
- Conditions are conjunctive (all must be satisfied)
- Promises can be constant (baseAmount > 0, proportionalAmount = 0) or proportional
- For proportional: proportionalAmount = coefficient Di, referenceUserId = Bi, referenceAction = Zi (can differ from Yi), thresholdAmount = Oi, maxAmount = Wi (cap)
- maxAmount keeps liabilities finite and provides clear starting values
If unclear, ask for clarification.
- Landing Page: App introduction, login/register
- Dashboard: User's groups and commitments overview
- Group View: Group details, members, commitments, liabilities
- Commitment Creation: Natural language input with preview
- Commitment List: View/edit/revoke commitments
- Liability View: Current liabilities visualization
GroupCard: Display group summaryCommitmentCard: Display commitment detailsCommitmentForm: Natural language input with AI assistanceLiabilityChart: Visualize liabilitiesMemberList: Show group membersCommitmentList: Filterable list of commitments
- Authentication: JWT tokens with secure httpOnly cookies
- Authorization: Role-based access control (RBAC)
- Input Validation: Zod schemas for all inputs
- SQL Injection: Use ORM parameterized queries
- XSS Prevention: React's built-in escaping + CSP headers
- Rate Limiting: Limit API requests per user
- LLM Safety: Validate LLM outputs before storing
# Backend
cd backend
npm install
npm run dev
# Frontend
cd frontend
npm install
npm start- Docker containers for backend and frontend
- PostgreSQL managed database
- Environment variables for configuration
- HTTPS with SSL certificates
- CDN for static assets
# Backend
DATABASE_URL=postgresql://...
JWT_SECRET=...
OPENAI_API_KEY=...
PORT=3001
NODE_ENV=production
# Frontend
REACT_APP_API_URL=https://api.carrots.app
- Unit tests: Individual functions (commitment parser, liability calculator)
- Integration tests: API endpoints
- E2E tests: Full user flows
- Component tests: React Testing Library
- Integration tests: User interactions
- E2E tests: Cypress
- Real-time Updates: WebSocket for live liability updates
- Notifications: Email/push notifications for new commitments
- Analytics: Dashboard for commitment statistics
- Export: Export commitments and liabilities to CSV/PDF
- Mobile App: React Native mobile application
- Multi-language: i18n support
- Advanced Conditions: Support for more complex conditional logic
- Commitment Templates: Pre-defined commitment templates
- Group Settings: Configurable group rules and permissions
- Audit Log: Track all changes to commitments