Start a new conversation with the AI PM by sending the first message. Only the project owner can start a conversation. The project must not be ARCHIVED.
- Starting a new conversation automatically closes the current ACTIVE conversation for this project (if any) — its status is set to
CLOSED. - A project can have at most one ACTIVE conversation at a time (MVP constraint).
- The conversation title is auto-generated from the first message: the first 50 characters, trimmed at the last word boundary so no word is cut off.
- After creating the USER message, a mock PM response is created immediately (since AI is not integrated yet). The mock response simulates a realistic PM acknowledgement. The API contract is designed so that real AI integration can replace the mock later without any changes to the API.
- The response includes the newly created conversation with its messages array (both the USER message and the mock PM response).
- An ARCHIVED project cannot start a new conversation.
Auto-start variant (called from UC-03002)
StartConversationUseCase is also invoked internally by
UC-03002 Create Project immediately
after a project is created. This variant differs from the standard “user types first message”
flow described above:
- No
messageis supplied → no USER message is persisted, no mock PM message is generated. - Conversation is created with
status = ACTIVE,title = "New project"(i18n fallback; re-titled later from the first real exchange),sessionId = null. - Runs in the same transaction as the project insert — if conversation creation fails, project insert rolls back.
- The newly returned
conversationIdis included in theCreateProjectResponse.
After the FE navigates to /workspace/:projectId and loads this empty conversation, it triggers
Mara’s opening message via SSE without any user input. Mara uses the project’s description
and tech-stack fields (rendered into CLAUDE.md per
mara-context.md) as context, does not greet the user
(Pseudo-Mara already did during the create flow), and asks a short focused question about
today’s work.
Note: the standard “user types first message” flow described below still applies for all
subsequent Start Conversation calls (e.g. closing the current ACTIVE conversation and
starting a fresh one mid-project).
sequenceDiagram
actor User
User->>+FE: types first message and submits
FE->>FE: validate form
alt form is invalid
FE-->>User: show error messages <br> disable submit button
end
FE->>+BE: POST /api/v1/projects/{projectId}/conversations <br> Authorization: Bearer {accessToken} <br> StartConversationRequest
BE->>BE: validate access token
alt token missing or invalid
BE-->>FE: 401 Unauthorized <br> ErrorResponse
end
BE->>DB: load project by projectId
alt project not found
BE-->>FE: 404 Not Found <br> ErrorResponse
end
BE->>BE: check project belongs to user's tenant
alt project does not belong to tenant
BE-->>FE: 403 Forbidden <br> ErrorResponse
end
BE->>BE: check project is not ARCHIVED
alt project is ARCHIVED
BE-->>FE: 409 Conflict <br> ErrorResponse
end
BE->>BE: validate request body
alt request is invalid
BE-->>FE: 400 Bad Request <br> ErrorResponse
end
BE->>DB: find current ACTIVE conversation for project (if any)
alt ACTIVE conversation exists
BE->>DB: set existing conversation status = CLOSED
end
BE->>DB: insert new conversation (status = ACTIVE, title = auto-generated)
BE->>DB: insert USER message
BE->>BE: generate mock PM response
BE->>DB: insert PM message
BE->>-FE: 201 Created <br> ConversationDetailResponse
FE->>-User: open conversation view with messages
POST /api/v1/projects/{projectId}/conversations StartConversationRequest:
{
"message": "I want to add a contact form to the homepage with name, email, and message fields."
}
201 Created ConversationDetailResponse:
{
"data": {
"id": 8,
"title": "I want to add a contact form to the homepage",
"status": "ACTIVE",
"messageCount": 2,
"createdAt": "2026-04-29T12:00:00Z",
"updatedAt": "2026-04-29T12:00:01Z",
"messages": [
{
"id": 21,
"role": "USER",
"content": "I want to add a contact form to the homepage with name, email, and message fields.",
"createdAt": "2026-04-29T12:00:00Z"
},
{
"id": 22,
"role": "PM",
"content": "Great idea! I understand your request. Let me analyze the homepage structure and plan the contact form implementation. I'll get back to you with a detailed plan shortly.",
"createdAt": "2026-04-29T12:00:01Z"
}
]
}
}
400 Bad Request (validation) ErrorResponse:
{
"status": 400,
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"errors": [
{ "field": "message", "message": "must not be blank" }
]
}
401 Unauthorized (missing or invalid access token) ErrorResponse:
{
"status": 401,
"code": "AUTHENTICATION_FAILED",
"message": "Access token is missing or invalid"
}
403 Forbidden (project does not belong to user’s tenant) ErrorResponse:
{
"status": 403,
"code": "FORBIDDEN",
"message": "You do not have access to this project"
}
404 Not Found (project not found) ErrorResponse:
{
"status": 404,
"code": "NOT_FOUND_PROJECT",
"message": "Project not found"
}
409 Conflict (project is ARCHIVED) ErrorResponse:
{
"status": 409,
"code": "CONFLICT_PROJECT",
"message": "Cannot start a conversation on an ARCHIVED project"
}
Frontend
Validations
| Field | Constraints | Size | Pattern | Note |
|---|---|---|---|---|
| message | not_blank | 1 - 5000 |
Backend
Validations
| Field | Constraints | Size | Pattern | Note |
|---|---|---|---|---|
| projectId | not_null, positive | — | — | Path variable; must reference an existing project |
| message | not_blank | 1 - 5000 | First user message; used for auto-generating conversation title |
Mock PM Responses
The backend selects one of the following mock responses when creating the PM message. The selection can be random or sequential. This list will be replaced by real AI integration in a future iteration:
- “Great idea! I understand your request. Let me analyze the project structure and plan the implementation. I’ll outline the necessary changes shortly.”
- “Got it. I’m reviewing your requirements now. This looks like an interesting challenge — let me think through the best approach and come back to you with a plan.”
- “Understood! I’ve noted your request and I’m already thinking about how to approach this. Give me a moment to analyze the current state of the project.”
- “Perfect, I have a clear picture of what you need. Let me work through the details and prepare a step-by-step plan for this change.”
Test Cases
| GIVEN | WHEN | THEN |
|---|---|---|
| authenticated user, active project, no existing conversation | POST /projects/{projectId}/conversations is called | 201 Created with new conversation (ACTIVE), USER and PM messages returned |
| authenticated user, active project, existing ACTIVE conversation | POST /projects/{projectId}/conversations is called | existing conversation is closed, new conversation created, 201 Created returned |
| authenticated user, ARCHIVED project | POST /projects/{projectId}/conversations is called | 409 CONFLICT_PROJECT error response is returned |
| authenticated user, project does not exist | POST /projects/{projectId}/conversations is called | 404 NOT_FOUND_PROJECT error response is returned |
| authenticated user, project belongs to a different tenant | POST /projects/{projectId}/conversations is called | 403 FORBIDDEN error response is returned |
| message is blank | POST /projects/{projectId}/conversations is called | 400 VALIDATION_ERROR error response is returned |
| message longer than 5000 characters | POST /projects/{projectId}/conversations is called | 400 VALIDATION_ERROR error response is returned |
| no Authorization header | POST /projects/{projectId}/conversations is called | 401 AUTHENTICATION_FAILED error response is returned |
| first message exactly 50 characters | POST /projects/{projectId}/conversations is called | 201 Created; conversation title equals the message content exactly |
| first message longer than 50 characters | POST /projects/{projectId}/conversations is called | 201 Created; conversation title is trimmed at word boundary within first 50 chars |
| project just created (auto-start variant from UC-03002) | StartConversationUseCase.autoStart(projectId) is invoked internally | conversation created with status=ACTIVE, title=“New project”, no USER message, no mock PM message, sessionId=null |
| auto-start variant; underlying DB insert fails | StartConversationUseCase.autoStart(projectId) is invoked internally | exception propagates; outer Create Project transaction rolls back |
Thanks for the feedback.