supercode-interchange 0.4.19

Canonical, provider-neutral session interchange primitives for Supercode
Documentation
//! The workflow layer: what a harness's board owes and where each piece stands — Board, Task,
//! Lane, Dependency, Attempt, Handoff, Review (`docs/ONTOLOGY.md` §2.8). It sits above
//! orchestration: a task names the profile that works it, and an attempt is one session's run
//! at it. Lower layers never import this one.
pub mod codec;

use std::collections::BTreeMap;
use std::path::PathBuf;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::ontology::Residue;

/// Where a task stands. The lanes are the board's own (Hermes Kanban names them); every
/// harness's board maps onto them, and a lane the model does not know is `unknown` with the
/// source word in the task's residue.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Lane {
    /// Filed, not yet accepted onto the board.
    Triage,
    /// Accepted, waiting on a dependency.
    Todo,
    /// Parked until a time.
    Scheduled,
    /// Claimable by its assignee.
    Ready,
    /// Claimed and being worked.
    Running,
    /// Stopped on something a human decides.
    Blocked,
    /// Handed off, waiting for a reviewer's verdict.
    Review,
    /// Finished.
    Done,
    /// Kept for the record only.
    Archived,
    /// A lane this model does not name.
    #[serde(other)]
    Unknown,
}

impl Lane {
    /// The lane a board's status word names.
    pub fn parse(word: &str) -> Self {
        match word {
            "triage" => Self::Triage,
            "todo" => Self::Todo,
            "scheduled" => Self::Scheduled,
            "ready" => Self::Ready,
            "running" => Self::Running,
            "blocked" => Self::Blocked,
            "review" => Self::Review,
            "done" => Self::Done,
            "archived" => Self::Archived,
            _ => Self::Unknown,
        }
    }
}

/// What kind of directory a task is worked in.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum WorkspaceKind {
    /// A fresh directory, deleted when the task completes.
    Scratch,
    /// An existing directory, kept.
    Dir,
    /// A git worktree of the project, kept.
    Worktree,
    /// A kind this model does not name.
    #[serde(other)]
    Unknown,
}

/// Where a task is worked.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Workspace {
    /// The kind.
    pub kind: WorkspaceKind,
    /// The directory, when pinned or known.
    #[serde(default)]
    pub path: Option<String>,
    /// The branch a worktree is on.
    #[serde(default)]
    pub branch: Option<String>,
}

/// `parent` must be done before `child` is ready.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Dependency {
    /// The task that goes first.
    pub parent: String,
    /// The task that waits.
    pub child: String,
}

/// What an attempt handed to the next reader: the closeout in prose and the evidence as data.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Handoff {
    /// The human-readable closeout.
    #[serde(default)]
    pub summary: Option<String>,
    /// The machine-readable evidence (changed files, verification, residual risk), as given.
    #[serde(default)]
    pub metadata: Option<Value>,
}

/// A reviewer's verdict on a handoff.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Verdict {
    /// The implementer asked for review.
    Requested,
    /// The reviewer accepted the handoff.
    Approved,
    /// The reviewer sent it back to the implementer.
    ChangesRequested,
    /// The reviewer raised it to a human.
    Escalated,
}

/// One review step on a task.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Review {
    /// The verdict.
    pub verdict: Verdict,
    /// The profile that gave it.
    #[serde(default)]
    pub by: Option<String>,
    /// Why, in the reviewer's words.
    #[serde(default)]
    pub reason: Option<String>,
    /// When, RFC3339.
    #[serde(default)]
    pub at: Option<String>,
}

/// One run at a task: a profile claimed it, worked it in a session, and ended with a handoff,
/// a block, or an error.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Attempt {
    /// The id, unique on the board.
    pub id: String,
    /// The profile that ran it.
    #[serde(default)]
    pub profile: Option<String>,
    /// The workflow step it ran, when the task has steps.
    #[serde(default)]
    pub step: Option<String>,
    /// The board's status word for the run.
    pub status: String,
    /// Start, RFC3339.
    #[serde(default)]
    pub started_at: Option<String>,
    /// End, RFC3339, when ended.
    #[serde(default)]
    pub ended_at: Option<String>,
    /// How it ended, in the board's words.
    #[serde(default)]
    pub outcome: Option<String>,
    /// What it handed off.
    #[serde(default)]
    pub handoff: Option<Handoff>,
    /// The error, when it failed.
    #[serde(default)]
    pub error: Option<String>,
}

/// A note on the task's thread: the inter-agent protocol, read by every later attempt.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Comment {
    /// Who wrote it (a profile, or a human).
    pub author: String,
    /// The note.
    pub body: String,
    /// When, RFC3339.
    #[serde(default)]
    pub at: Option<String>,
}

/// One task on a board.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Task {
    /// The id (the board's own token).
    pub id: String,
    /// The title.
    pub title: String,
    /// The body: the brief, and the acceptance where the board treats it as such.
    #[serde(default)]
    pub body: Option<String>,
    /// The profile that works it.
    #[serde(default)]
    pub assignee: Option<String>,
    /// Where it stands.
    pub lane: Lane,
    /// Higher first.
    #[serde(default)]
    pub priority: i64,
    /// A namespace within the board.
    #[serde(default)]
    pub tenant: Option<String>,
    /// The key automation filed it under, so a retry finds it instead of duplicating it.
    #[serde(default)]
    pub idempotency_key: Option<String>,
    /// Where it is worked.
    pub workspace: Workspace,
    /// Skills pinned to it beyond the assignee's own.
    #[serde(default)]
    pub skills: Vec<String>,
    /// Model override.
    #[serde(default)]
    pub model: Option<String>,
    /// Provider override.
    #[serde(default)]
    pub provider: Option<String>,
    /// Who filed it (a profile, or a human).
    #[serde(default)]
    pub created_by: Option<String>,
    /// Filed, RFC3339.
    #[serde(default)]
    pub created_at: Option<String>,
    /// First claimed, RFC3339.
    #[serde(default)]
    pub started_at: Option<String>,
    /// Done, RFC3339.
    #[serde(default)]
    pub completed_at: Option<String>,
    /// The result recorded on completion, in the board's words.
    #[serde(default)]
    pub result: Option<String>,
    /// Every run at it, oldest first.
    #[serde(default)]
    pub attempts: Vec<Attempt>,
    /// Every review step, oldest first.
    #[serde(default)]
    pub reviews: Vec<Review>,
    /// The thread, oldest first.
    #[serde(default)]
    pub comments: Vec<Comment>,
    /// Source fields the record does not model, verbatim.
    #[serde(default)]
    pub residue: Residue,
}

/// One board: a queue of tasks with its own store, workspaces and dispatcher.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Board {
    /// The slug (the board's directory name).
    pub slug: String,
    /// The display name, when the board has one.
    #[serde(default)]
    pub name: Option<String>,
    /// The board's directory.
    pub root: PathBuf,
    /// The tasks, by id.
    pub tasks: BTreeMap<String, Task>,
    /// The dependency edges.
    #[serde(default)]
    pub dependencies: Vec<Dependency>,
}

/// A home's boards.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Workflow {
    /// The home folder.
    pub root: PathBuf,
    /// `default` and the named boards.
    pub boards: BTreeMap<String, Board>,
}