Modules/Taskboard/public/BoardSnapshot.hpp
Шурупов Илья Викторович 18a418495c Add Taskboard: node-graph task board backed by an MCP task server
The board edits the agent task database through the Python MCP server, which
now lives in this module (Taskboard/server). Code is versioned here; the data
(tasks.db and per-task session workspaces) stays in AGENTS_DATA_DIR, by default
~/agents/manager, decided only in server/paths.py.

UI:
- Tasks are graph nodes; parent/child and dependency links map to set_parent
  and dependencies. Dropping a link on empty space creates a connected task.
- Inspector edits title, description, status, priority, model, effort,
  permission mode and colour.
- Edits stay local until Save (Ctrl+S, File > Save); the status bar shows the
  unsaved count. Unsaved work is the difference between the board and the last
  server board (BoardSnapshot), saved atomically with save_board.
- Server changes merge in on a one-second poll without touching unsaved edits;
  the same field changed on both sides becomes a keep-mine/use-theirs conflict.
- Sessions: start, stop, restart with new settings (keeping or dropping the
  conversation), open in a terminal, send messages; a node badge shows the
  live session state.

Server:
- Headless Claude Code sessions, one tmux session per task, with state from
  hooks and the exit reason recorded; manual or auto permission modes.
- save_board, edit_task conflict checks, field history, get_board and
  revision polling, session tools, task workspaces with report conventions.
2026-10-21 17:55:30 +03:00

73 lines
1.8 KiB
C++

#pragma once
#include "TaskBoard.hpp"
#include "TaskStore.hpp"
#include "picojson.h"
#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>
namespace tp::taskboard {
enum class LinkKind { Parent, Dependency };
struct TaskLink {
LinkKind kind = LinkKind::Parent;
ui::NodeId from = 0;
ui::NodeId to = 0;
auto operator<=>(const TaskLink&) const = default;
};
struct NodeLayout {
Vec2F position;
halnf width = 0;
halnf bodyHeight = 0;
bool collapsed = false;
bool operator==(const NodeLayout&) const = default;
};
struct TaskRecord {
std::map<TaskField, picojson::value> fields;
NodeLayout layout;
};
// A complete description of the board's persistent content. The server's last known board and the
// board the user is editing are both snapshots; what is unsaved is exactly their difference.
struct BoardSnapshot {
std::map<ui::NodeId, TaskRecord> tasks;
std::set<TaskLink> links;
std::vector<std::string> palette;
[[nodiscard]] static BoardSnapshot capture(const TaskBoard& board);
};
struct FieldEdit {
ui::NodeId task = 0;
TaskField field = TaskField::Title;
picojson::value value;
picojson::value expected;
};
struct BoardChanges {
std::vector<ui::NodeId> created;
std::vector<ui::NodeId> deleted;
std::vector<FieldEdit> edits;
std::vector<ui::NodeId> layouts;
std::vector<TaskLink> linksAdded;
std::vector<TaskLink> linksRemoved;
std::optional<std::vector<std::string>> palette;
[[nodiscard]] bool empty() const;
[[nodiscard]] size_t count() const;
[[nodiscard]] static BoardChanges between(const BoardSnapshot& saved, const BoardSnapshot& current);
};
[[nodiscard]] LinkKind linkKind(const ui::Link& link);
[[nodiscard]] const std::vector<TaskField>& persistentFields();
}