Skip to content

feat(github): close and reopen stories from Facility - #180

Open
prubianes wants to merge 1 commit into
theam:mainfrom
prubianes:feature/close-story
Open

feat(github): close and reopen stories from Facility#180
prubianes wants to merge 1 commit into
theam:mainfrom
prubianes:feature/close-story

Conversation

@prubianes

Copy link
Copy Markdown

feat(github): close and reopen stories from Facility

Closes #102.

What changed

Adds:

  • POST /v1/projects/:projectId/stories/:number/close
  • POST /v1/projects/:projectId/stories/:number/reopen

Both routes are gated by repos:write, accept Idempotency-Key, and emit story.closed / story.reopened audit events.

Closing a story now:

  1. records the human reason as a GitHub issue comment attributed to the authenticated principal;
  2. closes the issue with state_reason;
  3. updates Facility's local mirror from the state returned by GitHub;
  4. records the decision in the audit log.

Reopen is intentionally minimal: it reopens the issue, updates the mirror, and records the audit event without adding another comment.

In the web app, the story page gains a Close story action for issue stories — a required reason and a completed / not planned choice — which becomes a one-click Reopen story once the story is closed. Both are shown only to principals holding repos:write.

Decisions

state_reason defaults to not_planned. This endpoint represents the abandon / won't-do path. Completed work normally closes through a merged pull request, where GitHub already records the issue as completed. Using not_planned by default keeps the API and UI semantics aligned with that workflow.

repos:write, not a new permission. #102 asked for this to be settled explicitly. issues:write governs platform watchtower issues, a different concept, while this verb mutates a connected repository in the same way as the pull-request link routes.

A direct verb, not a HITL proposal. As proposed in the issue, the human closing the story is already the gate.

GitHub remains the source of truth. The local mirror is updated only after the GitHub mutation succeeds. This departs from the issue's suggested optimistic mirror update: waiting for GitHub's response ensures the mirror cannot claim a close that did not happen.

Retries are safe. If the closing-reason comment is written but the state transition fails, a retry updates the marked comment instead of posting a duplicate. If the issue is already closed on GitHub, Facility reconciles the mirror and returns changed: false.

Known limitation

Stories closed as not_planned still appear under the Shipped pipeline stage for seven days because the mirror does not currently store GitHub's state_reason. That requires a separate schema / classification change and is intentionally out of scope here.

Verification

  • pnpm --filter @facility/api test — 478 passed, 1 skipped (Docker-backed sandbox E2E)
  • pnpm --filter @facility/web test — 66 passed
  • pnpm --filter @facility/sdk test — 23 passed (route manifest and committed-contract drift guards)
  • pnpm lint, pnpm typecheck, pnpm build:clean — clean
  • pnpm guards — 2 guards, 0 failed
  • packages/sdk/openapi.json and src/schema.d.ts regenerated with pnpm --filter @facility/api openapi

pnpm verify is currently blocked by a pre-existing timing flake in runner/test/workspace.test.ts (.facility-hang.pid not written within the 100 ms deadline). I reproduced the same failure with this change fully stashed, while the affected test file passes standalone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Close a story from Facility, with a reason

1 participant