summaryrefslogtreecommitdiff
path: root/internal/story/story.go
blob: dc1764fcd4cc62c0d5367236ee6dd2bd4002f98a (plain)
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"`
}