using InvercargillSql.Orm; namespace ClankerSaddle.Data { /** * A software project worked on by saddle agents, stored in the * `projects` table. */ public class Project : Object { public int64 id { get; set; } public string name { get; set; default = ""; } public string git_url { get; set; default = ""; } public string default_branch { get; set; default = "main"; } public string? summary { get; set; default = null; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("projects") .column("id", o => o.id, (o, v) => o.id = v) .column("name", o => o.name, (o, v) => o.name = v) .column("git_url", o => o.git_url, (o, v) => o.git_url = v) .column("default_branch", o => o.default_branch, (o, v) => o.default_branch = v) .column("summary", o => o.summary, (o, v) => o.summary = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * A directed relationship between two projects ('co-developed' or * 'reference'), stored in the `project_links` table. */ public class ProjectLink : Object { public int64 id { get; set; } public int64 parent_project_id { get; set; } public int64 linked_project_id { get; set; } public string relationship { get; set; default = "reference"; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("project_links") .column("id", o => o.id, (o, v) => o.id = v) .column("parent_project_id", o => o.parent_project_id, (o, v) => o.parent_project_id = v) .column("linked_project_id", o => o.linked_project_id, (o, v) => o.linked_project_id = v) .column("relationship", o => o.relationship, (o, v) => o.relationship = v); } } /** * A unit of work for a saddle agent: created in {@link * TaskService.STATUS_DRAFT}, queued by its creator (human or goal * agent), dispatched to a saddle, worked, possibly blocked on * questions, and eventually done or failed (completion is * agent-only). The plan-12 shape: goals own the task graph — a task * links its {@link goal_id}, runs as a plain worker or the goal * agent itself ({@link kind}), carries a work {@link mode} and its * {@link creator}, and human tasks may carry an auto-enqueue * predecessor ({@link enqueue_after_task_id}). Stored in the * `tasks` table. Named `TaskEntity` (not `Task`) to stay clear of * `GLib.Task` in files that import Gio transitively. */ public class TaskEntity : Object { public int64 id { get; set; } public int64 project_id { get; set; } public string title { get; set; default = ""; } public string? description { get; set; default = null; } public string status { get; set; default = "draft"; } public int64 priority { get; set; default = 5; } public string? working_branch { get; set; default = null; } public string? summary { get; set; default = null; } public string? task_branch { get; set; default = null; } public string? branch_from { get; set; default = null; } public DateTime? created_at { get; set; default = null; } public DateTime? started_at { get; set; default = null; } public DateTime? completed_at { get; set; default = null; } public DateTime? suspended_at { get; set; default = null; } /** The goal this task belongs to (0 = standalone). */ public int64 goal_id { get; set; default = 0; } /** "task" (a worker) or "goal_run" (the goal agent's own run). */ public string kind { get; set; default = "task"; } /** implement / investigate / review / test (orchestrate for goal runs). */ public string mode { get; set; default = "implement"; } /** "human" or "goal" — enqueueing is creator-only. */ public string creator { get; set; default = "human"; } /** Human-created tasks only: auto-enqueue when this predecessor is done. */ public int64 enqueue_after_task_id { get; set; default = 0; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("tasks") .column("id", o => o.id, (o, v) => o.id = v) .column("project_id", o => o.project_id, (o, v) => o.project_id = v) .column("title", o => o.title, (o, v) => o.title = v) .column("description", o => o.description, (o, v) => o.description = v) .column("status", o => o.status, (o, v) => o.status = v) .column("priority", o => o.priority, (o, v) => o.priority = v) .column("working_branch", o => o.working_branch, (o, v) => o.working_branch = v) .column("summary", o => o.summary, (o, v) => o.summary = v) .column("task_branch", o => o.task_branch, (o, v) => o.task_branch = v) .column("branch_from", o => o.branch_from, (o, v) => o.branch_from = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v) .column("started_at", o => o.started_at, (o, v) => o.started_at = v) .column("completed_at", o => o.completed_at, (o, v) => o.completed_at = v) .column("suspended_at", o => o.suspended_at, (o, v) => o.suspended_at = v) .column("goal_id", o => o.goal_id, (o, v) => o.goal_id = v ?? 0) .column("kind", o => o.kind, (o, v) => o.kind = v) .column("mode", o => o.mode, (o, v) => o.mode = v) .column("creator", o => o.creator, (o, v) => o.creator = v) .column("enqueue_after_task_id", o => o.enqueue_after_task_id, (o, v) => o.enqueue_after_task_id = v ?? 0); } } /** * One LLM-reported progress line on a task (`saddle progress-update`), * stored in the `task_progress` table. */ public class TaskProgress : Object { public int64 id { get; set; } public int64 task_id { get; set; } public string summary { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_progress") .column("id", o => o.id, (o, v) => o.id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("summary", o => o.summary, (o, v) => o.summary = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** A remark on a task, authored either by a human or by the agent ('agent'). */ public class TaskComment : Object { public int64 id { get; set; } public int64 task_id { get; set; } public string author { get; set; default = "agent"; } public string body { get; set; default = ""; } public string? quote { get; set; default = null; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_comments") .column("id", o => o.id, (o, v) => o.id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("author", o => o.author, (o, v) => o.author = v) .column("body", o => o.body, (o, v) => o.body = v) .column("quote", o => o.quote, (o, v) => o.quote = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * A blocking prompt raised by an agent mid-task — a multiple-choice * {@link block_type} question, a request for comment or a review * request — answered by a human in the web UI; the answer is collected * by the saddle on its next * {@link ClankerSaddle.Api.SaddlePollEndpoint} poll. */ public class TaskBlock : Object { public int64 id { get; set; } public int64 task_id { get; set; } public string question { get; set; default = ""; } public string? answer { get; set; default = null; } public string block_type { get; set; default = "question"; } public string? options { get; set; default = null; } public string? body { get; set; default = null; } public DateTime? answered_at { get; set; default = null; } public string? answered_by { get; set; default = null; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_blocks") .column("id", o => o.id, (o, v) => o.id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("question", o => o.question, (o, v) => o.question = v) .column("answer", o => o.answer, (o, v) => o.answer = v) .column("type", o => o.block_type, (o, v) => o.block_type = v) .column("options", o => o.options, (o, v) => o.options = v) .column("body", o => o.body, (o, v) => o.body = v) .column("answered_at", o => o.answered_at, (o, v) => o.answered_at = v) .column("answered_by", o => o.answered_by, (o, v) => o.answered_by = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * A saddle container registered with this app: one daemon managing one * bash session at a time, controlled over HTTP at {@link host}. The * per-saddle token ({@link token}, plan 10) authorises every * daemon→web call; removing the row revokes it. */ public class Saddle : Object { public int64 id { get; set; } public string name { get; set; default = ""; } public string? container_id { get; set; default = null; } public string host { get; set; default = ""; } public string status { get; set; default = "offline"; } public int64 current_task_id { get; set; default = 0; } public DateTime? allocated_at { get; set; default = null; } public string? git_name { get; set; default = null; } public string? git_email { get; set; default = null; } public string? ssh_public_key { get; set; default = null; } public string? token { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("saddles") .column("id", o => o.id, (o, v) => o.id = v) .column("name", o => o.name, (o, v) => o.name = v) .column("container_id", o => o.container_id, (o, v) => o.container_id = v) .column("host", o => o.host, (o, v) => o.host = v) .column("status", o => o.status, (o, v) => o.status = v) .column("current_task_id", o => o.current_task_id, (o, v) => o.current_task_id = v) .column("allocated_at", o => o.allocated_at, (o, v) => o.allocated_at = v) .column("git_name", o => o.git_name, (o, v) => o.git_name = v) .column("git_email", o => o.git_email, (o, v) => o.git_email = v) .column("ssh_public_key", o => o.ssh_public_key, (o, v) => o.ssh_public_key = v) .column("token", o => o.token, (o, v) => o.token = v); } } /** * One run of a task on a saddle: opened when the dispatcher assigns * the task, closed with an outcome when the session ends. */ public class Session : Object { public int64 id { get; set; } public int64 task_id { get; set; } public int64 saddle_id { get; set; } public DateTime? started_at { get; set; default = null; } public DateTime? ended_at { get; set; default = null; } public string? outcome { get; set; default = null; } /** * What a resumed session's agent first received (plan 14 B4): * the resume_notice the allocation built, stored on the row at * resume-allocation; null for fresh sessions. */ public string? resume_notice { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("sessions") .column("id", o => o.id, (o, v) => o.id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("saddle_id", o => o.saddle_id, (o, v) => o.saddle_id = v) .column("started_at", o => o.started_at, (o, v) => o.started_at = v) .column("ended_at", o => o.ended_at, (o, v) => o.ended_at = v) .column("outcome", o => o.outcome, (o, v) => o.outcome = v) .column("resume_notice", o => o.resume_notice, (o, v) => o.resume_notice = v); } } /** * One daemon-global entry id already ingested for a saddle (plan * 13 B1): the (saddle, entry id) pair is the replay-integrity key. * An outbox redelivery under ANY session id — a web restart, session * churn, a mis-keyed send — finds its row already here and is * consumed silently; the pair survives daemon restarts because it * lives in the database, not in memory. */ public class SaddleEntryDedup : Object { public int64 id { get; set; } public int64 saddle_id { get; set; } public int64 entry_id { get; set; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("saddle_entry_dedup") .column("id", o => o.id, (o, v) => o.id = v) .column("saddle_id", o => o.saddle_id, (o, v) => o.saddle_id = v) .column("entry_id", o => o.entry_id, (o, v) => o.entry_id = v); } } /** * A numbered entry captured during a {@link Session} — a bash * command/output pair or a conversation-level event (comment, block, * progress, review, plan, user-comment) — stored as the saddle daemon * reports it. */ public class SessionCommand : Object { public int64 id { get; set; } public int64 session_id { get; set; } public string command_text { get; set; default = ""; } public string? output_text { get; set; default = null; } public int64 exit_code { get; set; default = 0; } public string entry_type { get; set; default = "command"; } public string? piped_input { get; set; default = null; } public DateTime? started_at { get; set; default = null; } public DateTime? completed_at { get; set; default = null; } public int64 duration_ms { get; set; default = 0; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("session_commands") .column("id", o => o.id, (o, v) => o.id = v) .column("session_id", o => o.session_id, (o, v) => o.session_id = v) .column("command_text", o => o.command_text, (o, v) => o.command_text = v) .column("output_text", o => o.output_text, (o, v) => o.output_text = v) .column("exit_code", o => o.exit_code, (o, v) => o.exit_code = v) .column("type", o => o.entry_type, (o, v) => o.entry_type = v) .column("piped_input", o => o.piped_input, (o, v) => o.piped_input = v) .column("started_at", o => o.started_at, (o, v) => o.started_at = v) .column("completed_at", o => o.completed_at, (o, v) => o.completed_at = v) .column("duration_ms", o => o.duration_ms, (o, v) => o.duration_ms = v); } } /** * A reusable instruction the dispatched agent must follow, stored in * the `rules` table and attached to projects and/or tasks. * `updated_at` stamps the last body/name edit (null until edited) * — the resume-time context_update delta keys on it. */ public class Rule : Object { public int64 id { get; set; } public string name { get; set; default = ""; } public string body { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public DateTime? updated_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("rules") .column("id", o => o.id, (o, v) => o.id = v) .column("name", o => o.name, (o, v) => o.name = v) .column("body", o => o.body, (o, v) => o.body = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v) .column("updated_at", o => o.updated_at, (o, v) => o.updated_at = v); } } /** An attachment of a rule to a project (`rule_projects`). */ public class RuleProject : Object { public int64 id { get; set; } public int64 rule_id { get; set; } public int64 project_id { get; set; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("rule_projects") .column("id", o => o.id, (o, v) => o.id = v) .column("rule_id", o => o.rule_id, (o, v) => o.rule_id = v) .column("project_id", o => o.project_id, (o, v) => o.project_id = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** An attachment of a rule to a task (`task_rules`). */ public class TaskRule : Object { public int64 id { get; set; } public int64 rule_id { get; set; } public int64 task_id { get; set; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_rules") .column("id", o => o.id, (o, v) => o.id = v) .column("rule_id", o => o.rule_id, (o, v) => o.rule_id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * An LLM-authored remark about a project (`saddle note` via the * callback endpoint), curated from the project page. The source task * is null for notes without one. */ public class ProjectNote : Object { public int64 id { get; set; } public int64 project_id { get; set; } public int64 source_task_id { get; set; default = 0; } public string body { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("project_notes") .column("id", o => o.id, (o, v) => o.id = v) .column("project_id", o => o.project_id, (o, v) => o.project_id = v) .column("source_task_id", o => o.source_task_id, (o, v) => o.source_task_id = v ?? 0) .column("body", o => o.body, (o, v) => o.body = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * One raw PTY output chunk of a {@link Session}, pulled from the * daemon's `GET /log` ring buffer while the session is active * (`session_raw_log`); `seq` is the daemon's monotonic chunk * sequence number. */ public class SessionRawLog : Object { public int64 id { get; set; } public int64 session_id { get; set; } public int64 seq { get; set; } public string chunk { get; set; default = ""; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("session_raw_log") .column("id", o => o.id, (o, v) => o.id = v) .column("session_id", o => o.session_id, (o, v) => o.session_id = v) .column("seq", o => o.seq, (o, v) => o.seq = v) .column("chunk", o => o.chunk, (o, v) => o.chunk = v); } } /** * A named tag used for dispatch routing (`capabilities`): saddles * declare the tags they carry, tasks the tags they require. */ public class Capability : Object { public int64 id { get; set; } public string name { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("capabilities") .column("id", o => o.id, (o, v) => o.id = v) .column("name", o => o.name, (o, v) => o.name = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** A tag declaration on a saddle (`saddle_capabilities`). */ public class SaddleCapability : Object { public int64 id { get; set; } public int64 capability_id { get; set; } public int64 saddle_id { get; set; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("saddle_capabilities") .column("id", o => o.id, (o, v) => o.id = v) .column("capability_id", o => o.capability_id, (o, v) => o.capability_id = v) .column("saddle_id", o => o.saddle_id, (o, v) => o.saddle_id = v); } } /** A required tag on a task (`task_capabilities`). */ public class TaskCapability : Object { public int64 id { get; set; } public int64 capability_id { get; set; } public int64 task_id { get; set; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_capabilities") .column("id", o => o.id, (o, v) => o.id = v) .column("capability_id", o => o.capability_id, (o, v) => o.capability_id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v); } } /** * A bearer token authorising one saddle daemon's calls against the * `/api/saddle/*` endpoints, generated at registration. */ public class ApiToken : Object { public int64 id { get; set; } public int64 saddle_id { get; set; } public string token { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("api_tokens") .column("id", o => o.id, (o, v) => o.id = v) .column("saddle_id", o => o.saddle_id, (o, v) => o.saddle_id = v) .column("token", o => o.token, (o, v) => o.token = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * A user's Web Push subscription endpoint (plan 11 task E shape): * `p256dh`/`auth` are the browser's application server keys; the * endpoint URL is unique per browser. `user_id` references the * Spry users table by value (no FK — the auth schema owns that * table). */ public class PushSubscription : Object { public int64 id { get; set; } public int64 user_id { get; set; } public string endpoint { get; set; default = ""; } public string p256dh { get; set; default = ""; } public string auth { get; set; default = ""; } public DateTime? created_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("push_subscriptions") .column("id", o => o.id, (o, v) => o.id = v) .column("user_id", o => o.user_id, (o, v) => o.user_id = v) .column("endpoint", o => o.endpoint, (o, v) => o.endpoint = v) .column("p256dh", o => o.p256dh, (o, v) => o.p256dh = v) .column("auth", o => o.auth, (o, v) => o.auth = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v); } } /** * A user's per-task "Notify me" opt-in (plan 11 task E shape): the * task's first start, failure and completion notify the opted-in * user; block events notify all push-subscribed users globally. */ public class TaskNotifyPref : Object { public int64 id { get; set; } public int64 task_id { get; set; } public int64 user_id { get; set; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("task_notify_prefs") .column("id", o => o.id, (o, v) => o.id = v) .column("task_id", o => o.task_id, (o, v) => o.task_id = v) .column("user_id", o => o.user_id, (o, v) => o.user_id = v); } } /** * A user-created goal (plan 12): an agent-orchestrated unit of * work over one primary project plus optional links. The brief * locks once started; the lifecycle runs draft → planning → plan * review → active → completion review → complete / failed / * cancelled, with paused freezing supervision in between. Stored * in the `goals` table. */ public class Goal : Object { public int64 id { get; set; } public int64 project_id { get; set; } public string title { get; set; default = ""; } public string brief { get; set; default = ""; } public string branch_name { get; set; default = ""; } public string? base_branch { get; set; default = null; } public string status { get; set; default = "draft"; } public int64 priority { get; set; default = 3; } public string? plan { get; set; default = null; } public string? completion_report { get; set; default = null; } public string creator { get; set; default = "human"; } /** * Plan 15 C3 (was last_seen_event_id): the timestamp through * which the goal agent has consumed its tasks' terminal * transitions — resume_due fires on transitions newer than * it, and a resume advance it. The narrative itself is * derived at read time. */ public DateTime? last_terminal_watermark { get; set; default = null; } public DateTime? created_at { get; set; default = null; } public DateTime? started_at { get; set; default = null; } public DateTime? completed_at { get; set; default = null; } public DateTime? paused_at { get; set; default = null; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("goals") .column("id", o => o.id, (o, v) => o.id = v) .column("project_id", o => o.project_id, (o, v) => o.project_id = v) .column("title", o => o.title, (o, v) => o.title = v) .column("brief", o => o.brief, (o, v) => o.brief = v) .column("branch_name", o => o.branch_name, (o, v) => o.branch_name = v) .column("base_branch", o => o.base_branch, (o, v) => o.base_branch = v) .column("status", o => o.status, (o, v) => o.status = v) .column("priority", o => o.priority, (o, v) => o.priority = v) .column("plan", o => o.plan, (o, v) => o.plan = v) .column("completion_report", o => o.completion_report, (o, v) => o.completion_report = v) .column("creator", o => o.creator, (o, v) => o.creator = v) .column("last_terminal_watermark", o => o.last_terminal_watermark, (o, v) => o.last_terminal_watermark = v) .column("created_at", o => o.created_at, (o, v) => o.created_at = v) .column("started_at", o => o.started_at, (o, v) => o.started_at = v) .column("completed_at", o => o.completed_at, (o, v) => o.completed_at = v) .column("paused_at", o => o.paused_at, (o, v) => o.paused_at = v); } } /** A project link on a goal (`goal_links`): co-developed or reference. */ public class GoalLink : Object { public int64 id { get; set; } public int64 goal_id { get; set; } public int64 project_id { get; set; } public string relationship { get; set; default = "reference"; } public static void entity_mapping(EntityMapperBuilder cfg) { cfg.table("goal_links") .column("id", o => o.id, (o, v) => o.id = v) .column("goal_id", o => o.goal_id, (o, v) => o.goal_id = v) .column("project_id", o => o.project_id, (o, v) => o.project_id = v) .column("relationship", o => o.relationship, (o, v) => o.relationship = v); } } }