Epic 2 — Project Management

Covers project creation, editing, listing with search and filters, soft-delete (archive/deactivate), running-state guard on archive, restore/reactivate, and the global zero-state onboarding flow. Hard-delete (DELETE /projects/{id}) is intentionally absent from the UI. Depends on planned projects.archived_at TIMESTAMPTZ column (migration V7).

Personas: BU (create/edit/archive) OP (all) AU (read-only)

Shared modules: OptimisticMutationHelper CorrelationChip LastSyncedBadge EnvProvenance

Non-goals: Hard-delete of projects. Run history under an archived project remains intact and fully reachable from Epics 8 and 15.

Story 2.1 — View Project List

As a
BU
I want
to see all my projects in a searchable, filterable list
So that
I can navigate quickly to the correct project
Scenario: Default project list loads
Giventhe user is authenticated and the tenant has at least one active project; no filter chips are active Whenthe user navigates to /projects ThenGET /projects is issued; one card per project is rendered showing name, github_repo_full_name, created_at, and the most recent runs.status Andprojects with projects.archived_at IS NOT NULL are hidden by default
Scenario: Show archived projects via filter chip
Whenthe user activates the Show archived filter chip ThenGET /projects?include_archived=true is issued; archived projects appear tagged with an Archived badge and run history links remain intact
Endpoint / DBPurpose
GET /projectsList projects; ?include_archived=true to include archived
DB projects.archived_atnull = active, non-null = archived

Story 2.2 — Create Project

As a
BU
I want
to create a new project by binding it to a GitHub repository
So that
I can trigger AMTP pipeline runs against that repository
Scenario: Successful project creation
Giventhe user is authenticated with BU or OP role and the GitHub App is healthy Whenthe user completes the create-project form and submits ThenPOST /projects is issued; the new project card appears in the list; the user is navigated to the new project's detail page
Scenario: Duplicate project name rejected
Givena project named "acme-api" already exists Whenthe user submits the create form with name: "acme-api" Thenthe API returns 409; the form renders a Name already in use inline validation error; no navigation occurs
Mutation addendum — Optimistic create + rollback:

UI clones the current projects store slice into prior_state, applies the optimistic write atomically (new card appears at the top with a loading indicator), then issues POST /projects. On 4xx / 5xx / network-loss, the store slice is restored from prior_state exactly and the CorrelationChip surfaces the failure cause. Regression: zero zombie entries remain in the list or in client state.

Endpoint / DBPurpose
POST /projectsCreate project
DB projects.name (UNIQUE)Duplicate-name guard

Story 2.3 — Edit Project

As a
BU
I want
to update a project's name, description, and pipeline defaults
So that
I can correct configuration without recreating the project and losing run history
Scenario: Successful project edit
Whenthe user modifies name, description, depth_level default, target_framework default, or base_branch and saves ThenPATCH /projects/{project_id} is issued with the changed fields; updated values are reflected immediately in the project detail and list views
Scenario: Edit blocked for Auditor role
Giventhe user has the Auditor role Thenno Edit affordance is rendered in the DOM and PATCH /projects/{project_id} is never issued
Endpoint / DBPurpose
PATCH /projects/{project_id}Update project metadata

Story 2.4 — Archive (Deactivate) Project

As a
BU
I want
to archive a project I no longer need
So that
it is hidden from default views while its complete run history remains intact
Scenario: Archive succeeds — no active children
Giventhe project has zero runs.status IN ('pending','running') rows and zero stages.status = 'awaiting_approval' rows Whenthe user clicks Deactivate / Archive and confirms the modal ThenPATCH /projects/{project_id} is issued with { "archived_at": "<ISO timestamp>" }; the project card is hidden from the default list and reachable via the Show archived filter chip
Scenario: Archive blocked — active run (running-state guard)
Giventhe project has at least one run with runs.status = 'running' Whenthe user clicks Deactivate / Archive Thena blocking modal lists the offending run_id(s) with a deep-link to the run timeline (Epic 4, cancel run); PATCH is not issued
Scenario: Archive blocked — pending approval
Giventhe project has at least one stages.status = 'awaiting_approval' Whenthe user clicks Deactivate / Archive Thena blocking modal lists the offending stage with a deep-link to the approval inbox (Epic 6); PATCH is not issued
Mutation addendum — Optimistic archive + rollback:

After all running-state guards pass, the UI clones prior_state, tags the project card Archived and removes it from the default list, then issues PATCH. On server error, the store slice is restored from prior_state exactly. Regression: project reappears in the active list with zero lingering Archived badge states.

Endpoint / DBPurpose
PATCH /projects/{project_id}Write archived_at value (or clear it)
DB projects.archived_atPlanned column (migration V7)
DB runs.statusGuard check values pending, running
DB stages.statusGuard check value awaiting_approval

Story 2.5 — Restore (Reactivate) Archived Project

As a
BU
I want
to restore an archived project to active status
So that
I can resume triggering runs against it without losing any prior history
Scenario: Successful restore
Giventhe Show archived filter is active and a project with archived_at IS NOT NULL is visible Whenthe user clicks Restore ThenPATCH /projects/{project_id} is issued with { "archived_at": null }; the project reappears in the default active list without the Archived badge
Mutation addendum — Optimistic restore + rollback:

UI clones prior_state, removes the Archived badge and moves the card to the active list, then issues PATCH. On server error, store slice is restored and the card reverts to the archived list with the badge restored (regression assertion).

Story 2.6 — Zero-State Onboarding (First Project)

As a
BU
I want
a guided prompt when my tenant has no projects
So that
I know how to connect my first repository and start a run without reading external docs
Scenario: Empty-tenant CTA
Giventhe authenticated tenant has zero projects Whenthe user navigates to /projects Thena zero-state CTA is rendered with heading Create your first project, sub-copy explaining the AMTP pipeline purpose, a primary button Create project, and a secondary link Install GitHub App (Epic 13)
Scenario: Auditor lands on empty or inaccessible tenant
Giventhe authenticated Auditor has no accessible projects Thenthe zero-state reads No projects are available for your role and the Create project CTA is absent from the DOM

Story 2.7 — Project Detail View

As a
BU
I want
to see a project's configuration and recent runs from a single page
So that
I can navigate quickly to run triggering, history, and artifact inspection
Scenario: Project detail loads for active project
Giventhe user navigates to /projects/{project_id} WhenGET /projects/{project_id} resolves Thenthe project's name, github_repo_full_name, description, configured defaults, and the 5 most recent runs (status + created_at) are rendered
Scenario: Archived project — banner shown and run trigger disabled
Giventhe project has projects.archived_at IS NOT NULL Thenan Archived banner is shown; the Trigger new run button is disabled with tooltip Archived project — restore to trigger runs
Endpoint / DBPurpose
GET /projects/{project_id}Project detail + recent runs
DB projects.archived_atArchived state check
DB runs.status, runs.created_atRecent-runs list