Skip to content

Board has no 'cancelled' terminal status — not_planned issue closes emit feature.completed #4103

Description

@mabry1985

Problem

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:

  • route not_planned issue closes there instead of done (feat(webhooks): auto-close the linked feature when a GitHub issue closes #4102 already plumbs state_reason to the handler + event, so the wiring point exists);
  • map cancelled to a distinct lifecycle signal (e.g. feature.cancelled, or simply not feature.completed) in FeatureLifecycleBusPublisher;
  • exclude cancelled from "shipped" metrics; treat it as terminal for backlog-clearing.

Acceptance

  • Closing a linked GitHub issue as "not planned" terminalizes the feature without emitting feature.completed and without inflating shipped counts.
  • Reopening still recovers it (parity with feat(webhooks): auto-close the linked feature when a GitHub issue closes #4102's reopened path).
  • 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions