Files
condado-newsletter/CLAUDE.md
Gabriel Sancho d834ca85b0 Add build instructions and project structure for Condado Abaixo da Média SA Email Bot
- Created INSTRUCTIONS.md detailing project goals, usage, and progress tracking.
- Defined project scope, technology stack, and core domain concepts.
- Outlined step-by-step build process from scaffolding to deployment.
- Included detailed descriptions for each step, including entity models, services, and controllers.
- Established a decision log to track key choices made during development.
2026-03-26 14:07:59 -03:00

9.5 KiB

Condado Abaixo da Média SA — Email Bot

A backend service built with Kotlin and Spring Boot. This file gives the AI persistent instructions and context about the project so every session starts with the right knowledge.


Project Overview

  • Language: Kotlin (JVM)
  • Framework: Spring Boot 3.x
  • Purpose: Simulate virtual employees of the fictional company "Condado Abaixo da Média SA". Each entity is a virtual employee with a name, email address, job title, personality, and an email schedule. At the scheduled time, the system reads recent emails from the company mailbox (filtered by a configurable time window), builds a prompt from the entity's profile + the email history, sends that prompt to an AI (OpenAI API), and dispatches the AI-generated email via SMTP.
  • Tone rule (critical): Every generated email must be written in an extremely formal, corporate tone — but the content is completely casual and nonsensical, like internal jokes between friends. This contrast is the core joke of the project and must be preserved in every generated email.
  • Architecture: REST API backend + scheduled AI-driven email dispatch

Tech Stack

Layer Technology
Language Kotlin (JVM)
Framework Spring Boot 3.x
Build Tool Gradle (Kotlin DSL — build.gradle.kts)
Database PostgreSQL (via Spring Data JPA)
Email Reading Jakarta Mail (IMAP) — read inbox for context
Email Sending Spring Mail (SMTP / JavaMailSender)
AI Integration OpenAI API (gpt-4o) via HTTP client
Scheduler Spring @Scheduled tasks
Testing JUnit 5 + MockK
Docs Springdoc OpenAPI (Swagger UI)

Project Structure

src/
├── main/
│   ├── kotlin/com/condado/newsletter/
│   │   ├── CondadoApplication.kt              # App entry point
│   │   ├── config/                            # Spring configuration classes
│   │   ├── controller/                        # REST controllers
│   │   ├── service/                           # Business logic
│   │   │   ├── EntityService.kt               # CRUD for virtual entities
│   │   │   ├── EmailReaderService.kt          # Reads emails via IMAP
│   │   │   ├── PromptBuilderService.kt        # Builds AI prompt from entity + emails
│   │   │   ├── AiService.kt                   # Calls OpenAI API
│   │   │   └── EmailSenderService.kt          # Sends email via SMTP
│   │   ├── repository/                        # Spring Data JPA repositories
│   │   ├── model/                             # JPA entities
│   │   ├── dto/                               # Data Transfer Objects
│   │   └── scheduler/                         # Scheduled tasks (trigger per entity)
│   └── resources/
│       ├── application.yml                    # Main config
│       └── application-dev.yml                # Dev profile config
└── test/
    └── kotlin/com/condado/newsletter/         # Tests mirror main structure

Build & Run Commands

# Build the project
./gradlew build

# Run the application (dev profile)
./gradlew bootRun --args='--spring.profiles.active=dev'

# Run all tests
./gradlew test

# Run a specific test class
./gradlew test --tests "com.condado.newsletter.service.PromptBuilderServiceTest"

# OpenAPI docs available at runtime
# http://localhost:8080/swagger-ui.html

Coding Standards

  • Use Kotlin idiomatic style: data classes, extension functions, and null-safety operators.
  • Prefer val over var wherever possible.
  • Use constructor injection for dependencies (never field injection with @Autowired).
  • All DTOs must be data classes with validation annotations (jakarta.validation).
  • Controller methods must return ResponseEntity<T> with explicit HTTP status codes.
  • Services must be annotated with @Service and never depend on controllers.
  • Repositories must extend JpaRepository<Entity, IdType>.
  • Use @Transactional on service methods that modify data.
  • All public functions must have KDoc comments.
  • Use snake_case for database columns and camelCase for Kotlin properties.
  • Keep controllers thin — business logic belongs in services.
  • The AI prompt construction logic must live exclusively in PromptBuilderService — no other class should build or modify prompt strings.

Naming Conventions

Artifact Convention Example
Classes PascalCase PromptBuilderService
Functions camelCase buildPrompt()
Variables camelCase entityList
Constants SCREAMING_SNAKE_CASE MAX_EMAIL_CONTEXT_DAYS
DB tables snake_case (plural) virtual_entities
REST endpoints kebab-case /api/v1/virtual-entities
Packages lowercase com.condado.newsletter

Testing Guidelines

  • Every service class must have a corresponding unit test class.
  • Use MockK for mocking (not Mockito).
  • Integration tests use @SpringBootTest and an H2 in-memory database.
  • Test method names follow the pattern: should_[expectedBehavior]_when_[condition].
  • Minimum 80% code coverage for service classes.

Environment Variables

Variable Description
SPRING_DATASOURCE_URL PostgreSQL connection URL
SPRING_DATASOURCE_USERNAME DB username
SPRING_DATASOURCE_PASSWORD DB password
MAIL_HOST SMTP host (for sending emails)
MAIL_PORT SMTP port
MAIL_USERNAME SMTP username (also used as IMAP login)
MAIL_PASSWORD SMTP/IMAP password
IMAP_HOST IMAP host (for reading the shared inbox)
IMAP_PORT IMAP port (default: 993)
IMAP_INBOX_FOLDER IMAP folder to read (default: INBOX)
OPENAI_API_KEY OpenAI API key for AI generation
OPENAI_MODEL OpenAI model to use (default: gpt-4o)
API_KEY API key to protect the REST endpoints

⚠️ Never hardcode credentials. Always use environment variables or a .env file (gitignored).


Key Domain Concepts

  • VirtualEntity: A fictional employee of "Condado Abaixo da Média SA". Has a name, a real email address (used as sender), a job title, a personality description, an email schedule (cron expression), and an email context window (how many days back to read emails for context).

  • EmailContext: A snapshot of recent emails read from the shared IMAP inbox, filtered by the entity's configured context window (e.g., last 3 days). Used to give the AI conversational context.

  • Prompt: The full text sent to the OpenAI API. Built by PromptBuilderService from the entity's profile + the EmailContext. Always instructs the AI to write in an extremely formal corporate tone with completely casual/nonsensical content.

  • DispatchLog: A record of each AI email generation and send attempt for a given entity. Stores the generated prompt, the AI response, send status, and timestamp.


The Prompt Template (Core Logic)

Every prompt sent to the AI must follow this structure:

You are [entity.name], [entity.jobTitle] at "Condado Abaixo da Média SA".

Your personality: [entity.personality]

IMPORTANT TONE RULE: You must write in an extremely formal, bureaucratic, corporate tone —
as if writing an official memo. However, the actual content of the email must be completely
casual, trivial, or nonsensical — as if talking to close friends about mundane things.
The contrast between the formal tone and the casual content is intentional and essential.

Here are the most recent emails from the company inbox (last [entity.contextWindowDays] days)
for context:

[list of recent emails: sender, subject, body]

Write a new email to be sent to the company group, continuing the conversation naturally.
Reply or react to the recent emails if relevant. Sign off as [entity.name], [entity.jobTitle].

Git Workflow

  • Branch naming: feature/<short-description>, fix/<short-description>, chore/<short-description>
  • Commit messages follow Conventional Commits: feat:, fix:, chore:, docs:, test:
  • PRs require at least one passing CI check before merging.
  • Never commit directly to main.