Skip to content

Soft-delete / Trash support: SDK + CLI#463

Open
yeldarby wants to merge 11 commits intomainfrom
soft-delete
Open

Soft-delete / Trash support: SDK + CLI#463
yeldarby wants to merge 11 commits intomainfrom
soft-delete

Conversation

@yeldarby
Copy link
Copy Markdown
Contributor

@yeldarby yeldarby commented Apr 24, 2026

Summary

Adds project and version lifecycle management mirroring the soft-delete and Trash features added to the web app in roboflow/roboflow#11131. Deleting a project or version now moves it to Trash (30-day retention) and cancels any in-flight training jobs on it; items can be restored within the retention window.

  • Project-level: Project.delete() / Project.restore()
  • Version-level: Version.delete() / Version.restore()
  • Workspace-level: Workspace.trash(), Workspace.restore_from_trash()
  • CLI: roboflow project delete|restore, roboflow version delete|restore, roboflow trash list

Details

New rfapi functions (roboflow/adapters/rfapi.py):

  • delete_project(api_key, workspace_url, project_url)DELETE /:workspace/:project
  • delete_version(api_key, workspace_url, project_url, version)DELETE /:workspace/:project/:version
  • list_trash(api_key, workspace_url)GET /:workspace/trash
  • restore_trash_item(api_key, workspace_url, item_type, item_id, parent_id=None)POST /:workspace/trash/restore

SDK methodsProject.delete() returns the server response. Project.restore() looks the project up by slug in the workspace Trash and restores it (raises RuntimeError if not found). Same shape for Version. For scripts without a live handle, Workspace.trash() returns the list of items and Workspace.restore_from_trash(type, id, parent_id) restores by id.

CLI commands — destructive commands (project delete, version delete) prompt for confirmation interactively and accept --yes / -y for scripted use. All commands honor --json for structured output and use output_error() with actionable hints on every error path (per the Agent Experience checklist in CONTRIBUTING.md).

Permanent deletion is intentionally web-UI-only

Emptying Trash or immediately deleting a single Trash item destroys data irrecoverably, so it's not exposed on the SDK or CLI — those actions live only in the Roboflow app's Trash view, which has an explicit confirmation dialog. Items left in Trash are cleaned up automatically after 30 days.

Guard tests ensure rfapi.trash_delete_immediately, rfapi.empty_trash, Workspace.delete_from_trash, Workspace.empty_trash, roboflow trash empty, and roboflow trash delete stay off the SDK/CLI surface going forward.

Backward compatibility

Purely additive — no existing APIs changed. The new routes require project:update (for projects) or version:update (for versions) scopes, which most existing keys already have.

Test plan

  • All 479 unit tests pass (python -m unittest) — 20 new tests covering project / version / trash CLI handlers and guard tests for the removed permanent-delete surface
  • ruff check + ruff format --check clean on all new/modified files
  • mypy roboflow clean
  • End-to-end smoke test against staging API (test_soft_delete_flow.py, in-tree but untracked) drives the CLI through delete project → restore project → delete version → restore version, with real HTTP round-trips to localapi.roboflow.one
  • CLI --help output lists the new commands (roboflow project --help, roboflow version --help, roboflow trash --help)
  • Audited against the Agent Experience checklist in CONTRIBUTING.md: --json, no interactive prompts when --yes/--json set, output_error(..., hint=..., exit_code=N) on every failure path, exit codes 0/1/2/3

Dependencies

Blocked on the backend soft-delete PR: roboflow/roboflow#11131. That PR adds the server-side endpoints this SDK calls. Do not merge this until that one is in prod.

Companion docs PR

roboflow/roboflow-dev-reference#5 — CLI, REST API, and Python SDK docs pages for the new flow.

🤖 Generated with Claude Code

yeldarby and others added 7 commits April 23, 2026 21:41
Adds project and version lifecycle management mirroring the soft-delete
and Trash features that just landed in the web app. Projects and
versions are moved to Trash on delete (30-day retention) and can be
restored within the retention window; any in-flight training jobs are
cancelled automatically on the server.

New rfapi functions:
- delete_project, delete_version
- list_trash, restore_trash_item
- trash_delete_immediately, empty_trash

New SDK methods:
- Project.delete(), Project.restore()
- Version.delete(), Version.restore()
- Workspace.trash(), Workspace.restore_from_trash(),
  Workspace.delete_from_trash(), Workspace.empty_trash()

New CLI commands:
- roboflow project delete / restore
- roboflow version delete / restore
- roboflow trash list / empty / delete

All existing tests pass; new commands have --yes / -y to skip the
confirmation prompt for scripted use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- CLI-COMMANDS.md: new "Delete and restore" section covering project,
  version, and trash commands; added `trash` to the command-groups table.
- tests/cli/test_project_handler.py: registration + delete/restore
  unit tests.
- tests/cli/test_version_handler.py: registration + delete/restore
  unit tests (including the parent-project disambiguation lookup).
- tests/cli/test_trash_handler.py: full coverage for list/empty/delete
  commands with mocked rfapi.

SDK-side (`docs/core/*.md`) is auto-generated via mkdocstrings from
docstrings, so no changes needed there — the new methods are already
documented via their docstrings.

479 tests pass (20 new), ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Permanent deletion destroys data irrecoverably. Exposing it on the
public API — and through the SDK/CLI that wraps it — makes it one stray
curl or for-loop away from catastrophe, with no undo. It stays
available only in the web UI's Trash view, which has an explicit
confirmation dialog; items left in Trash are cleaned up automatically
after 30 days either way.

Removed:
- rfapi.trash_delete_immediately(), rfapi.empty_trash()
- Workspace.delete_from_trash(), Workspace.empty_trash()
- roboflow trash empty, roboflow trash delete commands
- Corresponding tests (replaced with guard tests that fail if the
  permanent-delete surface is ever re-added by accident)
- CLI-COMMANDS.md references

Kept (soft-delete + restore + list only):
- Project.delete() / Project.restore()
- Version.delete() / Version.restore()
- Workspace.trash() / Workspace.restore_from_trash()
- roboflow project delete|restore
- roboflow version delete|restore
- roboflow trash list

Paired with the corresponding backend removal in
roboflow/roboflow#11131 — the public REST API no longer exposes
POST /:ws/trash/deleteImmediately or POST /:ws/trash/empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CONTRIBUTING.md's Agent Experience checklist asks that every
output_error() include an actionable hint. The delete/restore/list
commands shipped with hints on some paths but not others. Fills in
the gaps so agents get a consistent "what went wrong AND what to do"
pair on every failure:

- resolver ValueError: tell them the valid shorthand formats
- API RoboflowError: point at the specific scope that's likely missing
  ('project:update' / 'version:update' / 'project:read')
- 'not in trash' on version restore: additionally note that a parent
  project in trash must be restored first

No behavior change — same error paths, same exit codes, same JSON
schema. Just more useful text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The delete_project() docstring still mentioned trash_delete_immediately
as a follow-on action, but that helper was removed. Replace with a note
about the 30-day cleanup cron so the lifecycle is documented without
pointing at a symbol that doesn't exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yeldarby yeldarby requested review from tonylampada April 24, 2026 02:18
@yeldarby yeldarby marked this pull request as ready for review April 24, 2026 02:18
yeldarby and others added 4 commits April 23, 2026 23:30
Mirrors delete_project() / delete_version() — wraps DELETE
/:workspace/workflows/:workflowUrl on the public API (added in the
paired backend PR). Workflow restore was already available via
restore_trash_item(..., \"workflow\", ...) through the generic trash
endpoint, so this closes the symmetry gap.

Intentionally not adding Workflow.delete() / .restore() SDK methods or
a `roboflow workflow delete|restore` CLI subcommand in this PR — there
is no Workflow handle object in the SDK yet, and adding that is
significant scope. Agents/scripts that want workflow lifecycle control
can call rfapi.delete_workflow() and Workspace.restore_from_trash()
directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the CLI surface for soft-delete — workflows now have the same
delete/restore subcommands as projects and versions. Both commands:

- call rfapi.delete_workflow / rfapi.restore_trash_item under the hood
  (the public API endpoints added in the paired backend PR)
- honor --json for structured output
- honor --yes / -y on delete to skip the confirmation prompt for
  scripted use
- emit output_error(..., hint=..., exit_code=N) with actionable hints
  per the Agent Experience checklist: which scope to check, what to
  run to see what's in Trash, etc.
- accept either the workflow URL slug (e.g. slow-webhooks) or its
  Firestore id (e.g. wf_abc123) on restore

Restore looks the workflow up in the workspace Trash by URL (falling
back to id), just like project/version restore. CLI-COMMANDS.md gets
a one-line update in the "Delete and restore" section.

485 tests pass (6 new: 2 registration + 4 handler).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant