Files
XQKqueue/.project-docs/30-worklog/tasks/20260813-ended-project-delete-6a714b5b.md

64 lines
6.7 KiB
Markdown

# 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.