diff options
Diffstat (limited to 'internal/story/story.go')
| -rw-r--r-- | internal/story/story.go | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/internal/story/story.go b/internal/story/story.go new file mode 100644 index 0000000..dc1764f --- /dev/null +++ b/internal/story/story.go @@ -0,0 +1,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"` +} |
