Summary
core.IsBlocked() only checks direct blocking relationships. If epic A blocks epic B, children of epic B (features, tasks) still show as "ready" — because they have no direct blockers themselves.
Epic A --blocks--> Epic B
└── Feature X (child of B, shows as ready)
Where
internal/beancore/links.go — FindActiveBlockers() checks:
- The bean's own
blocked_by field
- Other beans listing this bean in their
blocking field
Neither walks up the parent chain.
Affected
Everything that uses isBlocked filtering: list --ready and the GraphQL isBlocked filter. The new workflow commands in #89 (ready, next, blocked) also rely on this.
Open question
Not sure what the design intention is here. Possible interpretations:
- Children inherit parent's blocked status — if your parent is blocked, you're blocked. Simple, predictable. But maybe too coarse: you might want to prep child tasks while the parent epic is still blocked at the epic level.
- Children are independent (current behavior) — blocking only applies to the exact beans in the relationship. Users need to explicitly block children too.
- Middle ground —
isBlocked becomes ancestry-aware, but there's a way to distinguish transitive vs direct blocking so callers can choose.
wdyt?
Summary
core.IsBlocked()only checks direct blocking relationships. If epic A blocks epic B, children of epic B (features, tasks) still show as "ready" — because they have no direct blockers themselves.Where
internal/beancore/links.go—FindActiveBlockers()checks:blocked_byfieldblockingfieldNeither walks up the parent chain.
Affected
Everything that uses
isBlockedfiltering:list --readyand the GraphQLisBlockedfilter. The new workflow commands in #89 (ready,next,blocked) also rely on this.Open question
Not sure what the design intention is here. Possible interpretations:
isBlockedbecomes ancestry-aware, but there's a way to distinguish transitive vs direct blocking so callers can choose.wdyt?