修复项目无法删除问题
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# Task: Resolve ended project deletion semantics
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20260813-ended-project-delete-6a714b5b
|
||||
- Mode: Feature
|
||||
- Branch: main
|
||||
- Worktree: D:\Datas\PythonProjects\XQKqueue
|
||||
- Base commit: cb88a6901219c5a489eced946c8fe87427f3dedb
|
||||
- Owner: codex
|
||||
- Status: Complete; ready for integration
|
||||
|
||||
## Scope
|
||||
|
||||
- Replace the contradictory `PROJECT_HAS_HISTORY` outcome for ended projects with guarded archival while retaining hard deletion for projects without queue history.
|
||||
- Hide archived projects from every ordinary administrator, staff, visitor, browser-display, and Android-display project list or direct operational entry.
|
||||
- Preserve archived projects and their sessions, tickets, batches, simulations, and audit/history joins so administrators can still inspect retained history.
|
||||
- Add an explicit schema state, server transaction/query guards, accurate administrator UI wording, and regression coverage at the real PostgreSQL/HTTP seams.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- User-confirmed product semantics: an ended project with history is archived, retains history, and must not appear in any ordinary project list.
|
||||
- Projects with no queue history remain eligible for irreversible hard deletion; projects with history must be `ENDED` before archival.
|
||||
- Use an explicit nullable `archived_at` field rather than implicit GORM soft-delete scopes; every ordinary project query must make visibility intentional, while history joins intentionally retain archived projects.
|
||||
- Archived projects cannot be operated, edited, granted to staff, resolved by public code/token, or restored through this task.
|
||||
- Project code and display-token uniqueness remain global after archival; this task does not add code reuse, restoration, a purge flow, or an archived-project management list.
|
||||
- PostgreSQL remains the authority for the archival transaction and state constraint. Preserve existing queue-history and personal-data retention rules.
|
||||
- Concurrent write ownership is split by file: archival core, ordinary query visibility, and web wording/tests. The root task owns integration, task documentation, any explicitly coordinated history authorization seam, and final verification.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Added migration `000015_project_archival` and `Project.ArchivedAt`. PostgreSQL enforces that only `ENDED` projects may carry an archive timestamp; project codes and display tokens remain globally unique.
|
||||
- Changed the existing project removal transaction so an unused project is still hard-deleted, an operational project that is not ended returns `PROJECT_MUST_BE_ENDED`, and an ended project with queue history is archived atomically. Archival removes staff grants, writes `PROJECT_ARCHIVED`, and retains the project row, sessions, tickets, batches, simulations, and project-scoped audit entries.
|
||||
- Hid archived projects from administrator overview data, account grant lists, staff project/queue entry points, visitor project and active-phone lookup results, browser/Android display overview, and direct display code/token resolution. Direct administrator maintenance and operational write paths also reject archived projects.
|
||||
- Kept administrator history joins and private terminal-ticket lookups intentionally able to resolve the retained project. These are historical records, not ordinary project directories.
|
||||
- Prevented account create/update calls from granting archived projects, including a row-lock protocol that serializes grant creation against archival.
|
||||
- Updated the administrator action to the accurate neutral wording “移除项目” and made the confirmation copy distinguish archival from permanent deletion. When a staff user's final project disappears, the web client now clears the cached selection, stops polling the archived project, and hides stale queue data.
|
||||
- Prevented the demo seed reset from matching and erasing an archived project's retained history.
|
||||
|
||||
## Verification
|
||||
|
||||
- TDD red evidence:
|
||||
- The initial PostgreSQL deletion integration test did not compile because `Project.ArchivedAt` was absent.
|
||||
- Administrator deletion-flow tests failed against the former delete-only wording.
|
||||
- The staff-page regression test showed that an archived final project remained in `sessionStorage` and stale queue content remained visible.
|
||||
- PostgreSQL 17, disposable fresh database: `go test -p 1 ./internal/database ./internal/httpapi -count=1` passed, including migration shape, hard deletion, ended-project archival, history/audit retention, grant removal, visibility, and rejection cases.
|
||||
- Server: `go test ./... -count=1`, `go vet ./...`, and `go build ./...` passed.
|
||||
- Web: `vitest run` passed 18 files / 70 tests; both application and Node TypeScript configurations passed `tsc --noEmit`; `vite build` passed.
|
||||
- Repository: `git diff --check` passed; line-ending notices are informational working-copy warnings.
|
||||
- Independent Sol High read-only final review: `PASS`, with no P0/P1 findings. The initial review found the ambiguous “归档项目” action label because unused projects are hard-deleted; it was corrected to “移除项目”, retested, and passed re-review.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Deploy migration 15 before the archive-aware server/application. After the first project is archived, rolling back to an older binary or dropping `archived_at` would expose that project in ordinary lists again; such a rollback requires a compatibility filter or a deliberate data migration.
|
||||
- Pre-existing display event streams are not proactively disconnected at the instant of archival. All new display resolution and operational writes are rejected, so no new project activity is produced; add project-level stream invalidation later if instantaneous connection termination becomes a requirement.
|
||||
- Add a dedicated PostgreSQL concurrency regression for the grant-write versus archive interleaving if this area receives further authorization changes. The current sorted `FOR KEY SHARE` grant locks and project `FOR UPDATE` archive lock were independently reviewed as correct.
|
||||
- Restoration, code reuse, permanent history purge, and a separate archive-management screen remain intentionally out of scope and require explicit retention/product decisions.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- Target: `.project-docs/40-domain/business-rules.md`. Record the confirmed removal rule: unused projects may be hard-deleted; projects with retained queue history must be ended and are then archived with history preserved.
|
||||
- Target: `.project-docs/20-architecture/data-flow.md`. Record the visibility split between ordinary active-project queries, which must filter `archived_at`, and historical joins, which intentionally retain archived project identity.
|
||||
- Target: `.project-docs/20-architecture/decisions/`. Consider an accepted decision for explicit archival state instead of implicit ORM soft-delete behavior, including the global code/token uniqueness and rollback constraints.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Reflection: Resolve destructive semantics before implementing deletion
|
||||
|
||||
- Task: `20260813-ended-project-delete-6a714b5b`
|
||||
- Date: 2026-08-13
|
||||
- Status: Confirmed workflow lesson
|
||||
|
||||
## What happened
|
||||
|
||||
The first project-removal implementation treated any queue history as a permanent hard-delete blocker and told the administrator to end the project. That message was internally contradictory: ending the project did not change the blocker, so the user could never complete the requested removal workflow.
|
||||
|
||||
The database constraints correctly signaled that operational and audit history must not be casually cascaded, but they did not by themselves define the product meaning of “delete.” The missing decision was whether the user intended destructive purge, operational removal with retained history, or simple status closure.
|
||||
|
||||
## Why it matters
|
||||
|
||||
Deletion labels often conceal several distinct domain actions. Implementing the safest database behavior without first resolving the user-visible lifecycle can create a technically guarded endpoint that has no usable success path. It can also force a later schema and query audit because archival affects every list, authorization path, cache, direct lookup, and background tool—not just the delete handler.
|
||||
|
||||
## Better workflow
|
||||
|
||||
Before implementing a destructive command, establish and test a small lifecycle table: entity state, presence of retained history, requested action, resulting visibility, retained data, and whether restoration or identifier reuse is allowed. Treat ordinary listings and historical attribution as separate query contracts. Then inventory all readers and writers against that contract before editing the endpoint.
|
||||
|
||||
## Promotion candidate
|
||||
|
||||
- Target: repository planning/review guidance for destructive lifecycle changes.
|
||||
- Proposal: require an explicit delete/archive/purge semantics check and an ordinary-list versus history-query inventory before implementation.
|
||||
- Evidence: the original guarded hard-delete path could never succeed for the exact ended project it instructed the user to create.
|
||||
- Future impact: reduces contradictory workflows and prevents archived data from leaking through secondary lists, stale grants, caches, seeds, or direct identifiers.
|
||||
- Human confirmation: required before promoting this lesson into shared canonical guidance.
|
||||
Reference in New Issue
Block a user