chore: import upstream snapshot with attribution
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "jcode-task-types"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -0,0 +1,281 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GoalScope {
|
||||
Global,
|
||||
#[default]
|
||||
Project,
|
||||
}
|
||||
|
||||
impl GoalScope {
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"global" => Some(Self::Global),
|
||||
"project" => Some(Self::Project),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Global => "global",
|
||||
Self::Project => "project",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GoalStatus {
|
||||
Draft,
|
||||
#[default]
|
||||
Active,
|
||||
Paused,
|
||||
Blocked,
|
||||
Completed,
|
||||
Archived,
|
||||
Abandoned,
|
||||
}
|
||||
|
||||
impl GoalStatus {
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"draft" => Some(Self::Draft),
|
||||
"active" => Some(Self::Active),
|
||||
"paused" => Some(Self::Paused),
|
||||
"blocked" => Some(Self::Blocked),
|
||||
"completed" => Some(Self::Completed),
|
||||
"archived" => Some(Self::Archived),
|
||||
"abandoned" => Some(Self::Abandoned),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Draft => "draft",
|
||||
Self::Active => "active",
|
||||
Self::Paused => "paused",
|
||||
Self::Blocked => "blocked",
|
||||
Self::Completed => "completed",
|
||||
Self::Archived => "archived",
|
||||
Self::Abandoned => "abandoned",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort_rank(self) -> u8 {
|
||||
match self {
|
||||
Self::Active => 0,
|
||||
Self::Blocked => 1,
|
||||
Self::Draft => 2,
|
||||
Self::Paused => 3,
|
||||
Self::Completed => 4,
|
||||
Self::Archived => 5,
|
||||
Self::Abandoned => 6,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_resumable(self) -> bool {
|
||||
matches!(self, Self::Active | Self::Blocked | Self::Draft)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
pub struct GoalStep {
|
||||
pub id: String,
|
||||
pub content: String,
|
||||
#[serde(default = "default_pending_status")]
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
pub struct GoalMilestone {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
#[serde(default = "default_pending_status")]
|
||||
pub status: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub steps: Vec<GoalStep>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GoalUpdate {
|
||||
pub at: DateTime<Utc>,
|
||||
pub summary: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Goal {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
#[serde(default)]
|
||||
pub scope: GoalScope,
|
||||
#[serde(default)]
|
||||
pub status: GoalStatus,
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
#[serde(default)]
|
||||
pub why: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub success_criteria: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub milestones: Vec<GoalMilestone>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub next_steps: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blockers: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub current_milestone_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub progress_percent: Option<u8>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub updates: Vec<GoalUpdate>,
|
||||
}
|
||||
|
||||
impl Goal {
|
||||
pub fn new(title: &str, scope: GoalScope) -> Self {
|
||||
let now = Utc::now();
|
||||
let trimmed = title.trim();
|
||||
Self {
|
||||
id: sanitize_goal_id(trimmed),
|
||||
title: trimmed.to_string(),
|
||||
scope,
|
||||
status: GoalStatus::Active,
|
||||
description: String::new(),
|
||||
why: String::new(),
|
||||
success_criteria: Vec::new(),
|
||||
milestones: Vec::new(),
|
||||
next_steps: Vec::new(),
|
||||
blockers: Vec::new(),
|
||||
current_milestone_id: None,
|
||||
progress_percent: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
updates: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_milestone(&self) -> Option<&GoalMilestone> {
|
||||
let current_id = self.current_milestone_id.as_deref()?;
|
||||
self.milestones.iter().find(|m| m.id == current_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sanitize_goal_id(id: &str) -> String {
|
||||
let slug = slugify(id);
|
||||
if slug.is_empty() {
|
||||
"goal".to_string()
|
||||
} else {
|
||||
slug
|
||||
}
|
||||
}
|
||||
|
||||
fn slugify(input: &str) -> String {
|
||||
let mut slug = String::new();
|
||||
let mut prev_dash = false;
|
||||
for ch in input.chars() {
|
||||
let lower = ch.to_ascii_lowercase();
|
||||
if lower.is_ascii_alphanumeric() {
|
||||
slug.push(lower);
|
||||
prev_dash = false;
|
||||
} else if !prev_dash {
|
||||
slug.push('-');
|
||||
prev_dash = true;
|
||||
}
|
||||
}
|
||||
slug.trim_matches('-').to_string()
|
||||
}
|
||||
|
||||
fn default_pending_status() -> String {
|
||||
"pending".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct TodoItem {
|
||||
pub content: String,
|
||||
pub status: String,
|
||||
pub priority: String,
|
||||
pub id: String,
|
||||
/// Optional group label. Todos that share a group are displayed together
|
||||
/// under a single header. Use one group per coherent goal; when work is
|
||||
/// steered into a new area, start a new group instead of renaming.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub group: Option<String>,
|
||||
/// Forward-looking confidence, from 0-100, that this todo can be completed correctly.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub confidence: Option<u8>,
|
||||
/// Confidence, from 0-100, recorded when the todo is marked completed.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub completion_confidence: Option<u8>,
|
||||
/// Every distinct confidence value this todo has carried, oldest first,
|
||||
/// ending with the current one. Maintained by the todo tool (not the
|
||||
/// model): the first entry is the planning-time confidence, later entries
|
||||
/// record how the assessment evolved while the item was worked on. This
|
||||
/// preserves the planning signal even after the model overwrites
|
||||
/// `confidence` when marking the item done.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub confidence_history: Vec<u8>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_by: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub assigned_to: Option<String>,
|
||||
}
|
||||
|
||||
/// A goal-level assessment attached to a todo group (or, for an ungrouped
|
||||
/// flat list, the whole list as one implicit goal with `group: None`).
|
||||
///
|
||||
/// Hill-climbability is a property of an objective, not of individual steps:
|
||||
/// "optimize grep latency" is hill-climbable because progress has a metric,
|
||||
/// while "design an onboarding screen" is not because success is a taste
|
||||
/// judgment. Items like "read the auth code" have no meaningful score of
|
||||
/// their own, so the score lives here instead of on `TodoItem`.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct TodoGoal {
|
||||
/// Group label this goal describes. `None` covers the ungrouped list.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub group: Option<String>,
|
||||
/// How hill-climbable this goal is, from 0-100: can progress be measured
|
||||
/// against a quantifiable, verifiable objective and iterated on?
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub hill_climbability: Option<u8>,
|
||||
/// The measurable objective progress is climbing toward, when one exists
|
||||
/// (e.g. "p50 grep latency under 50ms on the repo corpus"). A stated
|
||||
/// objective is what makes a high score credible.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub objective: Option<String>,
|
||||
/// The concrete feedback loop used to judge whether each iteration improves
|
||||
/// the outcome (e.g. a benchmark command and the metric it reports).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub feedback_loop: Option<String>,
|
||||
/// How completely the agent owned the goal's full outcome, including the
|
||||
/// requested work, reasonably necessary adjacent work, end-to-end
|
||||
/// validation, cleanup, and explicit disclosure of remaining gaps.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub end_to_end_ownership: Option<u8>,
|
||||
}
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PersistedCatchupState {
|
||||
#[serde(default)]
|
||||
pub seen_at_ms_by_session: HashMap<String, i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CatchupBrief {
|
||||
pub reason: String,
|
||||
pub tags: Vec<String>,
|
||||
pub last_user_prompt: Option<String>,
|
||||
pub activity_steps: Vec<String>,
|
||||
pub files_touched: Vec<String>,
|
||||
pub tool_counts: Vec<(String, usize)>,
|
||||
pub validation_notes: Vec<String>,
|
||||
pub latest_agent_response: Option<String>,
|
||||
pub needs_from_user: String,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
Reference in New Issue
Block a user