You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The 5-status board model (backlog → in_progress → review → done, plus blocked) has one terminal status: done. FeatureLifecycleBusPublisher emits feature.completed on any transition to done (apps/server/src/services/feature-lifecycle-bus-publisher.ts:40, TERMINAL_STATUSES = {'done'} — archived is not considered).
The issue-close-sync webhook (#4102) terminalizes a linked feature to done when its GitHub issue closes. With #4102's follow-up, the close now carries state_reason (completed vs not_planned) on the audit trail and the feature:issue-closed event — but because the status still becomes done, an issue closed as not planned (wontfix/duplicate/abandoned) still:
emits feature.completed to protoWorkstacean (workstacean thinks it shipped), and
counts as shipped/completed work in any board metrics keyed on done.
archiveFeature isn't a fix: it's a destructive relocate (deletes + stubs the feature dir) that still lands on status: 'done' and would break #4102's reopen-recovery path.
Proposed fix
Add a cancelled (or abandoned) terminal status to FeatureStatus, and:
Unit coverage for completed vs not_planned close → distinct terminal status + distinct/absent lifecycle emission.
Context
Found taking over #4102 (roxy's issue-close-sync). #4102 ships the state_reason plumbing; this issue is the board-model change it sets up. Out of scope for #4102 itself (single-status terminal model is a broader change).
Problem
The 5-status board model (
backlog → in_progress → review → done, plusblocked) has one terminal status:done.FeatureLifecycleBusPublisheremitsfeature.completedon any transition todone(apps/server/src/services/feature-lifecycle-bus-publisher.ts:40,TERMINAL_STATUSES = {'done'}—archivedis not considered).The issue-close-sync webhook (#4102) terminalizes a linked feature to
donewhen its GitHub issue closes. With #4102's follow-up, the close now carriesstate_reason(completedvsnot_planned) on the audit trail and thefeature:issue-closedevent — but because the status still becomesdone, an issue closed as not planned (wontfix/duplicate/abandoned) still:feature.completedto protoWorkstacean (workstacean thinks it shipped), anddone.archiveFeatureisn't a fix: it's a destructive relocate (deletes + stubs the feature dir) that still lands onstatus: 'done'and would break #4102's reopen-recovery path.Proposed fix
Add a
cancelled(orabandoned) terminal status toFeatureStatus, and:not_plannedissue closes there instead ofdone(feat(webhooks): auto-close the linked feature when a GitHub issue closes #4102 already plumbsstate_reasonto the handler + event, so the wiring point exists);cancelledto a distinct lifecycle signal (e.g.feature.cancelled, or simply notfeature.completed) inFeatureLifecycleBusPublisher;cancelledfrom "shipped" metrics; treat it as terminal for backlog-clearing.Acceptance
feature.completedand without inflating shipped counts.Context
Found taking over #4102 (roxy's issue-close-sync). #4102 ships the
state_reasonplumbing; this issue is the board-model change it sets up. Out of scope for #4102 itself (single-status terminal model is a broader change).