feat: workspace review — withdrawn and removed requests, and a notice when someone removes yours

SA
27m ago
+110-15

Stacked on #1707 (workspace roles). The CLI side of withdrawing a review request and removing an approved contribution. The web app has the buttons; the backend adds the routes.

What changes

  • review-api.ts:
    • Contribution.status accepts withdrawn and retired, so those rows are read rather than skipped. Before, they were skipped, the list never counted as complete, and pending ids were never let go.
    • Reads decided_by_me and author_has_access.
  • Notices:
    • A withdrawn request is let go without a notice.
    • Your approved requests are tracked too, capped at 200. When one comes back retired and you didn't remove it yourself, the TUI shows "removed from the workspace by X — reason" once.
    • State files written before this load as before.
  • workspace review status: shows "withdrawn", and "removed from the workspace" with who removed it and why.
  • Docs: withdrawing and removing happen in the web app, and the CLI shows the new notice.

Ship this before the backend: the CLI reviewed in #1685 skips rows with these statuses and counts them as unreadable.

Verification

  • Typecheck: clean.
  • Tests: test/altimate/workspace/review.test.ts and skill-publish.test.ts pass after rebasing onto the roles branch (108 tests).
    • 6 new tests: a withdrawn request let go with nothing skipped; a removal by someone else noticed once, and your own removal not noticed; approval and removal between two checks; a removal of a request never seen; an older state file; the status wording.
    • Removing each fix made its test fail, for all 7 fixes.
  • Marker guard: passes.

šŸ¤– Generated with Claude Code


Summary by cubic

Adds CLI support for withdrawn and removed review requests, and shows a TUI notice when someone else removes your approved contribution from a workspace.

Now tracks approved requests (capped at 200) so a retired status arriving later is noticed once, with who removed it and why; your own removals and withdrawals are silent. Requests with withdrawn or retired statuses are read as known rows instead of skipped, so the request list stays complete and pending ids are released. State files written before this change still load, and workspace review status displays the new statuses with the remover's name and reason.

Docs updated to explain that withdrawing and removing happen in the web app.

Ship this before the backend: the CLI reviewed in #1685 skips rows with these statuses and counts them as unreadable.