Documentation
¶
Overview ¶
Package prd implements PRD decomposition with parallel shredding and merging.
Index ¶
- func AssignGlobalIDs(epicOrder []string, results map[string]*EpicTaskResult) ([]MergedTask, IDMapping)
- func DeduplicateTasks(tasks []MergedTask) ([]MergedTask, *DedupReport)
- func FormatValidationErrors(errs []ValidationError) string
- func NormalizeTitle(title string) string
- func ParseEpicBreakdown(data []byte) (*EpicBreakdown, []ValidationError, error)
- func ParseEpicTaskResult(data []byte, knownEpicIDs []string) (*EpicTaskResult, []ValidationError, error)
- func RemapDependencies(tasks []MergedTask, idMapping IDMapping, epicTasks map[string][]MergedTask) ([]MergedTask, *RemapReport)
- func ResequenceIDs(tasks []MergedTask, startID ...int) ([]MergedTask, IDMapping)
- func Slugify(title string) string
- func SortEpicsByDependency(breakdown *EpicBreakdown) ([]string, error)
- func TopologicalDepths(tasks []MergedTask) map[string]int
- type AmbiguousRef
- type DAGError
- type DAGErrorType
- type DAGValidation
- type DedupGroup
- type DedupMerge
- type DedupReport
- type EmitOpts
- type EmitResult
- type Emitter
- type EmitterOption
- type Epic
- type EpicBreakdown
- type EpicTaskResult
- type IDMapping
- type MergedTask
- type PhaseInfo
- type RemapReport
- type ScatterEvent
- type ScatterEventType
- type ScatterFailure
- type ScatterOption
- type ScatterOpts
- type ScatterOrchestrator
- type ScatterResult
- type ShredEvent
- type ShredEventType
- type ShredOpts
- type ShredResult
- type Shredder
- type ShredderOption
- type TaskDef
- type UnresolvedRef
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AssignGlobalIDs ¶
func AssignGlobalIDs( epicOrder []string, results map[string]*EpicTaskResult, ) ([]MergedTask, IDMapping)
AssignGlobalIDs assigns T-001, T-002, ... to all tasks across epics in the order determined by epicOrder. Tasks within each epic retain their original order.
Epics present in results but absent from epicOrder are appended at the end, sorted by epic ID, for deterministic output. Epics present in epicOrder but absent from results are silently skipped.
The zero-padding width is 3 digits for fewer than 1000 total tasks, or 4 digits for 1000 or more.
Returns the merged tasks with global IDs assigned and the ID mapping from temp_id to global_id.
func DeduplicateTasks ¶
func DeduplicateTasks(tasks []MergedTask) ([]MergedTask, *DedupReport)
DeduplicateTasks removes duplicate tasks that share a normalized title. The task with the lowest GlobalID in each duplicate group is kept; all others are removed. Unique acceptance criteria from removed tasks are appended to the keeper's criteria list. All dependency references that pointed to removed tasks are rewritten to reference the keeper instead, and self-references are dropped. The final task list preserves the original ordering of the keeper tasks.
Returns the deduplicated task list and a DedupReport summarising what was done.
func FormatValidationErrors ¶
func FormatValidationErrors(errs []ValidationError) string
FormatValidationErrors formats a slice of ValidationError as a numbered list suitable for appending to LLM retry prompts. Returns an empty string if errs is empty.
func NormalizeTitle ¶
NormalizeTitle returns a normalized version of a task title for deduplication comparison. Steps applied in order:
- Lowercase the entire string.
- Strip common action-verb prefixes (word-boundary aware -- only strips when the prefix is followed by a space or end-of-string, not by another letter).
- Collapse multiple consecutive spaces into one and trim leading/trailing space.
- Remove all punctuation (non-alphanumeric, non-space characters).
If the result after stripping is empty, the original lowercased+normalized title is returned as a fallback (e.g. when the title is itself the prefix word, like "Implement").
func ParseEpicBreakdown ¶
func ParseEpicBreakdown(data []byte) (*EpicBreakdown, []ValidationError, error)
ParseEpicBreakdown parses JSON data into an EpicBreakdown, enforcing a 10 MB size cap. It unmarshals the JSON and validates the result, returning both the parsed value and any validation errors. Returns an error only for I/O or structural JSON failures.
func ParseEpicTaskResult ¶
func ParseEpicTaskResult(data []byte, knownEpicIDs []string) (*EpicTaskResult, []ValidationError, error)
ParseEpicTaskResult parses JSON data into an EpicTaskResult, enforcing a 10 MB size cap. It unmarshals the JSON and validates the result against the provided known epic IDs. Returns an error only for I/O or structural JSON failures.
func RemapDependencies ¶
func RemapDependencies( tasks []MergedTask, idMapping IDMapping, epicTasks map[string][]MergedTask, ) ([]MergedTask, *RemapReport)
RemapDependencies rewrites all task dependencies from temp IDs to global IDs. It processes both LocalDependencies (intra-epic temp_id references) and CrossEpicDeps ("E-NNN:label" references). The resolved global IDs are merged, deduplicated, and stored in each task's Dependencies field.
The epicTasks parameter maps an epic ID to the list of MergedTask values belonging to that epic; callers typically build this from the same tasks slice grouped by EpicID.
Returns the updated tasks and a report summarising how many references were resolved, which could not be resolved, and which were ambiguous.
func ResequenceIDs ¶
func ResequenceIDs(tasks []MergedTask, startID ...int) ([]MergedTask, IDMapping)
ResequenceIDs re-assigns sequential IDs starting from startID (T-{startID}, T-{startID+1}, ...) to tasks, closing any gaps left by deduplication. All Dependencies fields are updated to use the new IDs. The original task ordering is preserved. A startID of 0 or less is treated as 1.
Returns the updated task slice and an IDMapping from old GlobalID to new GlobalID. Only IDs that actually changed are included in the mapping.
func Slugify ¶
Slugify converts a task title to a kebab-case slug suitable for file names. It lowercases the title, replaces spaces and special characters with hyphens, strips non-ASCII characters, collapses consecutive hyphens, trims leading and trailing hyphens, and truncates to 50 characters at a word boundary.
If the result is empty after sanitization, the task's GlobalID is returned as a safe fallback.
func SortEpicsByDependency ¶
func SortEpicsByDependency(breakdown *EpicBreakdown) ([]string, error)
SortEpicsByDependency returns epic IDs in topological order using Kahn's algorithm. Epics with no dependencies are placed first, sorted lexicographically for determinism. Returns an error if a cycle is detected in the epic dependency graph.
func TopologicalDepths ¶
func TopologicalDepths(tasks []MergedTask) map[string]int
TopologicalDepths computes the depth of each task in the DAG. Depth 0 = no dependencies; depth N = the longest dependency path from a root node. Requires a valid DAG (no cycles). Call after ValidateDAG confirms validity. Returns nil if the graph is invalid or empty.
Types ¶
type AmbiguousRef ¶
type AmbiguousRef struct {
// TaskID is the global ID of the task that contains the ambiguous dependency.
TaskID string
// Reference is the original cross-epic ref (e.g., "E-003:some-label").
Reference string
// Candidates lists all global IDs whose title matched the label.
Candidates []string
}
AmbiguousRef records a cross-epic dependency reference that matched multiple tasks.
type DAGError ¶
type DAGError struct {
// Type classifies the error.
Type DAGErrorType
// TaskID is the task with the error (or the first task in a cycle).
TaskID string
// Details is a human-readable description of the error.
Details string
// Cycle holds the ordered list of task IDs forming the cycle (CycleDetected only).
Cycle []string
}
DAGError represents a specific validation error in the dependency graph.
type DAGErrorType ¶
type DAGErrorType int
DAGErrorType enumerates the types of DAG validation errors.
const ( // DanglingReference means a task depends on a nonexistent task ID. DanglingReference DAGErrorType = iota // SelfReference means a task lists itself as a dependency. SelfReference // CycleDetected means a cycle exists in the dependency graph. CycleDetected )
type DAGValidation ¶
type DAGValidation struct {
// Valid is true when the graph is a valid DAG (no errors found).
Valid bool
// TopologicalOrder contains task IDs in topological order; empty when invalid.
TopologicalOrder []string
// Depths maps task GlobalID to its topological depth (0 = no dependencies).
Depths map[string]int
// MaxDepth is the maximum depth found in the graph.
MaxDepth int
// Errors lists all validation errors found.
Errors []DAGError
}
DAGValidation holds the results of DAG validation.
func ValidateDAG ¶
func ValidateDAG(tasks []MergedTask) *DAGValidation
ValidateDAG checks the task dependency graph for:
- Dangling references (dependencies on nonexistent task IDs)
- Self-references (task depending on itself)
- Cycles (using Kahn's algorithm)
If the graph is valid, ValidateDAG also computes the topological order and per-task depths (depth 0 = no dependencies, depth N = longest path from a root).
Graphs with more than 10 000 tasks are rejected with a single error entry.
type DedupGroup ¶
type DedupGroup struct {
// NormalizedTitle is the shared normalized form used for deduplication matching.
NormalizedTitle string
// Tasks holds the tasks in this group, ordered by GlobalID (earliest first).
Tasks []MergedTask
}
DedupGroup represents a set of tasks with matching normalized titles.
type DedupMerge ¶
type DedupMerge struct {
// KeptTaskID is the global ID of the task that was kept.
KeptTaskID string
// KeptTitle is the original title of the kept task.
KeptTitle string
// RemovedTaskIDs lists the global IDs of the tasks that were removed.
RemovedTaskIDs []string
// RemovedTitles lists the original titles of the removed tasks.
RemovedTitles []string
// MergedCriteria is the number of acceptance criteria merged in from removed tasks.
MergedCriteria int
}
DedupMerge describes a single merge operation where one or more duplicate tasks were merged into a keeper task.
type DedupReport ¶
type DedupReport struct {
// OriginalCount is the total number of tasks before deduplication.
OriginalCount int
// RemovedCount is the number of tasks removed as duplicates.
RemovedCount int
// FinalCount is the total number of tasks after deduplication.
FinalCount int
// Merges describes each merge operation performed.
Merges []DedupMerge
// RewrittenDeps is the number of dependency references rewritten to point to keeper tasks.
RewrittenDeps int
}
DedupReport summarizes the deduplication results.
type EmitOpts ¶
type EmitOpts struct {
// Tasks is the final merged, deduplicated task list.
Tasks []MergedTask
// Validation holds DAG analysis results including topological depths.
Validation *DAGValidation
// Epics provides epic metadata used for phase naming.
Epics *EpicBreakdown
// StartID is the starting task number for re-sequencing (default 1).
StartID int
}
EmitOpts holds the inputs needed to generate output files.
type EmitResult ¶
type EmitResult struct {
// OutputDir is the directory in which all files were written.
OutputDir string
// TaskFiles lists the paths of all generated T-XXX-slug.md files.
TaskFiles []string
// TaskStateFile is the path to the generated task-state.conf.
TaskStateFile string
// PhasesFile is the path to the generated phases.conf.
PhasesFile string
// ProgressFile is the path to the generated PROGRESS.md.
ProgressFile string
// IndexFile is the path to the generated INDEX.md.
IndexFile string
// TotalTasks is the number of task spec files written.
TotalTasks int
// TotalPhases is the number of phases written to phases.conf.
TotalPhases int
}
EmitResult summarises all files generated by Emit.
type Emitter ¶
type Emitter struct {
// contains filtered or unexported fields
}
Emitter generates all output files from a merged, deduplicated, DAG-validated task list. It writes individual task spec files, task-state.conf, phases.conf, PROGRESS.md, and INDEX.md to the configured output directory.
func NewEmitter ¶
func NewEmitter(outputDir string, opts ...EmitterOption) *Emitter
NewEmitter creates an Emitter that writes output files to outputDir. Call Emit to generate all files.
func (*Emitter) Emit ¶
func (e *Emitter) Emit(opts EmitOpts) (*EmitResult, error)
Emit generates all output files from the merged task data. It re-sequences GlobalIDs to close any gaps left by deduplication, assigns phases from topological depth, and writes the following files:
- T-XXX-slug.md for each task
- task-state.conf
- phases.conf
- PROGRESS.md
- INDEX.md
When WithForce(false) (the default), Emit returns an error if any output file already exists.
type EmitterOption ¶
type EmitterOption func(*Emitter)
EmitterOption is a functional option for Emitter.
func WithEmitterLogger ¶
func WithEmitterLogger(logger *log.Logger) EmitterOption
WithEmitterLogger sets the logger used by the Emitter.
func WithForce ¶
func WithForce(force bool) EmitterOption
WithForce controls whether existing output files are overwritten. When false (the default), Emit returns an error if any output file already exists.
type Epic ¶
type Epic struct {
// ID is the unique epic identifier in E-NNN format (e.g., E-001).
ID string `json:"id"`
// Title is the short human-readable name for the epic.
Title string `json:"title"`
// Description is a longer explanation of the epic's scope.
Description string `json:"description"`
// PRDSections lists the PRD section references covered by this epic.
PRDSections []string `json:"prd_sections"`
// EstimatedTaskCount is the expected number of tasks in this epic; must be >= 0.
EstimatedTaskCount int `json:"estimated_task_count"`
// DependenciesOnEpics lists the IDs of other epics this epic depends on.
DependenciesOnEpics []string `json:"dependencies_on_epics"`
}
Epic represents a single epic within an EpicBreakdown.
type EpicBreakdown ¶
type EpicBreakdown struct {
Epics []Epic `json:"epics"`
}
EpicBreakdown is the top-level struct for Phase 1 (shred) output containing a list of epics. It maps to the JSON produced by the LLM during PRD decomposition.
func (*EpicBreakdown) Validate ¶
func (eb *EpicBreakdown) Validate() []ValidationError
Validate checks the EpicBreakdown for correctness.
Rules enforced:
- epics array must not be empty
- each epic must have non-empty id, title, description
- epic id must match E-NNN format
- duplicate epic IDs are not allowed
- estimated_task_count must be >= 0
- dependencies_on_epics entries must reference valid epic IDs within the same breakdown (no self-reference, no unknown IDs)
Returns a slice of ValidationError; returns nil if the breakdown is valid.
type EpicTaskResult ¶
type EpicTaskResult struct {
// EpicID identifies which epic these tasks belong to; must be in E-NNN format.
EpicID string `json:"epic_id"`
// Tasks is the list of task definitions for this epic.
Tasks []TaskDef `json:"tasks"`
}
EpicTaskResult is the top-level struct for Phase 2 (scatter) output containing per-epic task definitions.
func (*EpicTaskResult) Validate ¶
func (etr *EpicTaskResult) Validate(knownEpicIDs []string) []ValidationError
Validate checks the EpicTaskResult for correctness.
Parameters:
- knownEpicIDs: the set of epic IDs from the EpicBreakdown used to validate cross_epic_dependencies
Rules enforced:
- epic_id must be non-empty and match E-NNN format
- each task must have non-empty temp_id, title, description
- temp_id must match ENNN-TNN format
- duplicate temp_ids are not allowed
- effort must be one of: "small", "medium", "large"
- priority must be one of: "must-have", "should-have", "nice-to-have"
- acceptance_criteria should not be empty (produces a validation error)
- local_dependencies must reference valid temp_ids within the same result (no self-reference)
- cross_epic_dependencies use "E-NNN:label" format; the epic ID part is validated against knownEpicIDs
Returns a slice of ValidationError; returns nil if the result is valid.
type IDMapping ¶
IDMapping maps a task's temp_id to its assigned global_id. For example: "E001-T01" -> "T-001".
type MergedTask ¶
type MergedTask struct {
// GlobalID is the sequential global identifier in T-NNN (or T-NNNN) format.
GlobalID string
// TempID is the original temporary task identifier (e.g., E001-T01).
TempID string
// EpicID is the source epic identifier in E-NNN format.
EpicID string
// Title is the short human-readable name for the task.
Title string
// Description explains what the task implements.
Description string
// AcceptanceCriteria lists the conditions for task completion.
AcceptanceCriteria []string
// LocalDependencies lists temp_ids of tasks within the same epic (not yet resolved).
LocalDependencies []string
// CrossEpicDeps lists cross-epic dependency references in "E-NNN:label" format (not yet resolved).
CrossEpicDeps []string
// Dependencies contains the resolved global task IDs after dependency remapping.
// Populated by RemapDependencies; empty until that step runs.
Dependencies []string
// Effort is the size estimate; one of: "small", "medium", "large".
Effort string
// Priority is the importance classification; one of: "must-have", "should-have", "nice-to-have".
Priority string
}
MergedTask holds a task with its assigned global ID, retaining the original temp ID and all fields from the source TaskDef.
type PhaseInfo ¶
type PhaseInfo struct {
// ID is the 1-based phase number.
ID int
// Name is a human-readable phase label.
Name string
// StartTask is the lowest GlobalID in the phase (e.g., "T-001").
StartTask string
// EndTask is the highest GlobalID in the phase (e.g., "T-010").
EndTask string
// Tasks holds the tasks that belong to this phase.
Tasks []MergedTask
}
PhaseInfo describes a single phase derived from the topological depth grouping.
func AssignPhases ¶
func AssignPhases(tasks []MergedTask, depths map[string]int, epics *EpicBreakdown) []PhaseInfo
AssignPhases groups tasks by topological depth and returns a slice of PhaseInfo values sorted by phase ID. Tasks not present in the depths map are treated as depth 0. Within each phase, tasks are sorted by GlobalID.
Phase names are derived from the most common epic title at each depth level. When no epics are provided or no common title can be determined, the fallback "Phase N" label is used.
type RemapReport ¶
type RemapReport struct {
// Remapped is the count of dependency references that were successfully resolved.
Remapped int
// Unresolved holds references that could not be mapped to any global ID.
Unresolved []UnresolvedRef
// Ambiguous holds cross-epic references that matched more than one task title.
Ambiguous []AmbiguousRef
}
RemapReport summarizes the results of a dependency remapping operation.
type ScatterEvent ¶
type ScatterEvent struct {
// Type identifies the kind of event.
Type ScatterEventType
// EpicID is the epic being processed when this event was emitted.
EpicID string
// Message is a human-readable description of the event.
Message string
// Attempt is the 1-based attempt number associated with this event.
Attempt int
}
ScatterEvent is emitted during the scatter phase for progress tracking.
type ScatterEventType ¶
type ScatterEventType string
ScatterEventType identifies the kind of scatter event emitted during parallel epic decomposition.
const ( // ScatterEventWorkerStarted is emitted when a worker begins processing an epic. ScatterEventWorkerStarted ScatterEventType = "worker_started" // ScatterEventWorkerCompleted is emitted when a worker successfully decomposes an epic. ScatterEventWorkerCompleted ScatterEventType = "worker_completed" // ScatterEventWorkerRetry is emitted before each retry attempt within a worker. ScatterEventWorkerRetry ScatterEventType = "worker_retry" // ScatterEventWorkerFailed is emitted when a worker exhausts all retries. ScatterEventWorkerFailed ScatterEventType = "worker_failed" // ScatterEventRateLimited is emitted when a rate limit is detected and the worker must wait. ScatterEventRateLimited ScatterEventType = "rate_limited" )
type ScatterFailure ¶
type ScatterFailure struct {
// EpicID identifies the epic that failed.
EpicID string
// Errors holds the last set of validation errors, if any.
Errors []ValidationError
// Err is the underlying error (e.g., context cancellation, rate-limit exhaustion).
Err error
}
ScatterFailure records information about an epic that could not be decomposed.
type ScatterOption ¶
type ScatterOption func(*ScatterOrchestrator)
ScatterOption is a functional option for configuring a ScatterOrchestrator.
func WithConcurrency ¶
func WithConcurrency(n int) ScatterOption
WithConcurrency sets the maximum number of epic workers running concurrently.
func WithRateLimiter ¶
func WithRateLimiter(rl *agent.RateLimitCoordinator) ScatterOption
WithRateLimiter sets the shared rate-limit coordinator used by all workers.
func WithScatterEvents ¶
func WithScatterEvents(ch chan<- ScatterEvent) ScatterOption
WithScatterEvents sets the event channel for progress tracking. Events are sent non-blocking; if the channel is full the event is dropped.
func WithScatterLogger ¶
func WithScatterLogger(l *log.Logger) ScatterOption
WithScatterLogger sets the structured logger on the ScatterOrchestrator.
func WithScatterMaxRetries ¶
func WithScatterMaxRetries(n int) ScatterOption
WithScatterMaxRetries sets the maximum number of retry attempts per epic worker.
type ScatterOpts ¶
type ScatterOpts struct {
// PRDContent is the full PRD text injected into each worker prompt for context.
PRDContent string
// Breakdown is the EpicBreakdown produced by Phase 1 (Shredder).
Breakdown *EpicBreakdown
// Model is an optional model override for each agent invocation.
Model string
// Effort is an optional effort-level override for each agent invocation.
Effort string
}
ScatterOpts specifies the parameters for a single Scatter call.
type ScatterOrchestrator ¶
type ScatterOrchestrator struct {
// contains filtered or unexported fields
}
ScatterOrchestrator orchestrates the parallel Phase 2 decomposition of epics into tasks. It spawns one worker goroutine per epic, bounded by the configured concurrency limit.
func NewScatterOrchestrator ¶
func NewScatterOrchestrator(a agent.Agent, workDir string, opts ...ScatterOption) *ScatterOrchestrator
NewScatterOrchestrator creates a ScatterOrchestrator with the given agent, working directory, and options. Defaults: concurrency=3, maxRetries=3.
func (*ScatterOrchestrator) Scatter ¶
func (s *ScatterOrchestrator) Scatter(ctx context.Context, opts ScatterOpts) (*ScatterResult, error)
Scatter spawns one worker goroutine per epic in opts.Breakdown, with bounded concurrency. Workers collect results into shared slices protected by a mutex. Worker goroutines only return non-nil errors for fatal conditions (context cancellation); validation failures are recorded in ScatterResult.Failures.
Returns (empty ScatterResult, nil) for an empty or nil breakdown. Returns partial results alongside the context error on cancellation.
type ScatterResult ¶
type ScatterResult struct {
// Results holds one EpicTaskResult per epic that succeeded, ordered by epic ID.
Results []*EpicTaskResult
// Failures holds metadata for epics that failed after all retries.
Failures []ScatterFailure
// Duration is the total wall-clock time spent in Scatter.
Duration time.Duration
}
ScatterResult contains the aggregated output of the scatter phase.
type ShredEvent ¶
type ShredEvent struct {
// Type identifies the kind of event.
Type ShredEventType
// Message is a human-readable description of the event.
Message string
// Attempt is the 1-based attempt number associated with this event.
Attempt int
// Errors holds the validation errors that triggered a retry event.
Errors []ValidationError
}
ShredEvent is emitted during the shred process for progress tracking.
type ShredEventType ¶
type ShredEventType string
ShredEventType identifies the kind of shred event emitted during processing.
const ( // ShredEventStarted is emitted when the shred operation begins. ShredEventStarted ShredEventType = "shred_started" // ShredEventCompleted is emitted when the shred operation succeeds. ShredEventCompleted ShredEventType = "shred_completed" // ShredEventRetry is emitted before each retry attempt. ShredEventRetry ShredEventType = "shred_retry" // ShredEventFailed is emitted when all retry attempts are exhausted. ShredEventFailed ShredEventType = "shred_failed" )
type ShredOpts ¶
type ShredOpts struct {
// PRDPath is the path to the PRD markdown file.
PRDPath string
// OutputFile is the path where the epic-breakdown JSON will be written.
// If empty, defaults to filepath.Join(workDir, "epic-breakdown.json").
OutputFile string
// Model is an optional model override for the agent invocation.
Model string
// Effort is an optional effort-level override for the agent invocation.
Effort string
}
ShredOpts specifies the parameters for a single Shred call.
type ShredResult ¶
type ShredResult struct {
// Breakdown is the validated epic breakdown produced by the agent.
Breakdown *EpicBreakdown
// Duration is the total wall-clock time spent in Shred (including retries).
Duration time.Duration
// Retries is the number of retry attempts; 0 means the first try succeeded.
Retries int
// OutputFile is the absolute path where the JSON was written.
OutputFile string
}
ShredResult contains the validated EpicBreakdown and metadata.
type Shredder ¶
type Shredder struct {
// contains filtered or unexported fields
}
Shredder orchestrates the single-agent PRD-to-epics call. It reads a PRD file, sends it to an AI agent with a structured prompt, validates the resulting EpicBreakdown JSON, and retries on validation errors.
func NewShredder ¶
func NewShredder(a agent.Agent, workDir string, opts ...ShredderOption) *Shredder
NewShredder creates a Shredder with the given agent, working directory, and options. The default maxRetries is 3. Pass functional options to customize behavior.
type ShredderOption ¶
type ShredderOption func(*Shredder)
ShredderOption is a functional option for configuring a Shredder.
func WithEvents ¶
func WithEvents(ch chan<- ShredEvent) ShredderOption
WithEvents sets the event channel for progress tracking. Events are sent non-blocking; if the channel is full the event is dropped.
func WithLogger ¶
func WithLogger(l *log.Logger) ShredderOption
WithLogger sets the structured logger on the Shredder.
func WithMaxRetries ¶
func WithMaxRetries(n int) ShredderOption
WithMaxRetries sets the maximum number of retry attempts on the Shredder.
type TaskDef ¶
type TaskDef struct {
// TempID is the temporary task identifier in ENNN-TNN format (e.g., E001-T01).
TempID string `json:"temp_id"`
// Title is the short human-readable name for the task.
Title string `json:"title"`
// Description explains what the task implements.
Description string `json:"description"`
// AcceptanceCriteria lists the conditions that must be met for the task to be complete.
AcceptanceCriteria []string `json:"acceptance_criteria"`
// LocalDependencies lists temp_ids of other tasks within the same epic that must complete first.
LocalDependencies []string `json:"local_dependencies"`
// CrossEpicDeps lists cross-epic dependency references in "E-NNN:label" format.
CrossEpicDeps []string `json:"cross_epic_dependencies"`
// Effort is the size estimate; must be one of: "small", "medium", "large".
Effort string `json:"effort"`
// Priority is the importance classification; must be one of: "must-have", "should-have", "nice-to-have".
Priority string `json:"priority"`
}
TaskDef represents a single task definition within an EpicTaskResult.
type UnresolvedRef ¶
type UnresolvedRef struct {
// TaskID is the global ID of the task that contains the unresolved dependency.
TaskID string
// Reference is the original temp_id or cross-epic ref that could not be resolved.
Reference string
}
UnresolvedRef records a dependency reference that could not be mapped to a global ID.
type ValidationError ¶
type ValidationError struct {
// Field is the dotted path to the invalid field (e.g., "epics[0].id").
Field string `json:"field"`
// Message describes the validation failure in human-readable terms.
Message string `json:"message"`
}
ValidationError represents a single validation finding with a field path and human-readable message. It is designed to be serializable for use in retry prompt augmentation.