Skip to content

Repository Structure

Purpose

This document defines the monorepo strategy for Farmerspec projects. This structure is designed to be operated by AI agents, where each agent has a clear scope and minimal need to navigate outside their designated areas.


AI-First Design Philosophy

This repository structure is optimized for AI agent operation. The key principles:

  1. Clear boundaries - Each agent knows exactly which folders they own
  2. Minimal context switching - Agents don't jump between unrelated folders
  3. Self-contained work areas - Everything an agent needs is co-located
  4. Predictable patterns - Same structure repeated at each level
  5. Progressive disclosure - Documentation at every level for agent context
  6. Language isolation - TypeScript stays in apps/, Python stays in services/

Why This Matters for AI Agents

AI agents work best when they have: - Focused scope - Less context to load, fewer distractions - Clear ownership - No ambiguity about what to modify - Co-located files - Tests next to code, docs next to implementation - Consistent patterns - Learn once, apply everywhere - Language purity - No mixing Python and TypeScript in the same tree


The Farmerspec Agent System

Farmerspec orchestrates 10 AI agents that work sequentially on features. Each agent has specific folders they read and write.

Agent Overview

Feature Request
      │
      ▼
┌─────────────────┐
│  Baron          │  Classify, route, create feature structure
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Veuve          │  Answer product questions (if needed)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Duc            │  Architecture decisions, technical specs
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Marie          │  Write ALL tests BEFORE implementation
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Dede           │  Backend implementation (make tests pass)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Dali           │  Frontend implementation (make tests pass)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Gus            │  GitOps/Infrastructure (make tests pass)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Victor         │  Documentation review (flag issues)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  General        │  Code review, create PR
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Socrate        │  Retrospective, improve agents
└─────────────────┘

Agent Folder Ownership

Agent Role Primary Folders (Read/Write) Reference Folders (Read Only)
Baron Classifier & Router specs/, .farmerspec/ All (to understand scope)
Veuve Product Expert specs/*/product/ docs/
Duc Architect specs/*/architecture/, docs/architecture/ All (to understand system)
Marie Test Writer services/**/tests/, apps/**/tests/, tests/ specs/*/architecture/
Dede Backend Dev services/ (entire Python world) specs/, tests (to make pass)
Dali Frontend Dev apps/ (entire TypeScript world) specs/, tests (to make pass)
Gus GitOps Engineer infra/, platform/ specs/, tests (to make pass)
Victor Librarian docs/ (flags issues only) All docs/ and README.md files
General Code Reviewer .github/ All (to review)
Socrate Retrospective .farmerspec/retros/ All (to analyze)

Agent Deep Dive

Baron - The Classifier

Role: Receives feature requests, classifies them, creates the feature structure.

Writes to: - specs/<issue-number>/ - Creates feature folder - .farmerspec/state.yaml - Tracks workflow state

Why this structure helps Baron: - specs/ is a single, predictable location for all features - Numbered folders prevent conflicts - State file provides clear workflow tracking


Veuve - The Product Expert

Role: Answers product questions, clarifies requirements, defines acceptance criteria.

Writes to: - specs/<issue>/product/ - Product decisions, user stories

Reads from: - docs/ - Existing product documentation - specs/<issue>/ - Current feature context

Why this structure helps Veuve: - Product knowledge is centralized in docs/ - Feature-specific decisions stay in specs/<issue>/product/ - Clear separation from technical specs


Duc - The Architect

Role: Makes architecture decisions, writes technical specifications, updates global documentation.

Writes to: - specs/<issue>/architecture/ - Feature-specific architecture - docs/architecture/ - Global architecture updates - docs/services/<domain>/ - Domain documentation

Reads from: - All folders (to understand the full system)

Why this structure helps Duc: - docs/architecture/ is the single source of truth for system design - specs/<issue>/architecture/ keeps feature decisions contained - Domain-grouped services make boundaries clear


Marie - The Test Writer

Role: Writes ALL tests BEFORE any implementation. This is true TDD - tests define the contract.

Writes to: - services/<domain>/<service>/tests/unit/ - Backend unit tests - services/<domain>/<service>/tests/integration/ - Backend integration tests - apps/<app>/tests/unit/ - Frontend unit tests - apps/<app>/tests/component/ - Frontend component tests - tests/e2e/ - End-to-end tests - tests/contract/ - Contract tests - tests/journey/ - User journey tests - tests/infra/ - Infrastructure tests

Reads from: - specs/<issue>/architecture/ - What to test - docs/ - Existing patterns

Why this structure helps Marie: - Tests are co-located with the code they test (for unit/integration) - Cross-cutting tests have their own dedicated folder (tests/) - Clear markers (@pytest.mark.unit, etc.) for test categorization - Marie never touches src/ - only tests/

Critical rule: Marie writes tests, implementation agents make them pass. Marie CANNOT modify tests after handoff.


Dede - The Backend Developer

Role: Implements backend code to make Marie's tests pass.

Writes to: - services/<domain>/<service>/src/ - Backend implementation - services/<domain>/<service>/docs/ - Backend documentation - services/_packages/ - Shared Python code

Reads from: - services/<domain>/<service>/tests/ - Tests to make pass - specs/<issue>/architecture/ - Technical specs

Cannot modify: - tests/ - Tests are immutable after Marie writes them - apps/ - Frontend is Dali's domain (pure TypeScript) - infra/ - Infrastructure is Gus's domain

Why this structure helps Dede: - Never leaves services/ - Everything Dede needs is there - Tests are right next to the code: src/ and tests/ are siblings - Shared Python code is in services/_packages/, not a separate root folder - Domain grouping prevents Dede from accidentally touching other domains


Dali - The Frontend Developer

Role: Implements frontend code to make Marie's tests pass.

Writes to: - apps/<app>/src/ - Frontend implementation - apps/<app>/docs/ - Frontend documentation - apps/_packages/ - Shared TypeScript code (except generated-types/)

Reads from: - apps/<app>/tests/ - Tests to make pass - specs/<issue>/architecture/ - Technical specs - apps/_packages/generated-types/ - Generated types from Python models (READ ONLY)

Cannot modify: - tests/ - Tests are immutable after Marie writes them - services/ - Backend is Dede's domain (pure Python) - infra/ - Infrastructure is Gus's domain - apps/_packages/generated-types/ - Generated by CI scripts

Why this structure helps Dali: - Never leaves apps/ - Everything Dali needs is there - Tests are right next to the code - UI components are in apps/_packages/ui-library/ - Clear separation from backend concerns - No Python files to accidentally modify


Gus - The GitOps Engineer

Role: Implements infrastructure and deployment to make Marie's infra tests pass.

Writes to: - infra/terraform/modules/ - Terraform modules - infra/terraform/environments/ - Environment configs - infra/k8s/base/ - Base K8s manifests - infra/k8s/overlays/ - Environment overlays - infra/k8s/appsets/ - ArgoCD ApplicationSets - platform/ - Platform service configs and deployment scripts

Reads from: - tests/infra/ - Infra tests to make pass - specs/<issue>/architecture/ - Technical specs - services/ - To understand what needs deploying - apps/ - To understand what needs deploying

Cannot modify: - services/*/src/ - Application code - apps/*/src/ - Application code - Other agents' tests

Special responsibilities:

  1. Type Generation - Gus owns the CI workflow that generates TypeScript types from Python Pydantic models
  2. Infrastructure Hydration - Gus runs scripts/hydrate-infra.sh before any deployment

Why this structure helps Gus: - All infrastructure is in infra/ - Clear separation: terraform/ for cloud, k8s/ for GitOps - platform/ configs are separate from deployment specs - K8s manifests mirror the service structure (infra/k8s/base/<domain>/ maps to services/<domain>/) - Hydration pattern keeps K8s manifests self-contained


Victor - The Librarian

Role: Reviews ALL documentation for coherence, accuracy, and completeness. Flags issues but does NOT fix them.

Reads from: - All README.md files - All docs/ folders at every level - specs/ - Feature specifications

Writes to: - specs/<issue>/docs-review.md - Review report with flagged issues

Cannot modify: - Any documentation directly (only flags issues)

Why this structure helps Victor: - Progressive disclosure means docs exist at every level - Consistent pattern: every folder has README.md + docs/ - Clear ownership: Victor knows who to escalate each issue to - Single review report keeps findings organized

Doc ownership for escalation:

Documentation Owner
docs/architecture/ Duc
services/*/docs/ Dede
apps/*/docs/ Dali
infra/*/docs/ Gus
platform/*/docs/ Gus
tests/docs/ Marie
Root docs/ Duc

General - The Code Reviewer

Role: Reviews all code changes, ensures quality, creates the PR.

Reads from: - All modified files - specs/<issue>/ - Feature context - .farmerspec/state.yaml - Workflow state

Writes to: - .github/ - PR creation - specs/<issue>/review.md - Review notes

Why this structure helps General: - Can see all changes in context - Feature specs provide the "why" - Clear workflow state shows what each agent did


Socrate - The Philosopher

Role: Runs after EVERY issue completes. Analyzes the workflow, identifies improvements, updates agent prompts.

Reads from: - All folders (to analyze what happened) - specs/<issue>/ - The complete feature journey - .farmerspec/ - Agent prompts and templates

Writes to: - .farmerspec/retros/<issue>.md - Retrospective report - .farmerspec/improvements.yaml - Proposed improvements - Agent prompts (with human approval)

Why this structure helps Socrate: - Complete visibility into the entire workflow - Retrospectives are stored for learning - Improvements are tracked and versioned


Folder Architecture

Language Separation: The Core Principle

This monorepo enforces strict language separation:

World Folder Language Tooling Owner
🟢 TypeScript World apps/ TypeScript/React pnpm, Vite Dali
🔵 Python World services/ Python uv, pytest, ruff Dede
🟠 Infrastructure World infra/ HCL, YAML Terraform, Kustomize Gus

Why strict separation?

  1. Simplified tooling - No need to tell Python linters to ignore TypeScript files
  2. Cleaner CI pipelines - "If change is in apps/, run pnpm test"
  3. Agent clarity - Dali never leaves apps/, Dede never leaves services/
  4. No mixed packages - Shared code lives with its language

apps/ - TypeScript World (Frontend)

The apps/ folder is a pure TypeScript/Node.js workspace. It contains all frontend applications AND shared TypeScript packages.

Why subfolders in apps?

Different applications serve different purposes: - PWA - Progressive Web App for mobile-first users - Portal - Web portal for stakeholders/admins - Dashboard - Analytics/reporting interface - Marketing - Public marketing site

Each app is independently deployable with its own build, Dockerfile, and test suite.

apps/                                # 🟢 TYPESCRIPT WORLD (pnpm workspace)
├── README.md
├── docs/                            # Shared frontend patterns
├── package.json                     # Root workspace config
├── pnpm-workspace.yaml
├── turbo.json                       # Turbo monorepo config
├── tsconfig.base.json               # Shared TS config
│
├── _packages/                       # 📦 Shared TypeScript Code
│   ├── ui-library/                  # Shared React components
│   │   ├── package.json
│   │   ├── src/
│   │   └── tests/
│   │
│   ├── ts-utils/                    # Logging, formatting, helpers
│   │   ├── package.json
│   │   ├── src/
│   │   └── tests/
│   │
│   └── generated-types/             # ⚠️ READ-ONLY - Generated from Python
│       ├── package.json
│       └── src/                     # TypeScript interfaces from Pydantic
│
├── farmer-pwa/                      # 📱 Mobile-first PWA
│   ├── README.md
│   ├── docs/
│   ├── src/                         # Dali writes here
│   ├── tests/                       # Marie writes here
│   │   ├── unit/
│   │   └── component/
│   ├── public/
│   ├── Dockerfile
│   ├── package.json
│   └── vite.config.ts
│
└── stakeholder-portal/              # 🏢 Stakeholder web portal
    ├── README.md
    ├── docs/
    ├── src/
    ├── tests/
    ├── Dockerfile
    └── package.json

Why _packages/ prefix? - Sorts to top of directory listing - Visually distinct from apps - Signals "shared, don't deploy directly"

For Dali: Never leave apps/. All your shared UI code, utilities, and types are here in _packages/.


services/ - Python World (Backend)

The services/ folder is a pure Python workspace. It contains all backend microservices AND shared Python packages.

Domain concept: A domain is a bounded context - a group of related microservices that work together to serve a specific business capability.

Why domain grouping?

Microservices within a domain: - Share business concepts and vocabulary - Often communicate with each other - Can share a database (per-domain DB) - Are deployed to the same K8s namespace - Are owned by the same team (or agent)

services/                            # 🔵 PYTHON WORLD (uv workspace)
├── README.md
├── docs/                            # Shared backend patterns
├── pyproject.toml                   # Root workspace config
├── uv.lock
│
├── _packages/                       # 📦 Shared Python Code
│   ├── common-utils/                # Logging, middleware, config
│   │   ├── pyproject.toml
│   │   ├── src/
│   │   └── tests/
│   │
│   └── shared-models/               # 🔑 SOURCE OF TRUTH - Pydantic Models
│       ├── pyproject.toml
│       ├── src/
│       │   ├── user.py              # User model
│       │   ├── survey.py            # Survey model
│       │   └── ...
│       └── tests/
│
├── user-management/                 # 👤 USER DOMAIN
│   ├── README.md                    # Domain overview
│   ├── docs/                        # Domain concepts, boundaries
│   │
│   ├── bff/                         # GraphQL Backend-for-Frontend
│   │   ├── README.md
│   │   ├── docs/
│   │   ├── src/                     # Dede writes here
│   │   ├── tests/                   # Marie writes here
│   │   ├── Dockerfile
│   │   └── pyproject.toml
│   │
│   ├── auth-service/                # Authentication service
│   │   └── ...
│   │
│   └── profile-service/             # User profile management
│       └── ...
│
├── surveys/                         # 📋 SURVEYS DOMAIN
│   ├── README.md
│   ├── docs/
│   ├── bff/
│   ├── survey-service/
│   └── analytics-service/
│
└── payments/                        # 💰 PAYMENTS DOMAIN
    ├── README.md
    ├── docs/
    ├── bff/
    └── payout-service/

Domain to K8s Namespace Mapping:

Domain Folder K8s Namespace Database
User Management services/user-management/ user-management user_db
Surveys services/surveys/ surveys surveys_db
Payments services/payments/ payments payments_db

For Dede: Never leave services/. All your shared utilities and models are here in _packages/.


The Shared Types Problem: Solved

You need User defined in Python (for the API) AND in TypeScript (for the frontend). Don't maintain two copies - they'll drift apart.

Solution: Generation

┌─────────────────────────────────────┐
│  services/_packages/shared-models/  │  🔵 SOURCE OF TRUTH (Python)
│  └── src/user.py                    │     Pydantic models
└──────────────────┬──────────────────┘
                   │
                   │  .github/workflows/generate-types.yml
                   │  (CI runs on changes to shared-models/)
                   │
                   ▼
┌─────────────────────────────────────┐
│  apps/_packages/generated-types/    │  🟢 GENERATED (TypeScript)
│  └── src/user.ts                    │     TypeScript interfaces
└─────────────────────────────────────┘     ⚠️ READ-ONLY - Never edit!

How it works:

  1. Source of Truth: Dede defines Pydantic models in services/_packages/shared-models/
  2. Generation: CI runs datamodel-code-generator or similar on changes
  3. Output: TypeScript interfaces land in apps/_packages/generated-types/
  4. Consumption: Dali imports from @packages/generated-types

Critical rule: apps/_packages/generated-types/ is READ-ONLY. Dali can import from it but NEVER edit it. Changes must flow from Python → TypeScript.


platform/ - Platform Service Configurations

Configuration files for platform services, along with scripts to deploy them. These are the source of truth for config content, NOT the K8s deployment specs.

platform/
├── README.md
├── docs/
│
├── openfga/
│   ├── model.fga                     # Authorization model
│   ├── tuples/                       # Authorization tuples
│   │   └── base.json
│   └── scripts/
│       ├── deploy-model.sh           # Push model via API
│       └── seed-tuples.sh            # Seed tuples for dev/demo
│
├── supertokens/
│   ├── config.yaml                   # Auth server config
│   └── scripts/
│       └── ...
│
├── temporal/
│   └── workflows/                    # Workflow definitions
│
└── unleash/
    ├── flags.json                    # Feature flags
    └── scripts/
        └── deploy-flags.sh
Path What It Is Deployed By
openfga/model.fga Authorization model platform/openfga/scripts/deploy-model.sh
openfga/tuples/ Authorization tuples platform/openfga/scripts/seed-tuples.sh
supertokens/config.yaml Auth server config Hydration → ArgoCD (as ConfigMap)
temporal/workflows/ Workflow definitions Your code imports directly
unleash/flags.json Feature flags platform/unleash/scripts/deploy-flags.sh

Why co-locate scripts with configs? - Scripts deploy the configs they live next to - Gus doesn't need to jump between folders - Clear ownership: each platform service is self-contained

Why separate from infra/k8s/? - platform/ = Business logic (WHAT the config says + HOW to push it) - infra/k8s/ = Deployment specs (HOW it runs in K8s) - Some configs are pushed via API, not mounted as ConfigMaps


infra/ - Infrastructure & Deployment

All infrastructure-as-code and deployment manifests.

infra/terraform/ - Cloud Infrastructure

Terraform for provisioning cloud resources (VPC, EKS, RDS, etc.).

infra/terraform/
├── modules/              # Reusable Terraform modules
│   ├── vpc/
│   ├── eks/
│   ├── rds/
│   └── redis/
└── environments/         # Per-environment root modules
    ├── dev/
    │   ├── main.tf
    │   ├── variables.tf
    │   ├── outputs.tf
    │   ├── terraform.tfvars
    │   └── backend.tf
    ├── staging/
    └── prod/

Key principle: Each environment has its own state, isolated from others.

infra/k8s/ - GitOps Manifests (ArgoCD)

Kubernetes manifests managed by ArgoCD using the 3-level pattern:

infra/k8s/
├── bootstrap/            # Level 1: ArgoCD itself
│   └── argocd/
│       └── install.yaml
│
├── appsets/              # Level 2: ApplicationSets (generates apps)
│   ├── apps.yaml         # Creates apps for frontend apps
│   └── services.yaml     # Creates apps for backend services
│
├── base/                 # Level 3: Base K8s manifests (Kustomize)
│   ├── user-management/  # Maps to services/user-management/
│   │   ├── bff/
│   │   ├── auth-service/
│   │   └── profile-service/
│   ├── surveys/          # Maps to services/surveys/
│   │   └── ...
│   └── platform/         # Platform services
│       ├── openfga/
│       │   ├── deployment.yaml
│       │   ├── service.yaml
│       │   ├── kustomization.yaml
│       │   └── _generated/           # ⚠️ Hydrated - gitignored
│       │       └── model.fga
│       └── supertokens/
│           ├── deployment.yaml
│           ├── service.yaml
│           ├── kustomization.yaml
│           └── _generated/           # ⚠️ Hydrated - gitignored
│               └── config.yaml
│
└── overlays/             # Environment-specific patches
    ├── local/
    ├── dev/
    ├── staging/
    └── prod/

Why 3-level pattern? 1. Bootstrap - ArgoCD manages itself 2. ApplicationSets - Dynamically generate apps from folder structure 3. Base + Overlays - Kustomize for DRY manifests

Problem: Relative paths like ../../../../platform/supertokens/config.yaml in Kustomize are: - Fragile and break easily - Confuse tools and AI agents - Often blocked by Kustomize security settings

Solution: Use the Hydration Pattern - copy configs into infra/ before deployment.

┌─────────────────────────────────┐
│  platform/supertokens/          │  📋 SOURCE OF TRUTH
│  └── config.yaml                │     (Gus edits here)
└──────────────────┬──────────────┘
                   │
                   │  scripts/hydrate-infra.sh
                   │  (CI runs before Kustomize build)
                   │
                   ▼
┌─────────────────────────────────────────────────┐
│  infra/k8s/base/platform/supertokens/_generated │  🔧 HYDRATED COPY
│  └── config.yaml                                │     (gitignored)
└─────────────────────────────────────────────────┘

The hydration script:

#!/bin/bash
# scripts/hydrate-infra.sh

set -e

echo "💧 Hydrating infrastructure manifests..."

# SuperTokens config
echo "  → SuperTokens..."
mkdir -p infra/k8s/base/platform/supertokens/_generated
cp platform/supertokens/config.yaml infra/k8s/base/platform/supertokens/_generated/config.yaml

# OpenFGA model (if needed as ConfigMap)
echo "  → OpenFGA..."
mkdir -p infra/k8s/base/platform/openfga/_generated
cp platform/openfga/model.fga infra/k8s/base/platform/openfga/_generated/model.fga

echo "✅ Infrastructure hydration complete."

Kustomize now uses local files:

# infra/k8s/base/platform/supertokens/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

configMapGenerator:
  - name: supertokens-config
    files:
      # Robust: local path, no relative path hell
      - config.yaml=_generated/config.yaml

Why hydration is better for AI agents:

  1. Atomic units - When Gus looks at infra/k8s/base/platform/supertokens/, he sees a complete, self-contained package
  2. No path guessing - Gus doesn't need to mentally resolve ../../../../ paths
  3. Fast failure - If Veuve deletes a config in platform/ by mistake, hydrate-infra.sh fails immediately with "File not found"
  4. GitIgnored - _generated/ folders are not committed; they're build artifacts

Add to .gitignore:

# Hydrated configs (build artifacts)
**/_generated/

infra/docker/ - Local Development

Dockerfiles for local development dependencies (Postgres, Redis, etc.).


scripts/ - Developer Scripts (Root Level)

Generic development scripts that don't belong to any specific platform service.

scripts/
├── setup.sh              # First-time dev setup
├── test-all.sh           # Run all tests
├── reset-db.sh           # Reset local databases
└── hydrate-infra.sh      # Copy platform configs into infra/k8s

Why at root level? - These are generic dev scripts, not tied to any platform service - Easy to find: ./scripts/setup.sh - hydrate-infra.sh bridges platform/ and infra/ - doesn't belong to either

Platform-specific scripts live with their configs: - OpenFGA deploy: platform/openfga/scripts/deploy-model.sh - Feature flags: platform/unleash/scripts/deploy-flags.sh


seed-data/ - Demo and Test Data

Seed data for demos and local development.

seed-data/
├── README.md
├── scenarios/
│   ├── demo/                  # Demo data for sales
│   │   ├── users.json
│   │   ├── surveys.json
│   │   └── responses.json
│   └── cocoa-cooperative/     # Specific demo scenario
│       └── ...
└── seed.py                    # Seed script

Why at root level? - Seed data spans multiple domains (users, surveys, payments) - Used by both humans (demos) and tests (fixtures) - Clear purpose: demo/dev data, not production


tests/ - Cross-Cutting Tests

Tests that span multiple services or test infrastructure.

Path What It Tests When It Runs
e2e/ Full system, multiple services PR merge, nightly
contract/ API compatibility between services Every commit
journey/ User flows across the system PR merge, nightly
load/ Performance under stress Weekly, before release
smoke/ Quick "is everything up?" check After deploy
infra/terraform/ Terraform module validation Every commit
infra/k8s/ K8s manifest validation (kubeval) Every commit
infra/docker/ Dockerfile linting (hadolint) Every commit

Note: Unit and integration tests live WITH the code they test (in services/**/tests/, apps/**/tests/).


docs/ - Root Documentation

MkDocs aggregates documentation from all docs/ folders across the repo.

# mkdocs.yml
nav:
  - Home: index.md
  - Getting Started: getting-started/index.md
  - Architecture: architecture/index.md
  - Apps:
    - Overview: apps/docs/index.md
    - Farmer PWA: apps/farmer-pwa/docs/index.md
    - Stakeholder Portal: apps/stakeholder-portal/docs/index.md
  - Services:
    - Overview: services/docs/index.md
    - User Management:
      - Overview: services/user-management/docs/index.md
      - Auth Service: services/user-management/auth-service/docs/index.md
    - Surveys:
      - Overview: services/surveys/docs/index.md
  - Platform: platform/docs/index.md
  - Infrastructure: infra/docs/index.md
  - Tests: tests/docs/index.md

Progressive disclosure: Each level has its own docs/ for context-specific documentation.


Complete Repository Structure

my-project/
│
├── apps/                               # 🟢 TYPESCRIPT WORLD (pnpm workspace)
│   ├── README.md
│   ├── docs/
│   ├── package.json                    # Root workspace config
│   ├── pnpm-workspace.yaml
│   ├── turbo.json
│   ├── tsconfig.base.json
│   │
│   ├── _packages/                      # 📦 Shared TypeScript Code
│   │   ├── ui-library/                 # Shared React components
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   └── tests/                  # Marie writes here
│   │   │
│   │   ├── ts-utils/                   # Logging, formatting, helpers
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   └── tests/
│   │   │
│   │   └── generated-types/            # ⚠️ READ-ONLY - From Python
│   │       ├── package.json
│   │       └── src/
│   │
│   ├── farmer-pwa/                     # 📱 Mobile-first PWA
│   │   ├── README.md
│   │   ├── docs/
│   │   ├── src/                        # Dali writes here
│   │   ├── tests/                      # Marie writes here
│   │   │   ├── unit/
│   │   │   └── component/
│   │   ├── public/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── vite.config.ts
│   │
│   └── stakeholder-portal/             # 🏢 Stakeholder web portal
│       ├── README.md
│       ├── docs/
│       ├── src/
│       ├── tests/
│       ├── Dockerfile
│       └── package.json
│
├── services/                           # 🔵 PYTHON WORLD (uv workspace)
│   ├── README.md
│   ├── docs/
│   ├── pyproject.toml                  # Root workspace config
│   ├── uv.lock
│   │
│   ├── _packages/                      # 📦 Shared Python Code
│   │   ├── common-utils/               # Logging, middleware
│   │   │   ├── pyproject.toml
│   │   │   ├── src/
│   │   │   └── tests/                  # Marie writes here
│   │   │
│   │   └── shared-models/              # 🔑 SOURCE OF TRUTH - Pydantic
│   │       ├── pyproject.toml
│   │       ├── src/
│   │       └── tests/
│   │
│   ├── user-management/                # 👤 User domain
│   │   ├── README.md
│   │   ├── docs/
│   │   │
│   │   ├── bff/                        # GraphQL BFF
│   │   │   ├── README.md
│   │   │   ├── docs/
│   │   │   ├── src/                    # Dede writes here
│   │   │   ├── tests/                  # Marie writes here
│   │   │   ├── Dockerfile
│   │   │   └── pyproject.toml
│   │   │
│   │   ├── auth-service/
│   │   │   └── ...
│   │   │
│   │   └── profile-service/
│   │       └── ...
│   │
│   ├── surveys/                        # 📋 Surveys domain
│   │   ├── README.md
│   │   ├── docs/
│   │   ├── bff/
│   │   ├── survey-service/
│   │   └── analytics-service/
│   │
│   └── payments/                       # 💰 Payments domain
│       ├── README.md
│       ├── docs/
│       ├── bff/
│       └── payout-service/
│
├── platform/                           # ⚙️ GUS'S DOMAIN - Platform Configs
│   ├── README.md
│   ├── docs/
│   │
│   ├── openfga/
│   │   ├── model.fga                   # Authorization model
│   │   ├── tuples/                     # Authorization tuples
│   │   └── scripts/
│   │       ├── deploy-model.sh         # Push model via API
│   │       └── seed-tuples.sh          # Seed tuples
│   │
│   ├── supertokens/
│   │   ├── config.yaml                 # Auth server config
│   │   └── scripts/
│   │
│   ├── temporal/
│   │   └── workflows/                  # Workflow definitions
│   │
│   └── unleash/
│       ├── flags.json                  # Feature flags
│       └── scripts/
│           └── deploy-flags.sh
│
├── infra/                              # 🟠 GUS'S DOMAIN - Infrastructure
│   ├── README.md
│   ├── docs/
│   │
│   ├── terraform/
│   │   ├── README.md
│   │   ├── docs/
│   │   ├── modules/
│   │   │   ├── vpc/
│   │   │   ├── eks/
│   │   │   ├── rds/
│   │   │   └── redis/
│   │   └── environments/
│   │       ├── dev/
│   │       ├── staging/
│   │       └── prod/
│   │
│   ├── k8s/
│   │   ├── README.md
│   │   ├── docs/
│   │   ├── bootstrap/
│   │   │   └── argocd/
│   │   ├── appsets/
│   │   ├── base/
│   │   │   ├── user-management/
│   │   │   │   ├── bff/
│   │   │   │   ├── auth-service/
│   │   │   │   └── profile-service/
│   │   │   ├── surveys/
│   │   │   ├── payments/
│   │   │   └── platform/
│   │   │       ├── openfga/
│   │   │       │   ├── deployment.yaml
│   │   │       │   ├── service.yaml
│   │   │       │   ├── kustomization.yaml
│   │   │       │   └── _generated/     # ⚠️ Gitignored - hydrated
│   │   │       └── supertokens/
│   │   │           ├── deployment.yaml
│   │   │           ├── service.yaml
│   │   │           ├── kustomization.yaml
│   │   │           └── _generated/     # ⚠️ Gitignored - hydrated
│   │   └── overlays/
│   │       ├── local/
│   │       ├── dev/
│   │       ├── staging/
│   │       └── prod/
│   │
│   └── docker/
│       ├── postgres/
│       └── redis/
│
├── scripts/                            # 🛠️ Generic dev scripts
│   ├── setup.sh                        # First-time setup
│   ├── test-all.sh                     # Run all tests
│   ├── reset-db.sh                     # Reset databases
│   └── hydrate-infra.sh                # Copy platform configs → infra/k8s
│
├── seed-data/                          # 🌱 Demo and test data
│   ├── README.md
│   ├── scenarios/
│   │   ├── demo/
│   │   └── cocoa-cooperative/
│   └── seed.py
│
├── tests/                              # 🧪 MARIE'S DOMAIN - Cross-cutting
│   ├── README.md
│   ├── docs/
│   │
│   ├── e2e/
│   │   ├── conftest.py
│   │   └── test_user_registration.py
│   │
│   ├── contract/
│   │   ├── consumer/
│   │   └── provider/
│   │
│   ├── journey/
│   │   └── test_farmer_onboarding.py
│   │
│   ├── load/
│   │   ├── locustfile.py
│   │   └── scenarios/
│   │
│   ├── smoke/
│   │   └── test_all_services_up.py
│   │
│   └── infra/
│       ├── terraform/
│       ├── k8s/
│       └── docker/
│
├── specs/                              # 📋 FEATURE SPECS (All agents)
│   ├── README.md
│   │
│   └── <issue-number>/
│       ├── product/                    # Veuve writes here
│       ├── architecture/               # Duc writes here
│       ├── test-strategy.md            # Marie writes here
│       ├── docs-review.md              # Victor writes here
│       ├── review.md                   # General writes here
│       └── retro.md                    # Socrate writes here
│
├── docs/                               # 📚 GLOBAL DOCS (Duc owns)
│   ├── index.md
│   ├── getting-started/
│   ├── architecture/
│   ├── user-journeys/
│   └── api/
│
├── .farmerspec/                        # 🤖 AGENT SYSTEM
│   ├── state.yaml
│   ├── agents/
│   ├── templates/
│   └── retros/
│
├── .github/
│   ├── workflows/
│   │   ├── ci.yml
│   │   ├── generate-types.yml          # Runs on Python model changes
│   │   ├── deploy-dev.yml
│   │   ├── deploy-staging.yml
│   │   └── deploy-prod.yml
│   └── CODEOWNERS
│
├── docker-compose.yml
├── docker-compose.demo.yml
├── Makefile
├── mkdocs.yml
├── CLAUDE.md
├── README.md
├── .env.example
└── .gitignore

Why This Structure Works for AI Agents

1. Pure Language Worlds

The most important optimization: language isolation.

apps/     → 100% TypeScript  → Dali's world
services/ → 100% Python      → Dede's world

Benefits: - Dali never sees Python files - Dede never sees TypeScript files - No mixed-language packages/ folder - Simpler linting, testing, and CI

2. Agents Never Leave Their World

Agent Stays Within
Dali apps/ only
Dede services/ only
Gus infra/, platform/
Marie */tests/ and tests/

No context-switching between languages.

3. _packages/ is Co-located

Shared code lives WITH its language:

apps/
├── _packages/        # Shared TypeScript (Dali's)
│   ├── ui-library/
│   ├── ts-utils/
│   └── generated-types/
└── farmer-pwa/

services/
├── _packages/        # Shared Python (Dede's)
│   ├── common-utils/
│   └── shared-models/
└── user-management/

Why _packages/? - Sorts to top (underscore prefix) - Visually distinct from apps/services - Signals "shared, don't deploy"

4. Type Generation Solves Cross-Language

The User model exists in both languages, but: - Source of truth: services/_packages/shared-models/ (Python) - Generated: apps/_packages/generated-types/ (TypeScript)

Dali reads from generated types but NEVER edits them. Changes flow one way.

5. Hydration Keeps K8s Self-Contained

The hydration pattern copies platform/ configs into infra/k8s/:

platform/supertokens/config.yaml
    ↓ scripts/hydrate-infra.sh
infra/k8s/base/platform/supertokens/_generated/config.yaml

Benefits for Gus: - Each K8s manifest folder is self-contained - No relative path hell (../../../../) - Clear build step before deployment - Fast failure if source config is missing

6. Platform Scripts Live With Configs

platform/openfga/
├── model.fga              # The config
├── tuples/                # The data
└── scripts/
    ├── deploy-model.sh    # How to deploy the config
    └── seed-tuples.sh     # How to seed the data

Gus doesn't need to jump between folders - everything is co-located.

7. Domain Grouping Limits Blast Radius

Services are grouped by domain:

services/
├── user-management/    # 👤 User domain
│   ├── bff/
│   ├── auth-service/
│   └── profile-service/
├── surveys/            # 📋 Surveys domain
└── payments/           # 💰 Payments domain

When Dede works on surveys/survey-service/, they don't accidentally touch payments/.

8. K8s Mirrors Services

services/user-management/auth-service/
    ↓
infra/k8s/base/user-management/auth-service/

Gus can map any service to its K8s manifests by path.

9. Progressive Documentation

Docs exist at every level:

docs/                              # Global (Duc)
apps/
├── docs/                          # All apps (Dali)
└── farmer-pwa/
    └── docs/                      # This app (Dali)
services/
├── docs/                          # All services (Dede)
└── user-management/
    ├── docs/                      # This domain (Dede)
    └── auth-service/
        └── docs/                  # This service (Dede)

Victor knows exactly where to look and who owns each doc.


Agent Workflow Example

Feature: Add user authentication

  1. Baron creates specs/042-user-auth/, sets state to classification

  2. Veuve writes specs/042-user-auth/product/requirements.md

  3. Duc writes specs/042-user-auth/architecture/design.md, updates docs/architecture/auth.md

  4. Marie writes tests:

  5. services/user-management/auth-service/tests/unit/test_auth.py
  6. services/user-management/auth-service/tests/integration/test_auth_flow.py
  7. apps/farmer-pwa/tests/component/test_login_form.tsx
  8. tests/e2e/test_login_journey.py
  9. tests/infra/k8s/test_auth_service_manifest.py

  10. Dede implements backend (never leaves services/):

  11. services/user-management/auth-service/src/
  12. services/_packages/shared-models/src/user.py

  13. CI runs type generation (on change to shared-models):

  14. Input: services/_packages/shared-models/
  15. Output: apps/_packages/generated-types/

  16. Dali implements frontend (never leaves apps/):

  17. apps/farmer-pwa/src/components/LoginForm.tsx
  18. apps/_packages/ui-library/src/AuthButton.tsx
  19. Imports from @packages/generated-types

  20. Gus implements infrastructure:

  21. platform/supertokens/config.yaml
  22. infra/k8s/base/user-management/auth-service/
  23. Runs scripts/hydrate-infra.sh before testing

  24. Victor reviews docs, writes specs/042-user-auth/docs-review.md

  25. General reviews code, creates PR

  26. Socrate analyzes workflow, writes specs/042-user-auth/retro.md


CI Pipeline

With language separation and hydration, CI becomes clear:

# .github/workflows/ci.yml

jobs:
  python-tests:
    if: contains(github.event.paths, 'services/')
    steps:
      - run: cd services && uv run pytest

  typescript-tests:
    if: contains(github.event.paths, 'apps/')
    steps:
      - run: cd apps && pnpm test

  type-generation:
    if: contains(github.event.paths, 'services/_packages/shared-models/')
    steps:
      - run: datamodel-codegen --input services/_packages/shared-models/src --output apps/_packages/generated-types/src
      - run: git add apps/_packages/generated-types/
      - run: git commit -m "chore: regenerate TypeScript types" || true
      - run: git push

  infra-validation:
    if: contains(github.event.paths, 'infra/') || contains(github.event.paths, 'platform/')
    steps:
      # Hydrate before validation
      - run: ./scripts/hydrate-infra.sh
      - run: cd infra/terraform && terraform validate
      - run: cd infra/k8s && kustomize build base/

Deploy workflow:

# .github/workflows/deploy-prod.yml

jobs:
  deploy:
    steps:
      - uses: actions/checkout@v4

      # Hydrate before building manifests
      - name: Hydrate Infrastructure
        run: ./scripts/hydrate-infra.sh

      - name: Build Manifests
        run: kustomize build infra/k8s/overlays/prod > deploy.yaml

      - name: Deploy
        run: kubectl apply -f deploy.yaml

Platform Configs: Who Deploys What?

Config Location Deployed By How
OpenFGA model platform/openfga/model.fga CI script platform/openfga/scripts/deploy-model.sh
OpenFGA tuples platform/openfga/tuples/ CI or manual platform/openfga/scripts/seed-tuples.sh
SuperTokens config platform/supertokens/config.yaml ArgoCD Hydration → ConfigMap
Temporal workflows platform/temporal/workflows/ Your code Python import
Feature flags platform/unleash/flags.json CI script platform/unleash/scripts/deploy-flags.sh
K8s manifests infra/k8s/ ArgoCD Kustomize
Cloud infra infra/terraform/ Terraform terraform apply
TypeScript types apps/_packages/generated-types/ CI workflow Auto-generated

Testing Strategy by Agent

Test Type Location Written By Made Pass By
Backend unit services/<domain>/<svc>/tests/unit/ Marie Dede
Backend integration services/<domain>/<svc>/tests/integration/ Marie Dede
Frontend unit apps/<app>/tests/unit/ Marie Dali
Frontend component apps/<app>/tests/component/ Marie Dali
Shared Python tests services/_packages/*/tests/ Marie Dede
Shared TS tests apps/_packages/*/tests/ Marie Dali
E2E tests/e2e/ Marie All
Contract tests/contract/ Marie Dede/Dali
Journey tests/journey/ Marie All
Load tests/load/ Marie Gus validates
Smoke tests/smoke/ Marie Gus validates
Infra tests/infra/ Marie Gus

Critical rule: Once Marie writes tests, they are IMMUTABLE. Implementation agents make them pass, they don't change them.


Summary: Why This Structure?

Design Decision Benefit for AI Agents
Language isolation (apps/ vs services/) Agents never mix languages
_packages/ inside each world Shared code is co-located, not separate
Generated types (Python → TS) Single source of truth, one-way flow
Hydration pattern for K8s Self-contained manifests, no relative paths
No root packages/ folder Forces language purity
Domain-grouped services Clear boundaries, limited blast radius
Multiple apps in apps/ Each app independent, Dali focuses on one
Platform scripts with configs Gus doesn't jump between folders
K8s structure mirrors services Easy mental mapping for Gus
_generated/ folders gitignored Build artifacts, not source code
Progressive docs at every level Victor can review systematically
specs/ as feature home All agents contribute to single location
Tests co-located with code Agents see what to implement and test together

This structure is designed for AI agents to operate efficiently with strict language separation, minimal context switching, and clear ownership boundaries.