How Hierarchical Grouping of Tasks (Epics) Works in Beads
TLDR: Beads implements hierarchical task grouping through a first-class Epic issue type that establishes parent-child dependencies without blocking the ready queue, enforcing closure only when all child issues are resolved.
The gastownhall/beads project manages complex work through hierarchical grouping of tasks using Epics, which function as containers for child issues while maintaining distinct rules from blocking dependencies. Unlike hard blocks relationships that prevent work from entering the ready queue, parent-child links serve purely for organizational tracking and progress calculation.
Epic Data Model and Type System
In internal/types/types.go, the IssueType enum includes "epic" as a distinct classification alongside tasks and bugs. The Issue struct defines three optional fields populated exclusively for epics containing children: EpicTotalChildren, EpicClosedChildren, and EpicCloseable (lines 527-528 and 762-765).
These fields enable real-time progress tracking by counting total descendants and completed items. The EpicCloseable boolean specifically indicates whether an epic meets closure criteria, though this status depends on validation rules enforced elsewhere in the codebase.
Creating Epic Hierarchies
Creating an epic requires the -t epic flag with the bd create command. The CLI automatically assigns a top-level identifier, such as bd-a3f8e9, stored in the underlying Dolt database.
Adding children to an existing epic uses the --parent flag documented in website/docs/core-concepts/issues.md (lines 96-99). The system automatically appends dotted numeric suffixes to child IDs, reflecting their positional hierarchy:
# Create an epic
bd create "Auth System" -t epic -p 1
# Returns: bd-a3f8e9
# Add child tasks with automatic ID numbering
bd create "Design login UI" --parent bd-a3f8e9 # Returns: bd-a3f8e9.1
bd create "Backend validation" --parent bd-a3f8e9 # Returns: bd-a3f8e9.2
This numbering scheme persists across the epic lifecycle and remains consistent during export and import operations.
Visualizing Epic Relationships
To inspect the complete hierarchy of an epic, use the bd dep tree command followed by the epic ID. This renders a tree view showing the parent and all descendants:
bd dep tree bd-a3f8e9
# bd-a3f8e9 (Epic)
# ├─ bd-a3f8e9.1
# ├─ bd-a3f8e9.2
# └─ bd-a3f8e9.3
The visualization draws from the same parent-child metadata stored in the issue records, providing immediate context for dependency mapping.
Closure Validation and Lifecycle Rules
An epic enforces strict completion semantics: it cannot close while any child remains open. The EpicHasOpenChildren validation function in internal/validation/issue.go (lines 20-34) checks this constraint during the bd close operation.
If attempted prematurely, the operation fails with an error indicating open children remain. Users can bypass this validation using the --force flag, though this overrides the standard lifecycle protections:
# Attempting to close with open children fails
bd close bd-a3f8e9
# Error: epic has open children (validation)
# Force closure bypasses validation
bd close bd-a3f8e9 --force
Impact on Ready Queue Calculation
Parent-child relationships explicitly do not affect the ready queue calculation. While hard blocks dependencies prevent work from entering the ready state, epic hierarchies serve organizational purposes only. This distinction allows teams to structure large bodies of work without artificial workflow blockers, while still maintaining clear ownership and progress tracking.
Preserving Hierarchies Across Exports
Epic trees remain intact during bd export and bd import operations because parent-child edges exist as standard issue metadata within the Dolt schema. When importing issues, the system reconstructs the hierarchical relationships automatically, preserving dotted ID notation and dependency structures.
Summary
- Epic Structure: Defined in
internal/types/types.gowith dedicated progress fields (EpicTotalChildren,EpicClosedChildren,EpicCloseable). - Hierarchy Creation: Use
-t epicto create parents and--parentto link children, generating dotted numeric IDs likebd-a3f8e9.1. - Validation: The
EpicHasOpenChildrenfunction prevents closure until all children resolve, bypassable with--force. - Queue Impact: Parent-child relationships organize work without blocking the ready queue, unlike hard
blocksdependencies. - Data Portability: Hierarchies persist through export/import cycles via metadata preservation in Dolt.
Frequently Asked Questions
Can I close an epic while child issues remain open?
No, the EpicHasOpenChildren validation in internal/validation/issue.go blocks closure unless all descendants are resolved. You must either close the children first or use the --force flag to override this protection, though forced closure bypasses standard lifecycle safeguards.
Do parent-child epic relationships block the ready queue?
No, parent-child dependencies explicitly do not impact ready queue calculations according to the dependency table specifications. Only hard blocks dependencies prevent work from entering the ready state, allowing epic hierarchies to organize work without creating artificial workflow blockers.
How are child issue IDs generated when adding them to epics?
Child issues receive automatic dotted numeric suffixes reflecting their position in the tree. For example, the first child of epic bd-a3f8e9 becomes bd-a3f8e9.1, the second becomes bd-a3f8e9.2, and so on. This numbering system is handled by the CLI argument parsing logic in cmd/bd/bootstrap.go and persists across data exports.
Are epic hierarchies preserved during data export and import?
Yes, because parent-child edges are stored as standard issue metadata in the underlying Dolt database, epic trees survive bd export and bd import operations intact. The system reconstructs hierarchical relationships automatically during import, maintaining the original dotted ID notation and dependency structures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →