Docs

issue

Source: crates/lvcs-doc/src/kinds/issue/mod.rs.

/// An issue's id: the `HASH` of the canonical CBOR array `[ISSUE_DOMAIN,
/// creator key, n]` (the key as a byte string, `n` an integer), `n` being
/// the creator key's own count of the issues it created, from 1. Written
/// in `ID_ENCODING`; shown as `<alias>/<n>` (decision 304).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct IssueId(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);

/// How an `/assign` record changes who holds an issue or a task.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AssignOp {
    /// `by` takes it for itself (`--add-assignee @me`): at once when it has
    /// no assignee, else unless an assignee holds the claim within its
    /// window.
    Take,
    /// `who` is added at once: open for `who` = `by`, else the assign grant.
    Assign,
    /// `who` is removed at once: open for `who` = `by`, else the assign
    /// grant.
    Unassign,
}

/// One `/assign` record (decision 304). Read in `(time_ms, by)` order.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AssignRecord {
    pub by: ParticipantKey,
    pub who: ParticipantKey,
    pub op: AssignOp,
    /// The plan task's id, or `None` for the issue itself.
    pub task: Option<String>,
    pub time_ms: i64,
    /// The claimer's wait window, so every peer judges a hold alike.
    pub window_ms: u64,
    /// A `take` of a held issue or task: the thread address and message id
    /// of the ask it sent its holders, which they answer.
    pub ask: Option<(String, String)>,
}

/// One `/holds` record: an assignee keeps the issue or task a claim asked
/// for (decision 304).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HoldRecord {
    pub by: ParticipantKey,
    /// The held claim's id ([`AssignRecord::id`]), in `ID_ENCODING`.
    pub claim: String,
    pub time_ms: i64,
    pub reason: String,
}

/// One `/links` record: a range linked by hand, at the file's heads then.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LinkRecord {
    pub by: ParticipantKey,
    /// The plan task this links, by id, or `None` for the issue (decision
    /// 313: a task's latest link is its code range).
    pub task: Option<String>,
    pub address: String,
    /// The lines, as `LineName` writes them (`L3-L9`).
    pub lines: String,
    #[serde(with = "crate::cbor::heads")]
    pub heads: Vec<ChangeHash>,
    pub time_ms: i64,
}

/// One `/progress` record (decision 313): a structured checkpoint of the
/// work, at the frontier main's mirror showed when it was written.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckpointRecord {
    pub by: ParticipantKey,
    /// The frontier's id, in `ID_ENCODING`.
    pub frontier: String,
    pub done: Vec<String>,
    pub next: Vec<String>,
    pub blockers: Vec<String>,
    pub open_questions: Vec<String>,
    pub time_ms: i64,
}

/// Why an issue was closed by hand.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CloseReason {
    Wontfix,
    Duplicate,
}

/// One `/closes` record: a hand close or reopen, the latest by
/// `(time_ms, by)` deciding.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CloseRecord {
    pub by: ParticipantKey,
    /// `None` reopens.
    pub reason: Option<CloseReason>,
    pub time_ms: i64,
}

/// One operation of an [`IssueWrite`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueOp {
    /// Create the issue: the first operation of its first write, alone in
    /// it. `/meta` gets `creator` (the change's signer), `n`, `created_ms`
    /// (the origin's time), `title`, `body`, and `thread`, and `/labels`
    /// the labels.
    Create {
        n: u64,
        title: String,
        body: String,
        labels: BTreeSet<String>,
        /// The issue's thread's address.
        thread: String,
    },
    Title(String),
    Body(String),
    /// Add (`on`) or remove a label.
    Label {
        name: String,
        on: bool,
    },
    Assign(AssignRecord),
    Hold(HoldRecord),
    /// Append a plan task, unchecked; its id is the `HASH` of `(by, time_ms,
    /// text)` in `ID_ENCODING`.
    AddTask {
        text: String,
    },
    /// Check or uncheck the task with this id.
    Done {
        task: String,
        done: bool,
    },
    Link(LinkRecord),
    /// `by`'s latest heads of a document it changed while the issue was
    /// its current issue: `/work/<by>:<address>`.
    Work {
        address: String,
        #[serde(with = "crate::cbor::heads")]
        heads: Vec<ChangeHash>,
    },
    /// A workspace bound to the issue (`lvcs issue develop`):
    /// `/workspaces/<by>:<workspace id>` = its name.
    Workspace {
        id: String,
        name: String,
    },
    Close(CloseRecord),
    Checkpoint(CheckpointRecord),
}

/// Write `ops` to an issue as one change by `origin.author` at
/// `origin.time_ms / 1000` seconds (decision 304). A write holding
/// `Create` applies only to an issue not yet created, any other only to a
/// created one; the document ignores the rest.
///
/// Layout: genesis puts `/meta`, `/labels`, `/work`, and `/workspaces` as
/// maps and `/assign`, `/holds`, `/plan`, `/links`, `/closes`, and
/// `/progress` as lists.
/// A record is a map appended to its list: keys as the record's fields,
/// keys and ids in `ID_ENCODING`, heads a list of lowercase hex hashes,
/// enums in snake case, and `time_ms`, `window_ms`, and `n` integers. A
/// plan task is `{ id, by, text, time_ms, done }`, `done` a boolean.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IssueWrite {
    pub origin: Origin,
    pub ops: Vec<IssueOp>,
}