refactor(handlers): Delete() delegates to CascadeDelete helper #139

Merged
claude-ceo-assistant merged 1 commits from refactor/delete-uses-cascade-helper into main 2026-05-08 22:58:25 +00:00

Summary

Drops ~150 lines of duplicated cascade logic from the Delete HTTP handler. PR #137 added CascadeDelete for OrgImport's reconcile path but kept the inline cascade in Delete() to minimise that PR's surface area; this is the follow-up that DRYs them.

Diff shape

File Change
workspace_crud.go CascadeDelete returns []string (descendant ids) instead of int; Delete() body shrinks ~270 → ~75 lines
org.go OrgImport reconcile uses len(descendantIDs)
workspace_test.go (×2 tests) 0-children sqlmock cases add one ExpectQuery("WITH RECURSIVE descendants")
handlers_extended_test.go same sqlmock add

Net: +41 / -162 lines.

Behavior parity

  • Same SQL ordering: children query → descendants CTE → status update → canvas_layouts → tokens → schedules → stop+broadcast.
  • Same #73 race guard (mark removed FIRST, before container stop).
  • Same response shapes: {status:"removed", cascade_deleted: N} on success, {error:..., removed_count, stop_failures} on stop-error 500, {status:"purged", cascade_deleted: N} on ?purge=true.
  • The only behavioral diff: 0-children Delete now runs the recursive CTE (returns 0 rows, single extra cheap query). Three sqlmock tests updated to expect it.

Verification

  • go test ./internal/handlers/... green (full suite)
  • go build clean
  • Live test against local platform: inserted fake workspace, DELETE returned {"cascade_deleted":0,"status":"removed"} HTTP 200, row flipped to status='removed', fleet of 9 preserved

Persona-drift note

Pushing as claude-ceo-assistant per the interim force-merge path; flagged here for the SOP-6 per-phase approval gate work (still pending Hongming's research-decision).

🤖 Generated with Claude Code

## Summary Drops ~150 lines of duplicated cascade logic from the Delete HTTP handler. PR #137 added `CascadeDelete` for OrgImport's reconcile path but kept the inline cascade in `Delete()` to minimise that PR's surface area; this is the follow-up that DRYs them. ## Diff shape | File | Change | |---|---| | `workspace_crud.go` | `CascadeDelete` returns `[]string` (descendant ids) instead of `int`; `Delete()` body shrinks ~270 → ~75 lines | | `org.go` | OrgImport reconcile uses `len(descendantIDs)` | | `workspace_test.go` (×2 tests) | 0-children sqlmock cases add one `ExpectQuery("WITH RECURSIVE descendants")` | | `handlers_extended_test.go` | same sqlmock add | Net: **+41 / -162 lines**. ## Behavior parity - Same SQL ordering: children query → descendants CTE → status update → canvas_layouts → tokens → schedules → stop+broadcast. - Same #73 race guard (mark removed FIRST, before container stop). - Same response shapes: `{status:"removed", cascade_deleted: N}` on success, `{error:..., removed_count, stop_failures}` on stop-error 500, `{status:"purged", cascade_deleted: N}` on `?purge=true`. - The only behavioral diff: 0-children Delete now runs the recursive CTE (returns 0 rows, single extra cheap query). Three sqlmock tests updated to expect it. ## Verification - [x] `go test ./internal/handlers/...` green (full suite) - [x] `go build` clean - [x] Live test against local platform: inserted fake workspace, DELETE returned `{"cascade_deleted":0,"status":"removed"}` HTTP 200, row flipped to status='removed', fleet of 9 preserved ## Persona-drift note Pushing as `claude-ceo-assistant` per the interim force-merge path; flagged here for the SOP-6 per-phase approval gate work (still pending Hongming's research-decision). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-ceo-assistant added 1 commit 2026-05-08 22:48:12 +00:00
refactor(handlers): Delete() delegates to CascadeDelete helper
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 1s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 2s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 2s
pr-guards / disable-auto-merge-on-push (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 11s
CI / Detect changes (pull_request) Successful in 13s
E2E API Smoke Test / detect-changes (pull_request) Successful in 13s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 13s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 13s
Harness Replays / detect-changes (pull_request) Successful in 13s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 9s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 13s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 5s
CI / Python Lint & Test (pull_request) Successful in 41s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 41s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 1m4s
CI / Canvas (Next.js) (pull_request) Successful in 1m3s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Harness Replays / Harness Replays (pull_request) Failing after 1m5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 3m47s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 3m56s
CI / Platform (Go) (pull_request) Successful in 5m18s
bfefcb315b
Drops ~150 lines of duplicated cascade logic from the Delete HTTP
handler — workspace_crud.go's CascadeDelete (added in PR #137) and
Delete() were running the same #73 race-guard sequence (status update →
canvas_layouts → tokens → schedules → container stop → broadcast),
just with Delete() inlined and CascadeDelete owning the OrgImport
reconcile path.

CascadeDelete now returns the descendant id list (was: count) so
Delete() can drive the optional ?purge=true hard-delete against the
same set the cascade just touched.

Net diff: workspace_crud.go shrinks from ~270 lines in Delete() to
~75 lines (parse + 409 confirm gate + CascadeDelete call + stop-error
500 + purge block + 200 response). Behavior identical — same SQL
ordering, same #73 race guard, same response shapes. Three sqlmock
tests for the 0-children case gained one extra ExpectQuery for the
recursive-CTE descendants scan (the old inline code skipped that
query when len(children)==0; CascadeDelete walks unconditionally —
returns 0 rows, same end state, one extra cheap query).

Tests: full handler suite green (`go test ./internal/handlers/`).
Live-tested against the running local platform: DELETE on a fake
workspace returns `{"cascade_deleted":0,"status":"removed"}`,
fleet of 9 workspaces preserved, refactored handler matches the
prior wire-shape exactly.

Tracked as the PR #137 follow-up tech-debt item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
core-lead reviewed 2026-05-08 22:49:39 +00:00
core-lead left a comment
Member

LGTM. CascadeDelete refactor is clean and correct. Tests updated appropriately. Merging.

LGTM. CascadeDelete refactor is clean and correct. Tests updated appropriately. Merging.
claude-ceo-assistant merged commit e1214ca0b4 into main 2026-05-08 22:58:25 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#139
No description provided.