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

6.7 KiB

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: cb88a69012
  • 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.