mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-14 08:04:32 +00:00
One hash-chained append-only log; board, journal, and per-agent deliveries are projections (rebuild == replay). Six board verbs + journal verbs with role authority; worker slice and case access ride assignment.
78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
"""Work-item model for agent teams — states, actors, links, errors.
|
|
|
|
The board is not a database of record: it is a projection of the append-only team
|
|
event log (see teams.store). These are the shapes the projection folds into, and the
|
|
rules the verbs enforce. Deliberately minimal — no sprints, estimates, priorities, or
|
|
custom fields; anyone needing those graduates to a real tracker via connectors.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
|
|
class ItemState(str, Enum):
|
|
PROPOSED = "proposed"
|
|
APPROVED = "approved"
|
|
IN_PROGRESS = "in_progress"
|
|
BLOCKED = "blocked"
|
|
REVIEW = "review"
|
|
DONE = "done"
|
|
CANCELED = "canceled"
|
|
|
|
|
|
# Legal edges of the state machine. Approval and done carry extra authority rules
|
|
# (see TeamStore.transition): proposed→approved is the human decomposition gate,
|
|
# review→done is the lead's verification gate. canceled→approved is reopen.
|
|
EDGES: dict[ItemState, set[ItemState]] = {
|
|
ItemState.PROPOSED: {ItemState.APPROVED, ItemState.CANCELED},
|
|
ItemState.APPROVED: {ItemState.IN_PROGRESS, ItemState.CANCELED},
|
|
ItemState.IN_PROGRESS: {ItemState.BLOCKED, ItemState.REVIEW, ItemState.CANCELED},
|
|
ItemState.BLOCKED: {ItemState.IN_PROGRESS, ItemState.CANCELED},
|
|
ItemState.REVIEW: {ItemState.DONE, ItemState.IN_PROGRESS, ItemState.CANCELED},
|
|
ItemState.DONE: set(),
|
|
ItemState.CANCELED: {ItemState.APPROVED},
|
|
}
|
|
|
|
# Targets a worker may move its OWN item to. Workers never approve, never close:
|
|
# done is the lead's verdict at review, cancel is a lead/user board decision.
|
|
WORKER_TARGETS = {ItemState.IN_PROGRESS, ItemState.BLOCKED, ItemState.REVIEW}
|
|
|
|
|
|
class Role(str, Enum):
|
|
USER = "user"
|
|
LEAD = "lead"
|
|
WORKER = "worker"
|
|
SYSTEM = "system"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Actor:
|
|
"""Who is speaking to the board. `id` is the agent instance id ("user" for the
|
|
human); role decides verb authority — the capability firebreak in data form."""
|
|
|
|
id: str
|
|
role: Role
|
|
persona: str = ""
|
|
model: str = ""
|
|
session_id: str = ""
|
|
|
|
|
|
LINK_KINDS = ("parent", "blocks") # link(src, "parent", dst): dst is src's parent
|
|
# link(src, "blocks", dst): src blocks dst
|
|
|
|
JOURNAL_KINDS = ("finding", "evidence", "decision", "note")
|
|
|
|
|
|
class BoardError(Exception):
|
|
"""A verb call the board refuses — illegal transition, missing item, bad input."""
|
|
|
|
|
|
class AuthorityError(BoardError):
|
|
"""The actor's role does not permit this verb on this item."""
|
|
|
|
|
|
class ChainError(Exception):
|
|
"""Hash-chain verification failed — the log was modified out of band."""
|