Restore Project

Feature Owner: scorevi (Sean Patrick Caintic)
Module: Diagnose
Priority: P1
Sprint #12: Fully Implemented
Date: 2026-06-29


EXECUTIVE SUMMARY

What is this feature? Restore Project allows creators to recover archived quests and adventures by reverting their publishing_status from "archived" back to "draft." It is the inverse operation of archiving and serves as the safety net in the content lifecycle.
Why does it matter? Creators sometimes archive content prematurely or change their mind about retiring material. Without restore, archiving becomes a one-way path that carries the same risk as deletion.
What's the MVP scope? Single endpoint that reverts publishing_status to "draft" for any archived quest or adventure, with ownership verification and input validation.


1. USER PAIN POINT & SOLUTION

Current State (Without Feature)

Archiving is permanent in effect — once content is archived, there is no way to bring it back to an active state without manual database intervention.

Pain Point

Type

Impact

Emotional

Regret after archiving content that might still be useful

Functional

No recovery path for mistakenly archived content

Business

Lost content represents wasted creation effort and AI generation costs

Future State

Creators can browse their archived content and restore any item back to "draft" status with a single click. The full content lifecycle is reversible until permanent deletion.

Marketing Hook

"Changed your mind? One click to bring it back. Archive is safe because restore is instant."


2. 4D FRAMEWORK MAPPING

Phase

Mapping

Diagnose

Re-evaluate archived content against current standards

Design

Restored content re-enters the design workflow as draft

Develop

Resume development on previously archived material

Deliver

Restored content can be republished and exported


3. USER FLOWS

Entry Point

  • TrashContent.tsx → "Restore" button on archived item

Success Criteria

  • Content publishing_status changes from "archived" to "draft"

  • Content reappears in active library

  • Ownership verified before restore

Main Flow

  1. Creator navigates to TrashContent.tsx (archived content list)

  2. Clicks "Restore" on a quest or adventure

  3. System calls verifyContentOwnership() (implicit via RLS)

  4. PATCH /api/creator/restore-content updates publishing_status to "draft"

  5. Content reappears in main library

  6. UI updates to reflect restored state

Edge Cases

  • Restore non-archived content: Status changes to "draft" (may overwrite "published")

  • Restore content owned by another creator: verifyContentOwnership() blocks

  • Restore already-deleted content: 404 (content no longer exists)

Decision Points

  • Always restores to "draft" (never "published") — creator must manually republish

  • No confirmation dialog in MVP (single-click restore)


4. INFORMATION ARCHITECTURE

Primary

  • Content ID (UUID), content type (quests | adventures), publishing_status

Secondary

  • Restore timestamp, restored by (creator_id)

Actions

  • Restore (from TrashContent.tsx)


5. WIREFRAMES

Excluded — existing UI (TrashContent.tsx Restore button).


6. WIREFLOWS

Excluded — existing UI implemented.


7. PROTOTYPE

Excluded — feature is fully implemented.


8. BACKEND SCHEMA

Status Transition

archived → restore → draft → (creator publishes) → published

restoreContentSchema

{ content_id: UUID, content_type: z.enum(["quests","adventures"]) }

Same schema shape as archiveContentSchema. The route uses a raw Supabase update call, bypassing Zod schema validation for the status field (consistent with archive approach).


9. API ENDPOINTS

Method

Path

Auth

Purpose

File

PATCH

/api/creator/restore-content

Clerk

Revert status to "draft"

60 lines

restore-content (PATCH)

  • Schema: restoreContentSchema{ content_id: UUID, content_type: z.enum(["quests","adventures"]) }

  • Logic:

    1. Validate input with restoreContentSchema

    2. verifyContentOwnership() — confirms creator owns the content

    3. Raw Supabase call: .from(content_type).update({ publishing_status: "draft" }).eq("id", content_id)

    4. Returns success/error response

  • Note: Always restores to "draft", regardless of previous status


10. DATA REQUIREMENTS

Frontend Needs

  • Restore button in TrashContent.tsx

  • Visual feedback on restore success

  • Automatic list refresh after restore

API Calls

  • PATCH /api/creator/restore-content

  • GET /api/creator/list-archived (refresh after restore)

Caching

  • Archived list: Invalidate on restore


11. PERFORMANCE CONSIDERATIONS

DB Optimization

  • Single UPDATE operation, no joins

  • publishing_status index benefits both archive and restore operations

Response Time

  • Sub-50ms for single-item restore (simple UPDATE)


12. SECURITY & AUTHORIZATION

Access Control

  • Clerk authentication required

  • verifyContentOwnership() ensures creator can only restore their own content

  • RLS policies prevent cross-creator access

Auth Logic

  • Ownership verified before status change

  • No elevation of privilege (cannot restore another creator's content)

Validation

  • Input validated via restoreContentSchema (Zod)

  • Status update is raw SQL (no Zod on output) — consistent with archive route pattern


13. ERROR HANDLING

Error

Response

Invalid content_id (not UUID)

400 — Zod validation error

Invalid content_type

400 — Zod enum validation error

Content not found

404

Content not owned by requester

403 — verifyContentOwnership() failure

Content already deleted (not archived)

404 — row doesn't match


14. TESTING CHECKLIST

Happy Path

  • Restore archived quest → status changes to "draft"

  • Restore archived adventure → status changes to "draft"

  • Restored content appears in active library

  • Restored content disappears from archived list

Edge Cases

  • Restore content that is already "draft" (idempotent)

  • Restore content that is "published" (downgrades to draft)

  • Attempt to restore content owned by another creator (blocked)

  • Restore with invalid UUID format (blocked by Zod)

  • Restore with invalid content_type (blocked by Zod)


15. OPEN QUESTIONS

  • Should restore preserve the previous status (e.g., restore "published" to "published") instead of always defaulting to "draft"?

  • Should restore require a confirmation step to prevent accidental recovery?


16. OUT OF SCOPE

  • Bulk restore (multiple items at once)

  • Restore with version history (restore to specific point in time)

  • Restore audit log (who restored what and when beyond timestamp)


17. SUCCESS METRICS

  • Restore rate (% of archived items that get restored)

  • Time from archive to restore

  • Zero support tickets for "accidentally archived content"


18. DEPENDENCIES

  • Archive Project feature (1.4) — restore only applies to archived content

  • verifyContentOwnership() utility function

  • TrashContent.tsx UI component


19. TIMELINE

Completed — Feature is fully implemented in Sprint #12.


Document Version

1.0 - Initial version - 2026-06-29 08:16 UTC

1.1 - Added Document Version section and update author to have full name - 2026-06-29 08:44 UTC


Was this article helpful?