Files
issue-core/issue_core/api/schemas.py
tegwick b605d970e3 feat: rename to issue-core and add task ingestion endpoint
Renames the package, distribution, CLI alias, Makefile targets, and
working directory from issue-facade to issue-core, signalling its
role as the authoritative task lifecycle manager for the Coulomb org
(peer to activity-core, rules-core, project-core).

Adds POST /issues/ ingestion endpoint for activity-core's IssueSink,
under a new optional [api] extra. The endpoint is served by `issue
serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or
X-API-Key header), and routes the TaskSpec payload to the configured
default backend with full traceability metadata embedded in
sync_metadata.

- T01: Python package issue_tracker -> issue_core, dir rename
- T02: registered in state hub under custodian domain
- T03: INTENT.md (what it is, what it isn't, how it fits)
- T04: SCOPE.md (in/out-of-scope, integration boundaries)
- T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests
- T06: docs/nats-task-ingestion.md design stub

Closes ISSC-WP-0001.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:16:27 +02:00

51 lines
1.4 KiB
Python

"""
Pydantic schemas for the issue-core REST API.
The TaskIngestionRequest schema matches activity-core's IssueSink TaskSpec
payload exactly. See:
- SCOPE.md "TaskSpec payload" section
- activity-core docs/adr/adr-001-event-bridge-architecture.md
"""
from typing import List, Literal, Optional
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field
SourceType = Literal["rule", "instruction"]
Priority = Literal["high", "medium", "low"]
BackendName = Literal["gitea", "sqlite", "github"]
class TaskIngestionRequest(BaseModel):
"""TaskSpec payload from activity-core's IssueSink (POST /issues/)."""
model_config = ConfigDict(extra="forbid")
title: str = Field(..., min_length=1, max_length=500)
description: str = ""
target_repo: str = Field(..., min_length=1)
priority: Priority = "medium"
labels: List[str] = Field(default_factory=list)
due_in_days: Optional[int] = Field(default=None, ge=0)
source_type: SourceType
source_id: str = Field(..., min_length=1)
triggering_event_id: UUID
activity_definition_id: str = Field(..., min_length=1)
class TaskIngestionResponse(BaseModel):
"""Response returned to the emitter after a successful ingestion."""
issue_id: str
issue_url: Optional[str] = None
backend: BackendName
class ErrorResponse(BaseModel):
"""Uniform error envelope."""
error: str
detail: Optional[str] = None