sirenamc/stackline

Stackline, a team task board you run from the web or from Claude/ChatGPT. FastAPI + DataK3 + appid + Models + MCP.

A remix template · 0 remixes · Remix sirenamc/stackline

app/boards/application/ports.py

"""What the use cases need from the outside world. infrastructure/ provides it; tests fake it."""
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Protocol

from boards.domain.model import Board, BoardList, Card, Role


@dataclass(frozen=True)
class Event:
    """One line of the activity log: who did what, to which card, and what changed."""
    board_id: str
    card_number: Optional[int]
    actor: str
    action: str                  # "created", "moved", "edited", "assigned", "member added", ...
    field: Optional[str] = None
    before: Optional[str] = None
    after: Optional[str] = None
    at: Optional[datetime] = None


class BoardRepository(Protocol):
    def get(self, board_id: str) -> Optional[Board]: ...
    def boards_for(self, email: str) -> list: ...
    # Each write touches only what it names. Members are never written as a side effect of
    # saving a board, so a stale copy of a board can't bring back someone the owner removed.
    def save_board(self, board: Board) -> None: ...  # title and owner only
    def save_list(self, board_id: str, lst: BoardList) -> None: ...
    def save_membership(self, board_id: str, email: str, role: Role) -> None: ...
    def remove_membership(self, board_id: str, email: str) -> None: ...
    def get_card(self, number: int) -> Optional[Card]: ...
    def cards_on(self, board_id: str) -> list: ...
    def insert_card(self, card: Card) -> Card: ...  # under a number never used before
    def save_card(self, card: Card) -> None: ...
    def delete_card(self, number: int) -> None: ...
    def delete_board(self, board_id: str) -> None: ...  # cards, lists, people, log; numbers stay claimed
    def log(self, event: Event) -> None: ...
    def history(self, board_id: str, card_number: int) -> list: ...
    def activity(self, board_id: str, limit: int) -> list: ...
    def save_embedding(self, number: int, text_hash: str, vector: list) -> None: ...
    def embeddings(self, board_ids: list) -> dict: ...  # {card number: (text_hash, vector)}


class Assistant(Protocol):
    def checklist(self, title: str, description: str) -> list: ...
    def summary(self, facts: str) -> str: ...
    def embed(self, texts: list) -> list: ...
    def plan(self, goal: str, deadline: str, today: str) -> list: ...  # steps for set_up_board