trak_flow 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.envrc +3 -0
- data/CHANGELOG.md +69 -0
- data/COMMITS.md +196 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +281 -0
- data/README.md +479 -0
- data/Rakefile +16 -0
- data/bin/tf +6 -0
- data/bin/tf_mcp +81 -0
- data/docs/.keep +0 -0
- data/docs/api/database.md +434 -0
- data/docs/api/ruby-library.md +349 -0
- data/docs/api/task-model.md +341 -0
- data/docs/assets/stylesheets/extra.css +53 -0
- data/docs/assets/trak_flow.jpg +0 -0
- data/docs/cli/admin-commands.md +369 -0
- data/docs/cli/dependency-commands.md +321 -0
- data/docs/cli/label-commands.md +222 -0
- data/docs/cli/overview.md +163 -0
- data/docs/cli/plan-commands.md +344 -0
- data/docs/cli/task-commands.md +333 -0
- data/docs/core-concepts/dependencies.md +232 -0
- data/docs/core-concepts/labels.md +217 -0
- data/docs/core-concepts/overview.md +178 -0
- data/docs/core-concepts/plans-workflows.md +264 -0
- data/docs/core-concepts/tasks.md +205 -0
- data/docs/getting-started/configuration.md +120 -0
- data/docs/getting-started/installation.md +79 -0
- data/docs/getting-started/quick-start.md +245 -0
- data/docs/index.md +169 -0
- data/docs/mcp/integration.md +302 -0
- data/docs/mcp/overview.md +206 -0
- data/docs/mcp/resources.md +284 -0
- data/docs/mcp/tools.md +457 -0
- data/examples/basic_usage.rb +365 -0
- data/examples/cli_demo.sh +314 -0
- data/examples/mcp/Gemfile +9 -0
- data/examples/mcp/Gemfile.lock +226 -0
- data/examples/mcp/http_demo.rb +232 -0
- data/examples/mcp/stdio_demo.rb +146 -0
- data/lib/trak_flow/cli/admin_commands.rb +136 -0
- data/lib/trak_flow/cli/config_commands.rb +260 -0
- data/lib/trak_flow/cli/dep_commands.rb +71 -0
- data/lib/trak_flow/cli/label_commands.rb +76 -0
- data/lib/trak_flow/cli/main_commands.rb +386 -0
- data/lib/trak_flow/cli/plan_commands.rb +185 -0
- data/lib/trak_flow/cli/workflow_commands.rb +133 -0
- data/lib/trak_flow/cli.rb +110 -0
- data/lib/trak_flow/config/defaults.yml +114 -0
- data/lib/trak_flow/config/section.rb +74 -0
- data/lib/trak_flow/config.rb +276 -0
- data/lib/trak_flow/graph/dependency_graph.rb +288 -0
- data/lib/trak_flow/id_generator.rb +52 -0
- data/lib/trak_flow/mcp/resources/base_resource.rb +25 -0
- data/lib/trak_flow/mcp/resources/dependency_graph.rb +31 -0
- data/lib/trak_flow/mcp/resources/label_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/plan_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/plan_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_by_id.rb +31 -0
- data/lib/trak_flow/mcp/resources/task_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_next.rb +30 -0
- data/lib/trak_flow/mcp/resources/workflow_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/workflow_list.rb +21 -0
- data/lib/trak_flow/mcp/server.rb +140 -0
- data/lib/trak_flow/mcp/tools/base_tool.rb +29 -0
- data/lib/trak_flow/mcp/tools/comment_add.rb +33 -0
- data/lib/trak_flow/mcp/tools/dep_add.rb +34 -0
- data/lib/trak_flow/mcp/tools/dep_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/label_add.rb +28 -0
- data/lib/trak_flow/mcp/tools/label_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/plan_add_step.rb +35 -0
- data/lib/trak_flow/mcp/tools/plan_create.rb +33 -0
- data/lib/trak_flow/mcp/tools/plan_run.rb +58 -0
- data/lib/trak_flow/mcp/tools/plan_start.rb +58 -0
- data/lib/trak_flow/mcp/tools/task_block.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_close.rb +26 -0
- data/lib/trak_flow/mcp/tools/task_create.rb +51 -0
- data/lib/trak_flow/mcp/tools/task_defer.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_start.rb +25 -0
- data/lib/trak_flow/mcp/tools/task_update.rb +36 -0
- data/lib/trak_flow/mcp/tools/workflow_discard.rb +28 -0
- data/lib/trak_flow/mcp/tools/workflow_summarize.rb +34 -0
- data/lib/trak_flow/mcp.rb +38 -0
- data/lib/trak_flow/models/comment.rb +71 -0
- data/lib/trak_flow/models/dependency.rb +96 -0
- data/lib/trak_flow/models/label.rb +90 -0
- data/lib/trak_flow/models/task.rb +188 -0
- data/lib/trak_flow/storage/database.rb +638 -0
- data/lib/trak_flow/storage/jsonl.rb +259 -0
- data/lib/trak_flow/time_parser.rb +15 -0
- data/lib/trak_flow/version.rb +5 -0
- data/lib/trak_flow.rb +100 -0
- data/mkdocs.yml +143 -0
- metadata +392 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Ruby Library Reference
|
|
2
|
+
|
|
3
|
+
TrakFlow can be used as a Ruby library for programmatic task management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# Gemfile
|
|
9
|
+
gem 'trak_flow'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
require 'trak_flow'
|
|
16
|
+
|
|
17
|
+
# Initialize with default configuration
|
|
18
|
+
trak = TrakFlow.new
|
|
19
|
+
|
|
20
|
+
# Create a task
|
|
21
|
+
task = trak.create_task(
|
|
22
|
+
title: "Implement feature X",
|
|
23
|
+
type: "feature",
|
|
24
|
+
priority: 1
|
|
25
|
+
)
|
|
26
|
+
puts "Created: #{task.id}"
|
|
27
|
+
|
|
28
|
+
# List open tasks
|
|
29
|
+
tasks = trak.list_tasks(status: "open")
|
|
30
|
+
tasks.each { |t| puts "#{t.id}: #{t.title}" }
|
|
31
|
+
|
|
32
|
+
# Update a task
|
|
33
|
+
trak.start_task(task.id)
|
|
34
|
+
trak.close_task(task.id, summary: "Implemented in PR #42")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
### Default Configuration
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
trak = TrakFlow.new
|
|
43
|
+
# Uses .trak_flow/ in current directory
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Custom Configuration
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
trak = TrakFlow.new(
|
|
50
|
+
data_dir: "/path/to/data",
|
|
51
|
+
config: {
|
|
52
|
+
default_priority: 2,
|
|
53
|
+
gc_retention: "7d"
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Configuration from File
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
trak = TrakFlow.from_config("/path/to/config.json")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Core Classes
|
|
65
|
+
|
|
66
|
+
### TrakFlow
|
|
67
|
+
|
|
68
|
+
Main entry point for all operations.
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
trak = TrakFlow.new(options = {})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Task Methods
|
|
75
|
+
|
|
76
|
+
| Method | Description |
|
|
77
|
+
|--------|-------------|
|
|
78
|
+
| `create_task(attrs)` | Create a new task |
|
|
79
|
+
| `find_task(id)` | Find task by ID |
|
|
80
|
+
| `list_tasks(filters)` | List tasks with filters |
|
|
81
|
+
| `update_task(id, attrs)` | Update task attributes |
|
|
82
|
+
| `start_task(id)` | Mark as in_progress |
|
|
83
|
+
| `close_task(id, summary:)` | Mark as closed |
|
|
84
|
+
| `block_task(id, reason:)` | Mark as blocked |
|
|
85
|
+
| `reopen_task(id)` | Reopen closed task |
|
|
86
|
+
| `delete_task(id)` | Delete task |
|
|
87
|
+
|
|
88
|
+
#### Plan Methods
|
|
89
|
+
|
|
90
|
+
| Method | Description |
|
|
91
|
+
|--------|-------------|
|
|
92
|
+
| `create_plan(title, description:)` | Create a Plan |
|
|
93
|
+
| `add_step(plan_id, title, attrs)` | Add step to Plan |
|
|
94
|
+
| `start_plan(plan_id, title:)` | Create persistent Workflow |
|
|
95
|
+
| `execute_plan(plan_id, title:)` | Create ephemeral Workflow |
|
|
96
|
+
| `list_plans` | List all Plans |
|
|
97
|
+
| `list_workflows(plan_id:)` | List Workflows |
|
|
98
|
+
|
|
99
|
+
#### Dependency Methods
|
|
100
|
+
|
|
101
|
+
| Method | Description |
|
|
102
|
+
|--------|-------------|
|
|
103
|
+
| `add_dependency(source, target, type:)` | Add dependency |
|
|
104
|
+
| `remove_dependency(source, target)` | Remove dependency |
|
|
105
|
+
| `ready_tasks(filters)` | Find tasks with no blockers |
|
|
106
|
+
| `dependency_tree(task_id)` | Get dependency tree |
|
|
107
|
+
|
|
108
|
+
#### Label Methods
|
|
109
|
+
|
|
110
|
+
| Method | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| `add_label(task_id, label)` | Add label to task |
|
|
113
|
+
| `remove_label(task_id, label)` | Remove label |
|
|
114
|
+
| `labels_for(task_id)` | Get task's labels |
|
|
115
|
+
| `all_labels` | List all labels |
|
|
116
|
+
|
|
117
|
+
### TrakFlow::Models::Task
|
|
118
|
+
|
|
119
|
+
Represents a task.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
task = trak.find_task("tf-abc123")
|
|
123
|
+
|
|
124
|
+
# Properties
|
|
125
|
+
task.id # => "tf-abc123"
|
|
126
|
+
task.title # => "Implement feature"
|
|
127
|
+
task.description # => "Detailed description"
|
|
128
|
+
task.status # => "in_progress"
|
|
129
|
+
task.priority # => 1
|
|
130
|
+
task.type # => "feature"
|
|
131
|
+
task.assignee # => "claude"
|
|
132
|
+
task.parent_id # => nil
|
|
133
|
+
task.created_at # => Time
|
|
134
|
+
task.updated_at # => Time
|
|
135
|
+
task.closed_at # => nil
|
|
136
|
+
task.notes # => "..."
|
|
137
|
+
task.content_hash # => "a1b2c3d4"
|
|
138
|
+
|
|
139
|
+
# Plan/Workflow properties
|
|
140
|
+
task.plan # => false
|
|
141
|
+
task.source_plan_id # => nil
|
|
142
|
+
task.ephemeral # => false
|
|
143
|
+
|
|
144
|
+
# Predicates
|
|
145
|
+
task.open? # => false
|
|
146
|
+
task.closed? # => false
|
|
147
|
+
task.in_progress? # => true
|
|
148
|
+
task.blocked? # => false
|
|
149
|
+
task.plan? # => false
|
|
150
|
+
task.workflow? # => false
|
|
151
|
+
task.ephemeral? # => false
|
|
152
|
+
task.executable? # => true
|
|
153
|
+
task.discardable? # => false
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### TrakFlow::Storage::Database
|
|
157
|
+
|
|
158
|
+
SQLite storage layer.
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
db = TrakFlow::Storage::Database.new("/path/to/trak_flow.db")
|
|
162
|
+
|
|
163
|
+
# Query methods
|
|
164
|
+
db.find_task(id)
|
|
165
|
+
db.list_tasks(filters)
|
|
166
|
+
db.find_ready_tasks
|
|
167
|
+
db.find_plans
|
|
168
|
+
db.find_workflows(plan_id:)
|
|
169
|
+
|
|
170
|
+
# Write methods
|
|
171
|
+
db.insert_task(task)
|
|
172
|
+
db.update_task(task)
|
|
173
|
+
db.delete_task(id)
|
|
174
|
+
|
|
175
|
+
# Dependency methods
|
|
176
|
+
db.add_dependency(source, target, type)
|
|
177
|
+
db.remove_dependency(source, target)
|
|
178
|
+
db.dependencies_for(task_id)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### TrakFlow::Storage::Jsonl
|
|
182
|
+
|
|
183
|
+
JSONL file storage.
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
jsonl = TrakFlow::Storage::Jsonl.new("/path/to/issues.jsonl")
|
|
187
|
+
|
|
188
|
+
# Load all tasks
|
|
189
|
+
tasks = jsonl.load_all
|
|
190
|
+
|
|
191
|
+
# Append a task
|
|
192
|
+
jsonl.append(task)
|
|
193
|
+
|
|
194
|
+
# Rewrite entire file
|
|
195
|
+
jsonl.save_all(tasks)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Examples
|
|
199
|
+
|
|
200
|
+
### Create and Manage Tasks
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
require 'trak_flow'
|
|
204
|
+
|
|
205
|
+
trak = TrakFlow.new
|
|
206
|
+
|
|
207
|
+
# Create a feature with subtasks
|
|
208
|
+
feature = trak.create_task(
|
|
209
|
+
title: "User Authentication",
|
|
210
|
+
type: "epic",
|
|
211
|
+
priority: 1
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
login = trak.create_task(
|
|
215
|
+
title: "Login page",
|
|
216
|
+
parent_id: feature.id
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
logout = trak.create_task(
|
|
220
|
+
title: "Logout functionality",
|
|
221
|
+
parent_id: feature.id
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Add dependencies
|
|
225
|
+
trak.add_dependency(login.id, logout.id)
|
|
226
|
+
|
|
227
|
+
# Find ready work
|
|
228
|
+
ready = trak.ready_tasks
|
|
229
|
+
puts "Ready to work on: #{ready.map(&:title)}"
|
|
230
|
+
|
|
231
|
+
# Complete the workflow
|
|
232
|
+
trak.start_task(login.id)
|
|
233
|
+
trak.close_task(login.id, summary: "Implemented login")
|
|
234
|
+
|
|
235
|
+
# Now logout is ready
|
|
236
|
+
ready = trak.ready_tasks
|
|
237
|
+
puts "Now ready: #{ready.map(&:title)}"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Work with Plans
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
require 'trak_flow'
|
|
244
|
+
|
|
245
|
+
trak = TrakFlow.new
|
|
246
|
+
|
|
247
|
+
# Create a release plan
|
|
248
|
+
plan = trak.create_plan("Release Process")
|
|
249
|
+
trak.add_step(plan.id, "Update version number")
|
|
250
|
+
trak.add_step(plan.id, "Update CHANGELOG")
|
|
251
|
+
trak.add_step(plan.id, "Run tests")
|
|
252
|
+
trak.add_step(plan.id, "Create release tag")
|
|
253
|
+
trak.add_step(plan.id, "Publish gem")
|
|
254
|
+
|
|
255
|
+
# Start a release workflow
|
|
256
|
+
workflow = trak.start_plan(plan.id, title: "Release v1.0.0")
|
|
257
|
+
|
|
258
|
+
# Work through the steps
|
|
259
|
+
workflow.tasks.each do |task|
|
|
260
|
+
puts "Starting: #{task.title}"
|
|
261
|
+
trak.start_task(task.id)
|
|
262
|
+
# ... do the work ...
|
|
263
|
+
trak.close_task(task.id)
|
|
264
|
+
puts "Completed: #{task.title}"
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Summarize the workflow
|
|
268
|
+
trak.summarize_workflow(workflow.id, summary: "Released v1.0.0")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Filtering and Querying
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
require 'trak_flow'
|
|
275
|
+
|
|
276
|
+
trak = TrakFlow.new
|
|
277
|
+
|
|
278
|
+
# Filter by multiple criteria
|
|
279
|
+
bugs = trak.list_tasks(
|
|
280
|
+
status: "open",
|
|
281
|
+
type: "bug",
|
|
282
|
+
priority: [0, 1] # Critical or High
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Filter by label
|
|
286
|
+
frontend = trak.list_tasks(label: "frontend")
|
|
287
|
+
|
|
288
|
+
# Complex queries using the database directly
|
|
289
|
+
db = trak.database
|
|
290
|
+
high_priority_ready = db.list_tasks.select do |task|
|
|
291
|
+
task.priority <= 1 && db.is_ready?(task.id)
|
|
292
|
+
end
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Dependency Analysis
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
require 'trak_flow'
|
|
299
|
+
|
|
300
|
+
trak = TrakFlow.new
|
|
301
|
+
|
|
302
|
+
# Get dependency tree
|
|
303
|
+
tree = trak.dependency_tree("tf-abc123")
|
|
304
|
+
|
|
305
|
+
puts "Blocked by:"
|
|
306
|
+
tree[:blocked_by].each { |t| puts " - #{t.title}" }
|
|
307
|
+
|
|
308
|
+
puts "Blocks:"
|
|
309
|
+
tree[:blocks].each { |t| puts " - #{t.title}" }
|
|
310
|
+
|
|
311
|
+
# Check for cycles
|
|
312
|
+
if trak.would_create_cycle?("tf-a", "tf-b")
|
|
313
|
+
puts "Warning: would create a cycle!"
|
|
314
|
+
end
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Error Handling
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
require 'trak_flow'
|
|
321
|
+
|
|
322
|
+
trak = TrakFlow.new
|
|
323
|
+
|
|
324
|
+
begin
|
|
325
|
+
task = trak.find_task("invalid-id")
|
|
326
|
+
rescue TrakFlow::NotFoundError => e
|
|
327
|
+
puts "Task not found: #{e.message}"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
begin
|
|
331
|
+
trak.create_task(title: "") # Empty title
|
|
332
|
+
rescue TrakFlow::ValidationError => e
|
|
333
|
+
puts "Validation failed: #{e.message}"
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
begin
|
|
337
|
+
trak.add_dependency("tf-a", "tf-a") # Self-reference
|
|
338
|
+
rescue TrakFlow::DependencyError => e
|
|
339
|
+
puts "Invalid dependency: #{e.message}"
|
|
340
|
+
end
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Thread Safety
|
|
344
|
+
|
|
345
|
+
The library is designed for single-threaded use. For concurrent access:
|
|
346
|
+
|
|
347
|
+
1. Use separate `TrakFlow` instances per thread
|
|
348
|
+
2. Or use the MCP server for concurrent access
|
|
349
|
+
3. The SQLite database handles concurrent reads safely
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Task Model Reference
|
|
2
|
+
|
|
3
|
+
The Task model is the core data structure in TrakFlow.
|
|
4
|
+
|
|
5
|
+
## Class: TrakFlow::Models::Task
|
|
6
|
+
|
|
7
|
+
### Initialization
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
task = TrakFlow::Models::Task.new(
|
|
11
|
+
title: "Task title",
|
|
12
|
+
description: "Optional description",
|
|
13
|
+
type: "task",
|
|
14
|
+
priority: 2,
|
|
15
|
+
assignee: nil,
|
|
16
|
+
parent_id: nil,
|
|
17
|
+
plan: false,
|
|
18
|
+
ephemeral: false
|
|
19
|
+
)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Attributes
|
|
23
|
+
|
|
24
|
+
#### Required Attributes
|
|
25
|
+
|
|
26
|
+
| Attribute | Type | Description |
|
|
27
|
+
|-----------|------|-------------|
|
|
28
|
+
| `title` | String | Task title (required) |
|
|
29
|
+
|
|
30
|
+
#### Auto-Generated Attributes
|
|
31
|
+
|
|
32
|
+
| Attribute | Type | Description |
|
|
33
|
+
|-----------|------|-------------|
|
|
34
|
+
| `id` | String | Unique ID (e.g., `tf-abc123`) |
|
|
35
|
+
| `created_at` | Time | Creation timestamp |
|
|
36
|
+
| `updated_at` | Time | Last update timestamp |
|
|
37
|
+
| `content_hash` | String | Hash of task content |
|
|
38
|
+
|
|
39
|
+
#### Optional Attributes
|
|
40
|
+
|
|
41
|
+
| Attribute | Type | Default | Description |
|
|
42
|
+
|-----------|------|---------|-------------|
|
|
43
|
+
| `description` | String | `nil` | Detailed description |
|
|
44
|
+
| `status` | String | `"open"` | Current status |
|
|
45
|
+
| `priority` | Integer | `2` | Priority level (0-4) |
|
|
46
|
+
| `type` | String | `"task"` | Task type |
|
|
47
|
+
| `assignee` | String | `nil` | Assigned user/agent |
|
|
48
|
+
| `parent_id` | String | `nil` | Parent task ID |
|
|
49
|
+
| `closed_at` | Time | `nil` | When task was closed |
|
|
50
|
+
| `notes` | String | `""` | Free-form notes |
|
|
51
|
+
|
|
52
|
+
#### Plan/Workflow Attributes
|
|
53
|
+
|
|
54
|
+
| Attribute | Type | Default | Description |
|
|
55
|
+
|-----------|------|---------|-------------|
|
|
56
|
+
| `plan` | Boolean | `false` | Is this a Plan blueprint |
|
|
57
|
+
| `source_plan_id` | String | `nil` | Source Plan for Workflows |
|
|
58
|
+
| `ephemeral` | Boolean | `false` | Is this ephemeral |
|
|
59
|
+
|
|
60
|
+
### Status Values
|
|
61
|
+
|
|
62
|
+
| Status | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `open` | Ready to work on |
|
|
65
|
+
| `in_progress` | Currently being worked on |
|
|
66
|
+
| `blocked` | Waiting on something |
|
|
67
|
+
| `deferred` | Postponed for later |
|
|
68
|
+
| `closed` | Completed |
|
|
69
|
+
| `tombstone` | Archived (permanent) |
|
|
70
|
+
| `pinned` | Highlighted for visibility |
|
|
71
|
+
|
|
72
|
+
### Type Values
|
|
73
|
+
|
|
74
|
+
| Type | Description |
|
|
75
|
+
|------|-------------|
|
|
76
|
+
| `task` | General task (default) |
|
|
77
|
+
| `bug` | Bug fix |
|
|
78
|
+
| `feature` | New feature |
|
|
79
|
+
| `epic` | Large initiative |
|
|
80
|
+
| `chore` | Maintenance work |
|
|
81
|
+
|
|
82
|
+
### Priority Levels
|
|
83
|
+
|
|
84
|
+
| Level | Name | Description |
|
|
85
|
+
|-------|------|-------------|
|
|
86
|
+
| 0 | Critical | Urgent, drop everything |
|
|
87
|
+
| 1 | High | Important, do soon |
|
|
88
|
+
| 2 | Medium | Normal priority (default) |
|
|
89
|
+
| 3 | Low | Do when time permits |
|
|
90
|
+
| 4 | Backlog | Future consideration |
|
|
91
|
+
|
|
92
|
+
## Predicate Methods
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
task = TrakFlow::Models::Task.new(title: "Example")
|
|
96
|
+
|
|
97
|
+
# Status predicates
|
|
98
|
+
task.open? # status == "open"
|
|
99
|
+
task.closed? # status == "closed" or "tombstone"
|
|
100
|
+
task.in_progress? # status == "in_progress"
|
|
101
|
+
task.blocked? # status == "blocked"
|
|
102
|
+
task.deferred? # status == "deferred"
|
|
103
|
+
|
|
104
|
+
# Type predicates
|
|
105
|
+
task.bug? # type == "bug"
|
|
106
|
+
task.feature? # type == "feature"
|
|
107
|
+
task.epic? # type == "epic"
|
|
108
|
+
task.chore? # type == "chore"
|
|
109
|
+
|
|
110
|
+
# Plan/Workflow predicates
|
|
111
|
+
task.plan? # plan == true
|
|
112
|
+
task.workflow? # source_plan_id present and not a plan
|
|
113
|
+
task.ephemeral? # ephemeral == true
|
|
114
|
+
|
|
115
|
+
# Capability predicates
|
|
116
|
+
task.executable? # not a plan (can be executed)
|
|
117
|
+
task.discardable? # ephemeral (can be discarded)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Instance Methods
|
|
121
|
+
|
|
122
|
+
### Status Transitions
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
task.start! # -> in_progress
|
|
126
|
+
task.block! # -> blocked
|
|
127
|
+
task.defer! # -> deferred
|
|
128
|
+
task.close! # -> closed
|
|
129
|
+
task.reopen! # -> open
|
|
130
|
+
task.archive! # -> tombstone
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Content Management
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Update timestamp
|
|
137
|
+
task.touch!
|
|
138
|
+
|
|
139
|
+
# Append to notes with trace entry
|
|
140
|
+
task.append_trace("STARTED", "Beginning implementation")
|
|
141
|
+
# Adds: [2024-01-15T10:00:00Z] [STARTED] Beginning implementation
|
|
142
|
+
|
|
143
|
+
# Compute content hash
|
|
144
|
+
task.compute_hash!
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Serialization
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
# Convert to Hash
|
|
151
|
+
hash = task.to_h
|
|
152
|
+
|
|
153
|
+
# Convert to JSON
|
|
154
|
+
json = task.to_json
|
|
155
|
+
|
|
156
|
+
# Create from Hash
|
|
157
|
+
task = TrakFlow::Models::Task.from_hash(hash)
|
|
158
|
+
|
|
159
|
+
# Create from JSON
|
|
160
|
+
task = TrakFlow::Models::Task.from_json(json)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Validation
|
|
164
|
+
|
|
165
|
+
Tasks are validated on creation and update:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
task = TrakFlow::Models::Task.new(title: "")
|
|
169
|
+
task.valid? # => false
|
|
170
|
+
task.errors # => ["Title is required"]
|
|
171
|
+
|
|
172
|
+
task = TrakFlow::Models::Task.new(
|
|
173
|
+
title: "Test",
|
|
174
|
+
status: "invalid"
|
|
175
|
+
)
|
|
176
|
+
task.valid? # => false
|
|
177
|
+
task.errors # => ["Invalid status: invalid"]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Validation Rules
|
|
181
|
+
|
|
182
|
+
| Rule | Error Message |
|
|
183
|
+
|------|---------------|
|
|
184
|
+
| Title required | "Title is required" |
|
|
185
|
+
| Valid status | "Invalid status: {status}" |
|
|
186
|
+
| Valid priority | "Invalid priority: {priority}" |
|
|
187
|
+
| Valid type | "Invalid type: {type}" |
|
|
188
|
+
| Plans can't be ephemeral | "Plans cannot be ephemeral" |
|
|
189
|
+
| Plans stay open | "Plans cannot change status" |
|
|
190
|
+
| Plans can't derive from Plans | "Plans cannot be derived from other Plans" |
|
|
191
|
+
|
|
192
|
+
## ID Generation
|
|
193
|
+
|
|
194
|
+
IDs are generated using content hashing:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
task = TrakFlow::Models::Task.new(title: "Example")
|
|
198
|
+
task.generate_id!
|
|
199
|
+
task.id # => "tf-a1b2c3d4"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The ID is derived from:
|
|
203
|
+
- Title
|
|
204
|
+
- Description
|
|
205
|
+
- Type
|
|
206
|
+
- Creation timestamp
|
|
207
|
+
- Random salt (for uniqueness)
|
|
208
|
+
|
|
209
|
+
### ID Format
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
tf-xxxxxxxx
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `tf-` prefix identifies TrakFlow tasks
|
|
216
|
+
- 8 character hex hash
|
|
217
|
+
|
|
218
|
+
## JSON Representation
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"id": "tf-abc123",
|
|
223
|
+
"title": "Implement feature X",
|
|
224
|
+
"description": "Detailed description here",
|
|
225
|
+
"status": "in_progress",
|
|
226
|
+
"priority": 1,
|
|
227
|
+
"type": "feature",
|
|
228
|
+
"assignee": "claude",
|
|
229
|
+
"parent_id": null,
|
|
230
|
+
"created_at": "2024-01-15T10:00:00Z",
|
|
231
|
+
"updated_at": "2024-01-15T14:30:00Z",
|
|
232
|
+
"closed_at": null,
|
|
233
|
+
"content_hash": "a1b2c3d4",
|
|
234
|
+
"plan": false,
|
|
235
|
+
"source_plan_id": null,
|
|
236
|
+
"ephemeral": false,
|
|
237
|
+
"notes": ""
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Content Hash
|
|
242
|
+
|
|
243
|
+
The content hash enables:
|
|
244
|
+
|
|
245
|
+
1. **Change detection** - Know when content was modified
|
|
246
|
+
2. **Deduplication** - Identify identical tasks
|
|
247
|
+
3. **Sync support** - Merge changes from multiple sources
|
|
248
|
+
|
|
249
|
+
### Hash Computation
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
# Automatically computed on save
|
|
253
|
+
task.compute_hash!
|
|
254
|
+
|
|
255
|
+
# Hash is based on:
|
|
256
|
+
# - title
|
|
257
|
+
# - description
|
|
258
|
+
# - type
|
|
259
|
+
# - priority
|
|
260
|
+
# - assignee
|
|
261
|
+
# - status
|
|
262
|
+
# - parent_id
|
|
263
|
+
# - plan
|
|
264
|
+
# - source_plan_id
|
|
265
|
+
# - ephemeral
|
|
266
|
+
# - notes
|
|
267
|
+
|
|
268
|
+
# Excluded from hash:
|
|
269
|
+
# - id
|
|
270
|
+
# - created_at
|
|
271
|
+
# - updated_at
|
|
272
|
+
# - closed_at
|
|
273
|
+
# - content_hash itself
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Examples
|
|
277
|
+
|
|
278
|
+
### Creating Different Task Types
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
# Bug
|
|
282
|
+
bug = TrakFlow::Models::Task.new(
|
|
283
|
+
title: "Fix null pointer exception",
|
|
284
|
+
type: "bug",
|
|
285
|
+
priority: 0
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Feature
|
|
289
|
+
feature = TrakFlow::Models::Task.new(
|
|
290
|
+
title: "Add OAuth support",
|
|
291
|
+
type: "feature",
|
|
292
|
+
priority: 1,
|
|
293
|
+
description: "Support Google and GitHub OAuth"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Epic with children
|
|
297
|
+
epic = TrakFlow::Models::Task.new(
|
|
298
|
+
title: "User Management",
|
|
299
|
+
type: "epic"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
child = TrakFlow::Models::Task.new(
|
|
303
|
+
title: "Login page",
|
|
304
|
+
parent_id: epic.id
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Creating a Plan
|
|
309
|
+
|
|
310
|
+
```ruby
|
|
311
|
+
plan = TrakFlow::Models::Task.new(
|
|
312
|
+
title: "Deploy Checklist",
|
|
313
|
+
plan: true
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
plan.plan? # => true
|
|
317
|
+
plan.executable? # => false
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Status Workflow
|
|
321
|
+
|
|
322
|
+
```ruby
|
|
323
|
+
task = TrakFlow::Models::Task.new(title: "Example")
|
|
324
|
+
|
|
325
|
+
task.status # => "open"
|
|
326
|
+
|
|
327
|
+
task.start!
|
|
328
|
+
task.status # => "in_progress"
|
|
329
|
+
|
|
330
|
+
task.block!
|
|
331
|
+
task.status # => "blocked"
|
|
332
|
+
task.notes # Contains trace entry
|
|
333
|
+
|
|
334
|
+
task.reopen!
|
|
335
|
+
task.status # => "open"
|
|
336
|
+
|
|
337
|
+
task.start!
|
|
338
|
+
task.close!
|
|
339
|
+
task.status # => "closed"
|
|
340
|
+
task.closed_at # => Time.now
|
|
341
|
+
```
|