damien-kaiser/taskloft

Taskloft — team task boards (owner / member / viewer), card history, my plate, overdue, blockers, AI checklists and summaries, usable from Claude and ChatGPT over MCP.

A remix template · 0 remixes · Remix damien-kaiser/taskloft

app/boards/application/ports.py

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

from boards.domain.model import Board, Card


class BoardRepository(Protocol):
    # boards, people and lists
    def get(self, board_id: str) -> Optional[Board]: ...
    def boards_for(self, email: str) -> list: ...
    def save_board(self, board: Board) -> None: ...
    def remove_member(self, board_id: str, email: str) -> None: ...
    # cards
    def claim_number(self, card_id: str) -> int: ...          # next free card number, claimed once
    def get_card(self, card_id: str) -> Optional[Card]: ...
    def card_by_number(self, number: int) -> Optional[Card]: ...
    def cards_on(self, board_id: str) -> list: ...
    def save_card(self, card: Card) -> None: ...
    # blockers and checklist
    def set_blocker(self, card_id: str, blocked_by: str, on: bool) -> None: ...
    def checklist(self, card_id: str) -> list: ...
    def save_checklist_item(self, item: dict) -> None: ...
    # history
    def log(self, board_id: str, card_id: Optional[str], actor: str, action: str,
            changes: Optional[dict] = None) -> None: ...
    def history(self, board_id: str, card_id: Optional[str] = None, limit: int = 50) -> list: ...
    # daily summaries, cached by the facts they were written from
    def cached_summary(self, key: str) -> Optional[str]: ...
    def save_summary(self, key: str, board_id: str, text: str) -> None: ...
    # meaning (vectors of each card's text, for "find cards like this one")
    def vector_hashes(self, board_ids: list) -> dict: ...                 # card_id -> hash of the text embedded
    def save_vector(self, card_id: str, board_id: str, text_hash: str, vector: list) -> None: ...
    def nearest(self, board_ids: list, vector: list, k: int, exclude: Optional[str] = None) -> list: ...  # [(card_id, distance)]


class Assistant(Protocol):
    def checklist(self, card_title: str, description: str = "") -> list: ...
    def summary(self, board_title: str, facts: str) -> str: ...
    def embed(self, texts: list) -> list: ...                             # one vector per text