Skip to content

fix(server): let archived-only projects delete without force - #5037

Closed
kakismash wants to merge 1 commit into
pingdotgg:mainfrom
kakismash:fix/project-delete-archived-threads
Closed

fix(server): let archived-only projects delete without force#5037
kakismash wants to merge 1 commit into
pingdotgg:mainfrom
kakismash:fix/project-delete-archived-threads

Conversation

@kakismash

@kakismash kakismash commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What changed

project.delete in apps/server/src/orchestration/decider.ts now requires force=true only when the project has non-archived live threads. Archived threads no longer block deletion, but they are still included in the synthesized thread.delete cascade so no thread row outlives its project.

Added a regression test: a project whose only thread is archived deletes without force and emits [thread.deleted, project.deleted].

Why it should exist

Fixes #4632. The delete invariant counts every thread with deletedAt === null, but the client shell snapshot deliberately excludes archived threads (listActiveThreadRows filters archived_at IS NULL, and thread.archived deltas evict them from the store). So the sidebar sees an empty project, sends a plain delete without force, and the server rejects it with the raw invariant text — the user has no way out of the loop from the UI.

Gating the invariant on user-visible threads makes the server's definition of "empty" match what the client (and user) can actually see. Cascading over archived threads too is deliberate: project.deleted does not cascade in the projection, so skipping them would leave invisible, undeletable rows that the Archive panel silently drops (it groups archived threads by non-deleted projects).

No UI change.

Testing

  • vitest run src/orchestration in apps/server: 18 files, 203 tests pass
  • typecheck clean

🤖 Generated with Claude Code


Note

Medium Risk
Changes orchestration delete invariants and cascade behavior for archived threads; scope is limited to project deletion logic with a targeted regression test.

Overview
project.delete now treats a project as empty for the force check only when it has no non-archived live threads. Archived threads no longer block a plain delete, aligning server “empty” with what clients show in project views (archived_at IS NULL filters).

Deletion still cascades thread.delete over every live thread (including archived ones) before project.deleted, so archived rows are not left behind when the project goes away.

Adds a regression test: a project whose only remaining thread is archived deletes without force and emits thread.deleted then project.deleted.

Reviewed by Cursor Bugbot for commit fd0d80d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Allow project.delete without force when only archived threads exist

Previously, a non-forced project.delete was blocked whenever any non-deleted threads existed, including archived ones. Now, only visible (non-deleted, non-archived) threads block plain deletes.

  • In decider.ts, the invariant check now uses visibleThreads (non-deleted, non-archived) instead of all live threads.
  • Archived threads are still cleaned up: liveThreads (non-deleted) drives the cascade of thread.delete events before project.deleted.
  • Behavioral Change: projects with only archived threads now delete without requiring the force flag.

Macroscope summarized fd0d80d.

The project.delete invariant counted every non-deleted thread, but the
client shell snapshot excludes archived threads (and thread.archived
deltas evict them), so the sidebar sees an empty project and issues a
plain delete that the server then rejects with a raw invariant error.

Gate the force requirement on non-archived threads only, while still
cascading thread.delete over archived ones so no thread row outlives
its project (the projection does not cascade project.deleted).

Fixes pingdotgg#4632

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 445b6ee4-2f30-43e1-aed0-beff3d2c14e8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Jul 30, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR modifies project deletion behavior by allowing deletion without force when only archived threads remain. While well-tested and scoped, runtime behavior changes to deletion semantics warrant human review to confirm the intended UX.

You can customize Macroscope's approvability policy. Learn more.

@t3dotgg

t3dotgg commented Sep 4, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This was closed as part of an automated cleanup pass. If you believe it was closed in error, reply here and we will get it reopened.

Closing as superseded by #8798, which is on main. Project removal now warns that archived history will be deleted and sends force only after confirmation. This keeps archived-only projects removable without weakening the server safeguard for other callers.

@t3dotgg t3dotgg closed this Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Unable to remove a project with zero threads

2 participants