Saltar a contenido

Estado de fase (MASTER-00): POST_MVP — documento de catálogo de producto sujeto a validación; no es alcance aprobado de Gate 0 / PoC / MVP. Gobernanza: MASTER-00.

Backend Spec: Project Creation & Frontmatter Workspace Scanning (project.create)

1. Overview & Objectives

This specification defines the server-side logic for registering a new documentation project (createProject) in :docugraph-backend. It receives HTTP POST /api/v1/projects, enforces subscription tier limits (max 3 active projects for Free tier), initializes a project entry in PostgreSQL projects with status INITIALIZING, delegates Markdown frontmatter scanning to :docugraph-core, and updates project metadata upon completion.

2. API Endpoint Signature & Traceability

  • HTTP Method & Path: POST /api/v1/projects
  • operationId: createProject
  • Upstream Requirements: US-02, IX-02, SCHEMA-01
  • Primary Service Class: ProjectService.kt
  • Ktor Route Handler: ProjectRoutes.kt

3. Data Models & Kotlin Data Classes

package io.docugraph.backend.models.project

import kotlinx.serialization.Serializable

@Serializable
data class CreateProjectRequest(
    val name: String,
    val description: String? = null,
    val workspacePath: String,
    val gitRemoteUrl: String? = null,
    val gitDefaultBranch: String = "main",
    val schemaConfig: Map<String, String> = emptyMap()
)

@Serializable
data class ProjectResponse(
    val id: String,
    val ownerId: String,
    val name: String,
    val description: String?,
    val workspacePath: String,
    val gitRemoteUrl: String?,
    val status: String, // "INITIALIZING" | "ACTIVE" | "INDEXING_FAILED"
    val nodeCount: Int,
    val edgeCount: Int,
    val lastIndexedAt: String?,
    val createdAt: String,
    val updatedAt: String
)

4. Sequence & Control Flow

Client (FE-02)         Ktor ProjectRoute       ProjectService         Core Engine (:docugraph-core) PostgreSQL DB
      |                        |                      |                          |                   |
      |-- POST /projects ----->|                      |                          |                   |
      |   (CreateProjectReq)   |-- checkEntitlement ->|--------------------------------------------->| SELECT COUNT
      |                        |                      |<- sub status & count ------------------------|
      |                        |                      |                          |                   |
      |                        |                      |-- INSERT project (INIT)--------------------->| INSERT projects
      |                        |                      |                          |                   |
      |                        |                      |-- scanWorkspace(path) -->|                   |
      |                        |                      |                          |-- parse YAML frontmatters
      |                        |                      |                          |-- construct DAG   |
      |                        |                      |<- ParsedGraph (nodes/edges)------------------|
      |                        |                      |                          |                   |
      |                        |                      |-- batch insert nodes & edges --------------->| INSERT nodes/edges
      |                        |                      |-- UPDATE status = ACTIVE ------------------->| UPDATE projects
      |<- 201 Created ---------|<- ProjectResponse ---|                          |                   |

5. Database Interaction & SQL Queries

5.1 Project Limit & Entitlement Verification

SELECT 
    s.entitlement_id,
    COUNT(p.id) AS active_project_count
FROM subscriptions s
LEFT JOIN projects p ON p.owner_id = s.user_id
WHERE s.user_id = :userId
GROUP BY s.entitlement_id;

Business Rule: If entitlement_id == 'free' and active_project_count >= 3, reject request with 403 Forbidden (PROJECT_LIMIT_EXCEEDED).

5.2 Insert Project Record

INSERT INTO projects (
    owner_id, name, description, workspace_path, 
    git_remote_url, git_default_branch, schema_config, 
    node_count, edge_count, last_indexed_at
) VALUES (
    :ownerId, :name, :description, :workspacePath, 
    :gitRemoteUrl, :gitDefaultBranch, :schemaConfig::jsonb, 
    0, 0, NULL
)
RETURNING *;

5.3 Batch Insert Parsed Nodes and Edges

INSERT INTO nodes (
    project_id, atomic_id, title, document_type, status, 
    relative_file_path, file_checksum_sha256, frontmatter_json, tags
) VALUES (
    :projectId, :atomicId, :title, :docType, :status, 
    :relPath, :checksum, :frontmatterJson::jsonb, :tags
)
ON CONFLICT (project_id, atomic_id) DO UPDATE SET
    title = EXCLUDED.title,
    document_type = EXCLUDED.document_type,
    status = EXCLUDED.status,
    relative_file_path = EXCLUDED.relative_file_path,
    file_checksum_sha256 = EXCLUDED.file_checksum_sha256,
    frontmatter_json = EXCLUDED.frontmatter_json,
    tags = EXCLUDED.tags,
    updated_at = CURRENT_TIMESTAMP;
INSERT INTO node_edges (
    project_id, source_node_id, target_node_id, relationship_type
) VALUES (
    :projectId, :sourceNodeId, :targetNodeId, :relationshipType
)
ON CONFLICT (source_node_id, target_node_id, relationship_type) DO NOTHING;

6. Error Handling & HTTP Status Codes

Error Condition HTTP Code Internal Code Response Message
Invalid workspace directory path 400 Bad Request INVALID_WORKSPACE_PATH "Workspace path does not exist or is unreadable."
Free tier project limit (3) reached 403 Forbidden PROJECT_LIMIT_EXCEEDED "Free tier limited to 3 projects. Upgrade to Pro Tier."
Duplicate project name or path for owner 409 Conflict PROJECT_ALREADY_EXISTS "Project already registered for this path."
Parser engine crash 500 Server Error INDEXING_FAILED "Failed to parse Markdown workspace files."

7. Verification & Test Plan

  • Unit Test: Test project limit check logic for Free vs Pro accounts.
  • Integration Test: Mock :docugraph-core workspace parser and verify node/edge DB insertions.