1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
// Package story defines the Epic and Story data types that sit above
// internal/task's flat task tree in the planning hierarchy. This phase
// (7a) is pure data model + CRUD: no orchestration reads or writes these
// types yet. A later phase builds fan-out/orchestration logic (creating
// Builder/Evaluator/Arbitration tasks, gating deploys, etc.) on top of what
// is stored here.
package story
import (
"encoding/json"
"time"
)
// Epic is a large, loosely-scoped initiative that decomposes into one or
// more Stories. Epics are not execution units — they carry no root_task_id
// and are never dispatched.
type Epic struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
// Status is "OPEN" or "CLOSED". This phase does not validate transitions
// — storage writes whatever status string it is given, the same trust
// level as task.Project/UpdateProject.
Status string `json:"status"`
// DiscoverySource is "human" or "agent" — how the epic came to exist.
DiscoverySource string `json:"discovery_source,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Story is a shippable slice of work realized by a tree of internal/task
// Tasks rooted at RootTaskID. EpicID and ProjectID are loose references
// (plain strings, no FK enforcement) to epics.id / projects.id, matching
// the codebase's existing tolerance for unmatched reference strings (e.g.
// tasks.project).
//
// Status is one of DISCOVERY|FRAMING|BACKLOG|PRIORITIZED|IN_PROGRESS|
// SHIPPABLE|DEPLOYED|VALIDATING|REVIEW_READY|NEEDS_FIX|DONE|CANCELLED, but
// this phase enforces no state machine on it (unlike task.ValidTransition)
// — a later phase adds that once there is an orchestrator to make
// transitions meaningful. UpdateStory writes whatever status string it is
// given.
type Story struct {
ID string `json:"id"`
EpicID string `json:"epic_id,omitempty"`
ProjectID string `json:"project_id,omitempty"`
Name string `json:"name"`
BranchName string `json:"branch_name,omitempty"`
Status string `json:"status"`
DiscoverySource string `json:"discovery_source,omitempty"`
// Spec is the Planner's framing-pass output: product + arch context.
// Freeform text, not JSON.
Spec string `json:"spec,omitempty"`
// AcceptanceCriteria is stored as acceptance_criteria_json; defaults to
// an empty list, never null in API responses.
AcceptanceCriteria []string `json:"acceptance_criteria"`
// Validation is the freeform {type, steps, success_criteria} blob a
// later phase's validation agent will consume (curl|tests|playwright|
// gradle). Stored verbatim as validation_json; nil/omitted if unset.
Validation json.RawMessage `json:"validation,omitempty"`
// DeployConfig is optional, freeform JSON — only populated if a story
// should deploy-gate. Stored verbatim as deploy_config; nil/omitted if
// unset.
DeployConfig json.RawMessage `json:"deploy_config,omitempty"`
Priority string `json:"priority,omitempty"`
// RootTaskID is the top-level task realizing this story (usually a
// Planner-role task). Empty/unset means the story has no execution tree
// yet — GET .../task-tree returns an empty tree, not an error, in that
// case.
RootTaskID string `json:"root_task_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
|