Skip to content

R5 — Moderate, enrich, and launch #52

Description

@mrboxs

R5 — Moderate, enrich, and launch

Execution checklist for docs/roadmap.md R5 + docs/prd.md R5. Single issue for the whole release — do not split DB/API/UI into separate branches. Branch: feat/r5-moderate-enrich.

Outcome

One admin can turn minimal R4 submissions into complete published listings. This is the launch gate.

Context

  • project_submission (packages/db/src/schemas/submission.ts) already has rejectionReason, moderatedAt, moderatedBy — reuse them, do not add moderation fields to project for launch.
  • adminProcedure already exists (packages/api/src/procedures.ts, 401 no session / 403 non-admin) — use it for every moderation mutation.
  • submissionRouter (packages/api/src/routers/submission.ts) currently has only create + list on protectedProcedure — extend with admin + resubmit procedures below.
  • /_panel shell (apps/web/src/routes/_panel/route.tsx, my-submissions.tsx) + sidebar/data-table from PR Enhance sidebar and submission data table components #51 already exist. Roadmap text says /dashboard — reuse /_panel as the admin surface unless there is a reason to rename; document the choice.
  • R4 canonicalisation helper: canonicalizeGithubUrl in @altstack/shared/lib/github. Reuse it in approve/reject/resubmit paths.

Work

  • Keep moderation fields on project_submission (rejectionReason, moderatedAt, moderatedBy); do not add them to project for launch.
  • Add minimal audit_log: actor ID, action, target submission/project ID, optional metadata/reason, timestamp. Records moderation mutations only (no auth-activity events).
  • Add protected admin contracts: list pending submissions, approveSubmission, rejectSubmission. Keep remove as an internal admin operation on project if needed before launch.
  • approveSubmission input is the admin enrich step: tagline, short description, logo URL, optional website (prefilled from submission, editable), optional Markdown content, slug (prefilled from projectName, editable), and 1–3 existing category slugs.
  • approveSubmission handler re-checks canonical duplicate against both project.repositoryUrl and project_submission.repositoryUrl, refetches fresh GitHub metadata, then in one transaction inserts the published project + repository metadata + category links, marks the submission approved, and writes exactly one audit event. Slug collision resolves with a numeric suffix or 409 if unresolvable; document the choice in the issue/PR.
  • Enforce admin authorization in each mutation via adminProcedure. Invalid state transitions (e.g. approve an already-approved, reject a non-pending) return 409.
  • Build the smallest usable admin surface in the protected /_panel route (see Context on /dashboard naming): paginated pending-submissions list (reuse data-table), approve form with enrich fields, reject dialog requiring a reason.
  • Let the original submitter correct a rejected submission by editing only name/repository/website. Its narrow resubmitSubmission procedure (owner-only, protectedProcedure + submitter check) atomically clears the rejection reason and sets status back to pending. General editing of published projects remains R6.
  • Add moderation and audit tests: approve happy path, reject with reason, non-admin 403, unauthenticated 401, invalid transition 409, duplicate canonical on approve 409, resubmit rejectedpending (+ non-owner 403), exactly-one-audit-event per mutation in the same transaction. Run the complete smoke journey: seed → browse → search → sign in → submit (3 fields) → enrich/approve/reject → public/private assertions.
  • Record deployment notes: required GitHub OAuth callback URL, admin bootstrap process (how the first admin role is assigned), and known limitations.

Done when

  • An admin enriches + approves a submission and it immediately appears in browse/search as a complete listing.
  • An admin rejects with a reason; the submitter sees it privately (never public).
  • A valid re-submission moves rejected back to pending for another review.
  • Every moderation mutation creates exactly one audit event in the same transaction.
  • vp check, vp run -r test, and vp run -r build pass on the release branch.

Explicitly not in R5

Full dashboard CRUD, audit events for auth activity, featured projects, category management, analytics, queues, or scheduled sync.

Conventions

  • Branch: feat/r5-moderate-enrich
  • vp install before starting; vp check, vp run -r test, vp run -r build before merging.
  • Forward-only additive migration. Protected/admin procedures enforce auth; beforeLoad is UX guard only.
  • Error codes: 400 invalid input, 401 no session, 403 no permission, 404 unavailable public data, 409 duplicate/conflict. Public list/detail always applies status = 'published' on the server.
  • Lifecycle: pending → approve → published / reject → rejected → valid resubmit → pending; published → remove → removed (terminal). No draft for user submissions in R4/R5.

Close-out (fill on merge)

  • Shipped / version:
  • Deliberately not shipped:
  • Migration + rollback/forward-fix note:
  • Known limitations + evidence needed for R6/R7:
  • Test + smoke steps that passed:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions