diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md
new file mode 100644
index 00000000..77bde49a
--- /dev/null
+++ b/.planning/REQUIREMENTS.md
@@ -0,0 +1,63 @@
+# Requirements: Per-Project Export Template Assignment
+
+**Defined:** 2026-03-18
+**Core Value:** Teams can plan, execute, and track testing across manual and automated workflows in one place — with AI assistance to reduce repetitive work.
+**Issue:** GitHub #85
+
+## v2.1 Requirements
+
+Requirements for per-project export template assignment. Each maps to roadmap phases.
+
+### Schema
+
+- [x] **SCHEMA-01**: CaseExportTemplateProjectAssignment join model links CaseExportTemplate to Project (already exists)
+- [x] **SCHEMA-02**: Project has a default case export template relation
+
+### Admin UI
+
+- [x] **ADMIN-01**: Admin can assign/unassign export templates to a project in project settings
+- [x] **ADMIN-02**: Admin can set a default export template for a project
+
+### Export Dialog
+
+- [x] **EXPORT-01**: Export dialog only shows templates assigned to the current project
+- [x] **EXPORT-02**: Project default template is pre-selected in the export dialog
+- [x] **EXPORT-03**: If no templates are assigned to a project, all enabled templates are shown (backward compatible)
+
+## Future Requirements
+
+None — this is a self-contained feature.
+
+## Out of Scope
+
+| Feature | Reason |
+|---------------------------------------|-----------------------------------------------------------------|
+| Per-user template preferences | Not in issue #85, could be future enhancement |
+| Template creation from project settings | Templates are managed globally in admin; projects only assign existing ones |
+| Template ordering per project | Unnecessary complexity for v2.1 |
+
+## Traceability
+
+Which phases cover which requirements. Updated during roadmap creation.
+
+| Requirement | Phase | Status |
+|-------------|----------|------------------|
+| SCHEMA-01 | — | Complete (exists) |
+| SCHEMA-02 | Phase 25 | Complete |
+| ADMIN-01 | Phase 26 | Complete |
+| ADMIN-02 | Phase 26 | Complete |
+| EXPORT-01 | Phase 27 | Complete |
+| EXPORT-02 | Phase 27 | Complete |
+| EXPORT-03 | Phase 27 | Complete |
+
+**Coverage:**
+
+- v2.1 requirements: 7 total
+- Already complete: 1 (SCHEMA-01)
+- Remaining: 6
+- Mapped: 6/6
+
+---
+
+*Requirements defined: 2026-03-18*
+*Last updated: 2026-03-18 after roadmap creation (Phases 25-27)*
diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
new file mode 100644
index 00000000..8960be1e
--- /dev/null
+++ b/.planning/ROADMAP.md
@@ -0,0 +1,388 @@
+# Roadmap: TestPlanIt
+
+## Milestones
+
+- ✅ **v1.0 AI Bulk Auto-Tagging** - Phases 1-4 (shipped 2026-03-08)
+- ✅ **v1.1 ZenStack Upgrade Regression Tests** - Phases 5-8 (shipped 2026-03-17)
+- 📋 **v2.0 Comprehensive Test Coverage** - Phases 9-24 (planned)
+- 🚧 **v2.1 Per-Project Export Template Assignment** - Phases 25-27 (in progress)
+
+## Phases
+
+
+✅ v1.0 AI Bulk Auto-Tagging (Phases 1-4) - SHIPPED 2026-03-08
+
+- [x] **Phase 1: Schema Foundation** - Data model supports AI tag suggestions
+- [x] **Phase 2: Alert Service and Pipeline** - Background job pipeline processes tag suggestions
+- [x] **Phase 3: Settings Page UI** - Users can configure AI tagging from settings
+- [x] **Phase 4: (v1.0 complete)** - Milestone wrap-up
+
+
+
+
+✅ v1.1 ZenStack Upgrade Regression Tests (Phases 5-8) - SHIPPED 2026-03-17
+
+- [x] **Phase 5: CRUD Operations** - ZenStack v3 CRUD regression tests
+- [x] **Phase 6: Relations and Queries** - Relation query regression tests
+- [x] **Phase 7: Access Control** - Access control regression tests
+- [x] **Phase 8: Error Handling and Batch Operations** - Error handling and batch regression tests
+
+
+
+### 📋 v2.0 Comprehensive Test Coverage (Phases 9-24)
+
+- [x] **Phase 9: Authentication E2E and API Tests** - All auth flows and API token behavior verified (completed 2026-03-19)
+- [ ] **Phase 10: Test Case Repository E2E Tests** - All repository workflows verified end-to-end
+- [ ] **Phase 11: Repository Components and Hooks** - Repository UI components and hooks tested with edge cases
+- [ ] **Phase 12: Test Execution E2E Tests** - Test run creation and execution workflows verified
+- [ ] **Phase 13: Run Components, Sessions E2E, and Session Components** - Run UI components and session workflows verified
+- [ ] **Phase 14: Project Management E2E and Components** - Project workflows verified with component coverage
+- [ ] **Phase 15: AI Feature E2E and API Tests** - AI features verified end-to-end and via API with mocked LLM
+- [ ] **Phase 16: AI Component Tests** - AI UI components tested with all states and mocked data
+- [ ] **Phase 17: Administration E2E Tests** - All admin management workflows verified end-to-end
+- [ ] **Phase 18: Administration Component Tests** - Admin UI components tested with all states
+- [ ] **Phase 19: Reporting E2E and Component Tests** - Reporting and analytics verified with component coverage
+- [ ] **Phase 20: Search E2E and Component Tests** - Search functionality verified end-to-end and via components
+- [ ] **Phase 21: Integrations E2E, Components, and API Tests** - Integration workflows verified across all layers
+- [ ] **Phase 22: Custom API Route Tests** - All custom API endpoints verified with auth and error handling
+- [ ] **Phase 23: General Components** - Shared UI components tested with edge cases and accessibility
+- [ ] **Phase 24: Hooks, Notifications, and Workers** - Custom hooks, notification flows, and workers unit tested
+
+### 🚧 v2.1 Per-Project Export Template Assignment (Phases 25-27)
+
+**Milestone Goal:** Allow admins to assign specific Case Export Templates to individual projects and set a per-project default, so users only see relevant templates when exporting.
+
+- [x] **Phase 25: Default Template Schema** - Project model extended with optional default export template relation (completed 2026-03-19)
+- [x] **Phase 26: Admin Assignment UI** - Admin can assign, unassign, and set a default export template per project (completed 2026-03-19)
+- [x] **Phase 27: Export Dialog Filtering** - Export dialog shows only project-assigned templates with project default pre-selected (completed 2026-03-19)
+
+## Phase Details
+
+### Phase 9: Authentication E2E and API Tests
+**Goal**: All authentication flows are verified end-to-end and API token behavior is confirmed
+**Depends on**: Phase 8 (v1.1 complete)
+**Requirements**: AUTH-01, AUTH-02, AUTH-03, AUTH-04, AUTH-05, AUTH-06, AUTH-07, AUTH-08
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for sign-in/sign-out with valid credentials and correctly rejects invalid credentials
+ 2. E2E test passes for the complete sign-up flow including email verification
+ 3. E2E test passes for 2FA (setup, code entry, backup code recovery) with mocked authenticator
+ 4. E2E tests pass for magic link, SSO (Google/Microsoft/SAML), and password change with session persistence
+ 5. Component tests pass for all auth pages covering error states, and API tests confirm token auth, creation, revocation, and scope enforcement
+**Plans:** 4/4 plans complete
+
+Plans:
+- [ ] 09-01-PLAN.md -- Sign-in/sign-out and sign-up with email verification E2E tests
+- [ ] 09-02-PLAN.md -- 2FA, SSO, magic link, and password change E2E tests
+- [ ] 09-03-PLAN.md -- Auth page component tests (signin, signup, 2FA setup, 2FA verify)
+- [ ] 09-04-PLAN.md -- API token authentication, creation, revocation, and scope tests
+
+### Phase 10: Test Case Repository E2E Tests
+**Goal**: All test case repository workflows are verified end-to-end
+**Depends on**: Phase 9
+**Requirements**: REPO-01, REPO-02, REPO-03, REPO-04, REPO-05, REPO-06, REPO-07, REPO-08, REPO-09, REPO-10
+**Success Criteria** (what must be TRUE):
+ 1. E2E tests pass for test case CRUD including all custom field types (text, select, date, user, etc.)
+ 2. E2E tests pass for folder operations including create, rename, move, delete, and nested hierarchies
+ 3. E2E tests pass for bulk operations (multi-select, bulk edit, bulk delete, bulk move to folder)
+ 4. E2E tests pass for search/filter (text search, custom field filters, tag filters, state filters) and import/export (CSV, JSON, markdown)
+ 5. E2E tests pass for shared steps, version history, tag management, issue linking, and drag-and-drop reordering
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 11: Repository Components and Hooks
+**Goal**: Test case repository UI components and data hooks are fully tested with edge cases
+**Depends on**: Phase 10
+**Requirements**: REPO-11, REPO-12, REPO-13, REPO-14
+**Success Criteria** (what must be TRUE):
+ 1. Component tests pass for the test case editor covering TipTap rich text, custom fields, steps, and attachment uploads
+ 2. Component tests pass for the repository table covering sorting, pagination, column visibility, and view switching
+ 3. Component tests pass for folder tree, breadcrumbs, and navigation with empty and nested states
+ 4. Hook tests pass for useRepositoryCasesWithFilteredFields, field hooks, and filter hooks with mock data
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 12: Test Execution E2E Tests
+**Goal**: All test run creation and execution workflows are verified end-to-end
+**Depends on**: Phase 10
+**Requirements**: RUN-01, RUN-02, RUN-03, RUN-04, RUN-05, RUN-06
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for the test run creation wizard (name, milestone, configuration group, case selection)
+ 2. E2E test passes for step-by-step case execution including result recording, status updates, and attachments
+ 3. E2E test passes for bulk status updates and case assignment across multiple cases in a run
+ 4. E2E test passes for run completion workflow with status enforcement and multi-configuration test runs
+ 5. E2E test passes for test result import via API (JUnit XML format)
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 13: Run Components, Sessions E2E, and Session Components
+**Goal**: Test run UI components and all exploratory session workflows are verified
+**Depends on**: Phase 12
+**Requirements**: RUN-07, RUN-08, RUN-09, RUN-10, SESS-01, SESS-02, SESS-03, SESS-04, SESS-05, SESS-06
+**Success Criteria** (what must be TRUE):
+ 1. Component tests pass for test run detail view (case list, execution panel, result recording) including TestRunCaseDetails and TestResultHistory
+ 2. Component tests pass for MagicSelectButton/Dialog with mocked LLM responses covering success, loading, and error states
+ 3. E2E tests pass for session creation with template, configuration, and milestone selection
+ 4. E2E tests pass for session execution (add results with status/notes/attachments) and session completion with summary view
+ 5. Component and hook tests pass for SessionResultForm, SessionResultsList, CompleteSessionDialog, and session hooks
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 14: Project Management E2E and Components
+**Goal**: All project management workflows are verified end-to-end with component coverage
+**Depends on**: Phase 9
+**Requirements**: PROJ-01, PROJ-02, PROJ-03, PROJ-04, PROJ-05, PROJ-06, PROJ-07, PROJ-08, PROJ-09
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for the 5-step project creation wizard (name, description, template, members, configurations)
+ 2. E2E tests pass for project settings (general, integrations, AI models, quickscript, share links)
+ 3. E2E tests pass for milestone CRUD (create, edit, nest, complete, cascade delete) and project documentation editor with mocked AI writing assistant
+ 4. E2E tests pass for member management (add, remove, role changes) and project overview dashboard (stats, activity, assignments)
+ 5. Component and hook tests pass for ProjectCard, ProjectMenu, milestone components, and project permission hooks
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 15: AI Feature E2E and API Tests
+**Goal**: All AI-powered features are verified end-to-end and via API with mocked LLM providers
+**Depends on**: Phase 9
+**Requirements**: AI-01, AI-02, AI-03, AI-04, AI-05, AI-08, AI-09
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for AI test case generation wizard (source input, template, configure, review) with mocked LLM
+ 2. E2E test passes for auto-tag flow (configure, analyze, review suggestions, apply) with mocked LLM
+ 3. E2E test passes for magic select in test runs and QuickScript generation with mocked LLM
+ 4. E2E test passes for writing assistant in TipTap editor with mocked LLM
+ 5. API tests pass for all LLM and auto-tag endpoints (generate-test-cases, magic-select, chat, parse-markdown, submit, status, cancel, apply)
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 16: AI Component Tests
+**Goal**: All AI feature UI components are tested with edge cases and mocked data
+**Depends on**: Phase 15
+**Requirements**: AI-06, AI-07
+**Success Criteria** (what must be TRUE):
+ 1. Component tests pass for AutoTagWizardDialog, AutoTagReviewDialog, AutoTagProgress, and TagChip covering all states (loading, empty, error, success)
+ 2. Component tests pass for QuickScript dialog, template selector, and AI preview pane with mocked LLM responses
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 17: Administration E2E Tests
+**Goal**: All admin management workflows are verified end-to-end
+**Depends on**: Phase 9
+**Requirements**: ADM-01, ADM-02, ADM-03, ADM-04, ADM-05, ADM-06, ADM-07, ADM-08, ADM-09, ADM-10, ADM-11
+**Success Criteria** (what must be TRUE):
+ 1. E2E tests pass for user management (list, edit, deactivate, reset 2FA, revoke API keys) and group management (create, edit, assign users, assign to projects)
+ 2. E2E tests pass for role management (create, edit permissions per area) and SSO configuration (add/edit providers, force SSO, email domain restrictions)
+ 3. E2E tests pass for workflow management (create, edit, reorder states) and status management (create, edit flags, scope assignment)
+ 4. E2E tests pass for configuration management (categories, variants, groups) and audit log (view, filter, CSV export)
+ 5. E2E tests pass for Elasticsearch admin (settings, reindex), LLM integration management, and app config management
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 18: Administration Component Tests
+**Goal**: Admin UI components are tested with all states and form interactions
+**Depends on**: Phase 17
+**Requirements**: ADM-12, ADM-13
+**Success Criteria** (what must be TRUE):
+ 1. Component tests pass for QueueManagement, ElasticsearchAdmin, and audit log viewer covering loading, empty, error, and populated states
+ 2. Component tests pass for user edit form, group edit form, and role permissions matrix covering validation and error states
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 19: Reporting E2E and Component Tests
+**Goal**: All reporting and analytics workflows are verified with component coverage
+**Depends on**: Phase 9
+**Requirements**: RPT-01, RPT-02, RPT-03, RPT-04, RPT-05, RPT-06, RPT-07, RPT-08
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for the report builder (create report, select dimensions/metrics, generate chart)
+ 2. E2E tests pass for pre-built reports (automation trends, flaky tests, test case health, issue coverage) and report drill-down/filtering
+ 3. E2E tests pass for share links (create, access public/password-protected/authenticated) and forecasting (milestone forecast, duration estimates)
+ 4. Component tests pass for ReportBuilder, ReportChart, DrillDownDrawer, and ReportFilters with all data states
+ 5. Component tests pass for all chart types (donut, gantt, bubble, sunburst, line, bar) and share link components (ShareDialog, PasswordGate, SharedReportViewer)
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 20: Search E2E and Component Tests
+**Goal**: All search functionality is verified end-to-end with component coverage
+**Depends on**: Phase 9
+**Requirements**: SRCH-01, SRCH-02, SRCH-03, SRCH-04, SRCH-05
+**Success Criteria** (what must be TRUE):
+ 1. E2E test passes for global search (Cmd+K, cross-entity results, result navigation to correct page)
+ 2. E2E tests pass for advanced search operators (exact phrase, required/excluded terms, wildcards, field:value syntax)
+ 3. E2E test passes for faceted search filters (custom field values, tags, states, date ranges)
+ 4. Component tests pass for UnifiedSearch, GlobalSearchSheet, search result components, and FacetedSearchFilters with all data states
+ 5. Component tests pass for result display components (CustomFieldDisplay, DateTimeDisplay, UserDisplay) covering all field types
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 21: Integrations E2E, Components, and API Tests
+**Goal**: All third-party integration workflows are verified end-to-end with component and API coverage
+**Depends on**: Phase 9
+**Requirements**: INTG-01, INTG-02, INTG-03, INTG-04, INTG-05, INTG-06
+**Success Criteria** (what must be TRUE):
+ 1. E2E tests pass for issue tracker setup (Jira, GitHub, Azure DevOps) and issue operations (create, link, sync status) with mocked APIs
+ 2. E2E test passes for code repository setup and QuickScript file context with mocked APIs
+ 3. Component tests pass for UnifiedIssueManager, CreateIssueDialog, SearchIssuesDialog, and integration configuration forms
+ 4. API tests pass for integration endpoints (test-connection, create-issue, search, sync) with mocked external services
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 22: Custom API Route Tests
+**Goal**: All custom API endpoints are verified with correct behavior, auth enforcement, and error handling
+**Depends on**: Phase 9
+**Requirements**: CAPI-01, CAPI-02, CAPI-03, CAPI-04, CAPI-05, CAPI-06, CAPI-07, CAPI-08, CAPI-09, CAPI-10
+**Success Criteria** (what must be TRUE):
+ 1. API tests pass for project endpoints (cases/bulk-edit, cases/fetch-many, folders/stats) with auth and tenant isolation verified
+ 2. API tests pass for test run endpoints (summary, attachments, import, completed, summaries) and session summary endpoint
+ 3. API tests pass for milestone endpoints (descendants, forecast, summary) and share link endpoints (access, password-verify, report data)
+ 4. API tests pass for all report builder endpoints (all report types, drill-down queries) and admin endpoints (elasticsearch, queues, trash, user management)
+ 5. API tests pass for search, tag/issue count aggregation, file upload/download, health, metadata, and OpenAPI documentation endpoints
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 23: General Components
+**Goal**: All shared UI components are tested with full edge case and error state coverage
+**Depends on**: Phase 9
+**Requirements**: COMP-01, COMP-02, COMP-03, COMP-04, COMP-05, COMP-06, COMP-07, COMP-08
+**Success Criteria** (what must be TRUE):
+ 1. Component tests pass for Header, UserDropdownMenu, and NotificationBell covering all notification states (empty, unread count, loading)
+ 2. Component tests pass for comment system (CommentEditor, CommentList, MentionSuggestion) and attachment components (display, upload, preview carousel)
+ 3. Component tests pass for DataTable (sorting, filtering, column visibility, row selection) and form components (ConfigurationSelect, FolderSelect, MilestoneSelect, DatePickerField)
+ 4. Component tests pass for onboarding dialogs, TipTap editor extensions (image resize, tables, code blocks), and DnD components (drag previews, drag interactions)
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+### Phase 24: Hooks, Notifications, and Workers
+**Goal**: All custom hooks, notification flows, and background workers are unit tested
+**Depends on**: Phase 9
+**Requirements**: HOOK-01, HOOK-02, HOOK-03, HOOK-04, HOOK-05, NOTIF-01, NOTIF-02, NOTIF-03, WORK-01, WORK-02, WORK-03
+**Success Criteria** (what must be TRUE):
+ 1. Hook tests pass for ZenStack-generated data fetching hooks (useFindMany*, useCreate*, useUpdate*, useDelete*) with mocked data
+ 2. Hook tests pass for permission hooks (useProjectPermissions, useUserAccess, role-based hooks) covering all permission states
+ 3. Hook tests pass for UI state hooks (useExportData, useReportColumns, filter/sort hooks) and form hooks (useForm integrations, validation)
+ 4. Hook tests pass for integration hooks (useAutoTagJob, useIntegration, useLlm) with mocked providers
+ 5. Component tests pass for NotificationBell, NotificationContent, and NotificationPreferences; API tests pass for notification dispatch; unit tests pass for emailWorker, repoCacheWorker, and autoTagWorker
+**Plans**: 2 plans
+
+Plans:
+- [ ] 10-01-PLAN.md -- Gap-fill: test case edit/delete and bulk move to folder
+- [ ] 10-02-PLAN.md -- Gap-fill: shared steps CRUD and versioning
+
+---
+
+### Phase 25: Default Template Schema
+**Goal**: The Project model exposes an optional default export template so that the application can persist and query per-project default selections
+**Depends on**: Nothing (SCHEMA-01 already complete; this extends it)
+**Requirements**: SCHEMA-02
+**Success Criteria** (what must be TRUE):
+ 1. The Project model has an optional relation to CaseExportTemplate representing the project's default export template
+ 2. Setting and clearing the default template for a project persists correctly in the database
+ 3. ZenStack/Prisma generation succeeds and the new relation is queryable via generated hooks
+**Plans**: 1 plan
+
+Plans:
+- [ ] 25-01-PLAN.md -- Add defaultCaseExportTemplate relation to Project model and regenerate
+
+### Phase 26: Admin Assignment UI
+**Goal**: Admins can assign or unassign export templates to a project and designate one as the default, directly from project settings
+**Depends on**: Phase 25
+**Requirements**: ADMIN-01, ADMIN-02
+**Success Criteria** (what must be TRUE):
+ 1. Admin can navigate to project settings and see a list of all enabled export templates with their assignment status for that project
+ 2. Admin can assign an export template to a project and the assignment is reflected immediately in the UI
+ 3. Admin can unassign an export template from a project and it no longer appears in the project's assigned list
+ 4. Admin can mark one assigned template as the project default, and the selection persists across page reloads
+**Plans**: 2 plans
+
+Plans:
+- [ ] 26-01-PLAN.md -- Update ZenStack access rules for project admin write access
+- [ ] 26-02-PLAN.md -- Build ExportTemplateAssignmentSection and integrate into quickscript page
+
+### Phase 27: Export Dialog Filtering
+**Goal**: The export dialog shows only the templates relevant to the current project, with the project default pre-selected, while gracefully falling back when no assignments exist
+**Depends on**: Phase 26
+**Requirements**: EXPORT-01, EXPORT-02, EXPORT-03
+**Success Criteria** (what must be TRUE):
+ 1. When a project has assigned templates, the export dialog lists only those templates (not all global templates)
+ 2. When a project has a default template set, the export dialog opens with that template pre-selected
+ 3. When a project has no assigned templates, the export dialog shows all enabled templates (backward compatible fallback)
+**Plans**: 1 plan
+
+Plans:
+- [ ] 27-01-PLAN.md -- Filter QuickScript dialog templates by project assignment and pre-select project default
+
+---
+
+## Progress
+
+**Execution Order:**
+Phases execute in numeric order: 9 → 10 → 11 → 12 → 13 → 14 → 15 → 16 → 17 → 18 → 19 → 20 → 21 → 22 → 23 → 24 → 25 → 26 → 27
+
+| Phase | Milestone | Plans Complete | Status | Completed |
+|-------|-----------|----------------|--------|-----------|
+| 1. Schema Foundation | v1.0 | 1/1 | Complete | 2026-03-08 |
+| 2. Alert Service and Pipeline | v1.0 | 3/3 | Complete | 2026-03-08 |
+| 3. Settings Page UI | v1.0 | 1/1 | Complete | 2026-03-08 |
+| 4. (v1.0 complete) | v1.0 | 0/0 | Complete | 2026-03-08 |
+| 5. CRUD Operations | v1.1 | 4/4 | Complete | 2026-03-17 |
+| 6. Relations and Queries | v1.1 | 2/2 | Complete | 2026-03-17 |
+| 7. Access Control | v1.1 | 2/2 | Complete | 2026-03-17 |
+| 8. Error Handling and Batch Operations | v1.1 | 2/2 | Complete | 2026-03-17 |
+| 9. Authentication E2E and API Tests | v2.0 | 4/4 | Complete | 2026-03-19 |
+| 10. Test Case Repository E2E Tests | v2.0 | 0/2 | Planning complete | - |
+| 11. Repository Components and Hooks | v2.0 | 0/TBD | Not started | - |
+| 12. Test Execution E2E Tests | v2.0 | 0/TBD | Not started | - |
+| 13. Run Components, Sessions E2E, and Session Components | v2.0 | 0/TBD | Not started | - |
+| 14. Project Management E2E and Components | v2.0 | 0/TBD | Not started | - |
+| 15. AI Feature E2E and API Tests | v2.0 | 0/TBD | Not started | - |
+| 16. AI Component Tests | v2.0 | 0/TBD | Not started | - |
+| 17. Administration E2E Tests | v2.0 | 0/TBD | Not started | - |
+| 18. Administration Component Tests | v2.0 | 0/TBD | Not started | - |
+| 19. Reporting E2E and Component Tests | v2.0 | 0/TBD | Not started | - |
+| 20. Search E2E and Component Tests | v2.0 | 0/TBD | Not started | - |
+| 21. Integrations E2E, Components, and API Tests | v2.0 | 0/TBD | Not started | - |
+| 22. Custom API Route Tests | v2.0 | 0/TBD | Not started | - |
+| 23. General Components | v2.0 | 0/TBD | Not started | - |
+| 24. Hooks, Notifications, and Workers | v2.0 | 0/TBD | Not started | - |
+| 25. Default Template Schema | 1/1 | Complete | 2026-03-19 | - |
+| 26. Admin Assignment UI | 2/2 | Complete | 2026-03-19 | - |
+| 27. Export Dialog Filtering | 1/1 | Complete | 2026-03-19 | - |
diff --git a/.planning/STATE.md b/.planning/STATE.md
new file mode 100644
index 00000000..d22354c9
--- /dev/null
+++ b/.planning/STATE.md
@@ -0,0 +1,82 @@
+---
+gsd_state_version: 1.0
+milestone: v2.1
+milestone_name: Per-Project Export Template Assignment
+status: planning
+stopped_at: Completed 27-export-dialog-filtering/27-01-PLAN.md
+last_updated: "2026-03-19T05:37:52.328Z"
+last_activity: 2026-03-18 — Roadmap created for v2.1 (Phases 25-27)
+progress:
+ total_phases: 19
+ completed_phases: 3
+ total_plans: 4
+ completed_plans: 4
+ percent: 0
+---
+
+# State
+
+## Project Reference
+
+See: .planning/PROJECT.md (updated 2026-03-18)
+
+**Core value:** Teams can plan, execute, and track testing across manual and automated workflows in one place — with AI assistance to reduce repetitive work.
+**Current focus:** v2.1 Per-Project Export Template Assignment — Phase 25: Default Template Schema
+
+## Current Position
+
+Phase: 25 of 27 (Default Template Schema)
+Plan: — of TBD in current phase
+Status: Ready to plan
+Last activity: 2026-03-18 — Roadmap created for v2.1 (Phases 25-27)
+
+Progress: [░░░░░░░░░░] 0% (v2.1 phases)
+
+## Performance Metrics
+
+**Velocity:**
+- Total plans completed (v2.1): 0
+- Average duration: —
+- Total execution time: —
+
+**By Phase:**
+
+| Phase | Plans | Total | Avg/Plan |
+|-------|-------|-------|----------|
+| - | - | - | - |
+
+## Accumulated Context
+| Phase 25-default-template-schema P01 | 5min | 2 tasks | 5 files |
+| Phase 26-admin-assignment-ui P01 | 5 | 1 tasks | 1 files |
+| Phase 26 P02 | 15min | 2 tasks | 3 files |
+| Phase 26-admin-assignment-ui P02 | 45min | 3 tasks | 4 files |
+| Phase 27-export-dialog-filtering P01 | 15min | 2 tasks | 2 files |
+
+### Decisions
+
+- Follow TemplateProjectAssignment pattern (existing pattern for case field template assignments)
+- Backward compatible fallback: no assignments = show all enabled templates
+- SCHEMA-01 already complete (CaseExportTemplateProjectAssignment join model exists in schema.zmodel)
+- ZenStack hooks for CaseExportTemplateProjectAssignment are already generated
+- [Phase 25-default-template-schema]: Used onDelete: SetNull on defaultCaseExportTemplateId FK so deleting a CaseExportTemplate clears the default on referencing projects
+- [Phase 25-default-template-schema]: Named relation 'ProjectDefaultExportTemplate' disambiguates from CaseExportTemplateProjectAssignment join-table relation
+- [Phase 26-admin-assignment-ui]: Mirrored Projects model access pattern for project-admin-scoped create/delete on CaseExportTemplateProjectAssignment
+- [Phase 26-admin-assignment-ui]: Added translation keys in Task 1 commit because TypeScript validates next-intl keys against en-US.json at compile time
+- [Phase 26-admin-assignment-ui]: MultiAsyncCombobox chosen over checkbox list for better UX with large template lists
+- [Phase 26-admin-assignment-ui]: selectedTemplates stored as TemplateOption[] objects so badge data available without re-lookup
+- [Phase 27-export-dialog-filtering]: Used templateId (not caseExportTemplateId) — join model field name per schema.zmodel
+- [Phase 27-export-dialog-filtering]: filteredTemplates pattern: fetch global templates + assignment filter in useMemo for project-scoped template display
+
+### Pending Todos
+
+None yet.
+
+### Blockers/Concerns
+
+None yet.
+
+## Session Continuity
+
+Last session: 2026-03-19T05:35:21.836Z
+Stopped at: Completed 27-export-dialog-filtering/27-01-PLAN.md
+Resume file: None
diff --git a/.planning/phases/25-default-template-schema/25-01-PLAN.md b/.planning/phases/25-default-template-schema/25-01-PLAN.md
new file mode 100644
index 00000000..a1f68bd9
--- /dev/null
+++ b/.planning/phases/25-default-template-schema/25-01-PLAN.md
@@ -0,0 +1,169 @@
+---
+phase: 25-default-template-schema
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - testplanit/schema.zmodel
+autonomous: true
+requirements:
+ - SCHEMA-02
+
+must_haves:
+ truths:
+ - "Project model has an optional defaultCaseExportTemplate relation to CaseExportTemplate"
+ - "Setting defaultCaseExportTemplateId on a project persists a valid FK to CaseExportTemplate"
+ - "Clearing defaultCaseExportTemplateId (setting to null) persists correctly"
+ - "ZenStack and Prisma generation succeeds without errors"
+ - "Deleting a CaseExportTemplate that is a project default sets the FK to null (SetNull)"
+ artifacts:
+ - path: "testplanit/schema.zmodel"
+ provides: "Project model with defaultCaseExportTemplate relation"
+ contains: "defaultCaseExportTemplateId"
+ key_links:
+ - from: "Projects.defaultCaseExportTemplateId"
+ to: "CaseExportTemplate.id"
+ via: "@relation FK"
+ pattern: "defaultCaseExportTemplate\\s+CaseExportTemplate"
+---
+
+
+Add a nullable `defaultCaseExportTemplateId` FK and relation to the Project model in schema.zmodel, then regenerate ZenStack/Prisma artifacts and push the schema to the database.
+
+Purpose: Enables per-project default export template selection (SCHEMA-02). Phase 26 will build the admin UI to set this value; Phase 27 will use it to pre-select a template in the export dialog.
+
+Output: Updated schema.zmodel with the new relation, regenerated Prisma client and ZenStack hooks, database schema updated.
+
+
+
+@/Users/bderman/.claude/get-shit-done/workflows/execute-plan.md
+@/Users/bderman/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/25-default-template-schema/25-CONTEXT.md
+
+
+
+
+From testplanit/schema.zmodel (Project model, lines 334-467):
+```zmodel
+model Projects {
+ // ... existing fields ...
+ defaultRoleId Int?
+ defaultRole Roles? @relation("ProjectDefaultRole", fields: [defaultRoleId], references: [id])
+ // ... existing fields ...
+ assignedExportTemplates CaseExportTemplateProjectAssignment[]
+ quickScriptEnabled Boolean @default(false)
+ // access control rules follow
+}
+```
+
+From testplanit/schema.zmodel (CaseExportTemplate model, lines 785-806):
+```zmodel
+model CaseExportTemplate {
+ id Int @id @default(autoincrement())
+ name String @unique @length(1)
+ // ... other fields ...
+ projects CaseExportTemplateProjectAssignment[]
+
+ @@deny('all', !auth())
+ @@allow('all', auth().access == 'ADMIN')
+ @@allow('read', auth().access != null)
+}
+```
+
+
+
+
+
+
+ Task 1: Add defaultCaseExportTemplate relation to Project model
+ testplanit/schema.zmodel
+
+ - testplanit/schema.zmodel (lines 334-467 for Project model, lines 785-806 for CaseExportTemplate model)
+
+
+In `testplanit/schema.zmodel`, add two new fields to the `Projects` model, placed after the `assignedExportTemplates` line (line 377) and before `quickScriptEnabled`:
+
+```zmodel
+ defaultCaseExportTemplateId Int?
+ defaultCaseExportTemplate CaseExportTemplate? @relation("ProjectDefaultExportTemplate", fields: [defaultCaseExportTemplateId], references: [id], onDelete: SetNull)
+```
+
+Then add a back-relation on the `CaseExportTemplate` model. After the existing `projects` line (line 801), add:
+
+```zmodel
+ defaultForProjects Projects[] @relation("ProjectDefaultExportTemplate")
+```
+
+Key details:
+- Use `onDelete: SetNull` so deleting a CaseExportTemplate clears the default on any project referencing it (per CONTEXT.md discretion decision)
+- Use a named relation `"ProjectDefaultExportTemplate"` to disambiguate from the existing join-table relation via `CaseExportTemplateProjectAssignment`
+- The FK is nullable (`Int?`) and the relation is optional (`CaseExportTemplate?`) since projects may not have a default
+- No access control changes needed on Project model (existing rules cover all fields)
+
+
+ cd testplanit && grep -n "defaultCaseExportTemplateId" schema.zmodel && grep -n "defaultCaseExportTemplate " schema.zmodel && grep -n "defaultForProjects" schema.zmodel && grep -n "ProjectDefaultExportTemplate" schema.zmodel
+
+
+ - schema.zmodel contains `defaultCaseExportTemplateId Int?` inside the Projects model
+ - schema.zmodel contains `defaultCaseExportTemplate CaseExportTemplate? @relation("ProjectDefaultExportTemplate", fields: [defaultCaseExportTemplateId], references: [id], onDelete: SetNull)` inside the Projects model
+ - schema.zmodel contains `defaultForProjects Projects[] @relation("ProjectDefaultExportTemplate")` inside the CaseExportTemplate model
+ - The new fields appear after `assignedExportTemplates` and before `quickScriptEnabled` in the Projects model
+
+ Project model has nullable FK and optional relation to CaseExportTemplate for per-project default; CaseExportTemplate has back-relation
+
+
+
+ Task 2: Regenerate ZenStack/Prisma and push schema
+ testplanit/schema.zmodel
+
+ - testplanit/schema.zmodel (verify Task 1 changes are present)
+ - testplanit/package.json (confirm `generate` script exists)
+
+
+Run `pnpm generate` from the `testplanit/` directory. This command:
+1. Runs `zenstack generate` which regenerates all ZenStack hooks in `lib/hooks/`
+2. Runs `prisma db push` which applies the schema change to the database (adds nullable `defaultCaseExportTemplateId` column to Projects table)
+
+The nullable FK addition is safe for existing data -- all existing rows get NULL automatically.
+
+After generation succeeds, verify the generated Prisma schema includes the new field by checking `testplanit/prisma/schema.prisma` for `defaultCaseExportTemplateId`.
+
+
+ cd testplanit && pnpm generate 2>&1 | tail -20 && grep "defaultCaseExportTemplateId" prisma/schema.prisma
+
+
+ - `pnpm generate` exits with code 0
+ - `testplanit/prisma/schema.prisma` contains `defaultCaseExportTemplateId`
+ - `testplanit/prisma/schema.prisma` contains `defaultCaseExportTemplate`
+ - No TypeScript compilation errors related to the new fields
+
+ ZenStack hooks regenerated, Prisma client updated, database schema pushed with new nullable FK column on Projects table
+
+
+
+
+
+1. `grep "defaultCaseExportTemplateId" testplanit/schema.zmodel` returns the FK field
+2. `grep "defaultCaseExportTemplate " testplanit/schema.zmodel` returns the relation field
+3. `grep "defaultForProjects" testplanit/schema.zmodel` returns the back-relation on CaseExportTemplate
+4. `grep "defaultCaseExportTemplateId" testplanit/prisma/schema.prisma` confirms Prisma schema was generated
+5. `cd testplanit && pnpm generate` completes without errors
+
+
+
+- Project model in schema.zmodel has `defaultCaseExportTemplateId Int?` and `defaultCaseExportTemplate CaseExportTemplate?` relation
+- CaseExportTemplate model has `defaultForProjects Projects[]` back-relation
+- `pnpm generate` succeeds (ZenStack + Prisma generation + db push)
+- Generated Prisma schema and hooks include the new relation
+
+
+
diff --git a/.planning/phases/25-default-template-schema/25-01-SUMMARY.md b/.planning/phases/25-default-template-schema/25-01-SUMMARY.md
new file mode 100644
index 00000000..b420d502
--- /dev/null
+++ b/.planning/phases/25-default-template-schema/25-01-SUMMARY.md
@@ -0,0 +1,105 @@
+---
+phase: 25-default-template-schema
+plan: 01
+subsystem: database
+tags: [prisma, zenstack, postgresql, schema, relations]
+
+# Dependency graph
+requires: []
+provides:
+ - "Projects model with nullable defaultCaseExportTemplateId FK and defaultCaseExportTemplate optional relation"
+ - "CaseExportTemplate model with defaultForProjects back-relation"
+ - "Database column Projects.defaultCaseExportTemplateId (nullable, SetNull on delete)"
+affects:
+ - 26-default-template-ui
+ - 27-export-dialog
+
+# Tech tracking
+tech-stack:
+ added: []
+ patterns:
+ - "Named relation 'ProjectDefaultExportTemplate' disambiguates from CaseExportTemplateProjectAssignment join-table relation"
+ - "onDelete: SetNull on nullable FK clears default when template is deleted"
+
+key-files:
+ created: []
+ modified:
+ - testplanit/schema.zmodel
+ - testplanit/prisma/schema.prisma
+ - testplanit/lib/hooks/__model_meta.ts
+ - testplanit/lib/hooks/projects.ts
+ - testplanit/lib/openapi/zenstack-openapi.json
+
+key-decisions:
+ - "Used onDelete: SetNull so deleting a CaseExportTemplate clears defaultCaseExportTemplateId on any referencing project (no orphan FKs)"
+ - "Named relation 'ProjectDefaultExportTemplate' required to disambiguate from the existing join-table relation via CaseExportTemplateProjectAssignment"
+
+patterns-established:
+ - "Pattern: nullable optional relation with SetNull for per-project default selections (mirrors defaultRoleId/defaultRole pattern)"
+
+requirements-completed: [SCHEMA-02]
+
+# Metrics
+duration: 5min
+completed: 2026-03-19
+---
+
+# Phase 25 Plan 01: Default Template Schema Summary
+
+**Nullable `defaultCaseExportTemplateId` FK added to Projects model with `onDelete: SetNull`, enabling per-project export template defaults; ZenStack/Prisma regenerated and database schema pushed.**
+
+## Performance
+
+- **Duration:** ~5 min
+- **Started:** 2026-03-19T03:05:00Z
+- **Completed:** 2026-03-19T03:10:00Z
+- **Tasks:** 2
+- **Files modified:** 5
+
+## Accomplishments
+
+- Added `defaultCaseExportTemplateId Int?` FK and `defaultCaseExportTemplate CaseExportTemplate?` optional relation to Projects model in schema.zmodel
+- Added `defaultForProjects Projects[]` back-relation on CaseExportTemplate model with named relation `"ProjectDefaultExportTemplate"`
+- Ran `pnpm generate` — ZenStack hooks, Prisma client, and OpenAPI spec regenerated; nullable column pushed to database with all existing rows defaulting to NULL
+
+## Task Commits
+
+Each task was committed atomically:
+
+1. **Task 1: Add defaultCaseExportTemplate relation to Project model** - `a739d82d` (feat)
+2. **Task 2: Regenerate ZenStack/Prisma and push schema** - `0cabe15d` (chore)
+
+## Files Created/Modified
+
+- `testplanit/schema.zmodel` - Added FK and relation fields to Projects model; added back-relation to CaseExportTemplate model
+- `testplanit/prisma/schema.prisma` - Generated Prisma schema with new fields
+- `testplanit/lib/hooks/__model_meta.ts` - Updated ZenStack model metadata
+- `testplanit/lib/hooks/projects.ts` - Updated ZenStack hooks for Projects model
+- `testplanit/lib/openapi/zenstack-openapi.json` - Updated OpenAPI spec
+
+## Decisions Made
+
+- Used `onDelete: SetNull` so deleting a CaseExportTemplate automatically clears the FK on any project that used it as default — no orphaned references
+- Named the relation `"ProjectDefaultExportTemplate"` to disambiguate from the existing `CaseExportTemplateProjectAssignment` join-table relation that also links Projects and CaseExportTemplate
+
+## Deviations from Plan
+
+None - plan executed exactly as written.
+
+## Issues Encountered
+
+None - `pnpm generate` and `prisma db push` completed cleanly on first attempt.
+
+## User Setup Required
+
+None - no external service configuration required. The nullable column was added to the existing Projects table without any data migration needed.
+
+## Next Phase Readiness
+
+- Schema change is ready for Phase 26 (admin UI) to set `defaultCaseExportTemplateId` on projects
+- Phase 27 (export dialog) can use `project.defaultCaseExportTemplate` to pre-select a template
+- ZenStack auto-generated hooks (`useFindManyProjects`, `useUpdateProjects`) already include the new field
+
+---
+*Phase: 25-default-template-schema*
+*Completed: 2026-03-19*
diff --git a/.planning/phases/26-admin-assignment-ui/26-01-SUMMARY.md b/.planning/phases/26-admin-assignment-ui/26-01-SUMMARY.md
new file mode 100644
index 00000000..bfd36a64
--- /dev/null
+++ b/.planning/phases/26-admin-assignment-ui/26-01-SUMMARY.md
@@ -0,0 +1,88 @@
+---
+phase: 26-admin-assignment-ui
+plan: 01
+subsystem: database
+tags: [zenstack, prisma, access-control, zmodel, project-admin]
+
+# Dependency graph
+requires:
+ - phase: 25-default-template-schema
+ provides: CaseExportTemplateProjectAssignment join model in schema.zmodel
+provides:
+ - Project admins can create/delete CaseExportTemplateProjectAssignment records for their own projects
+affects: [26-admin-assignment-ui]
+
+# Tech tracking
+tech-stack:
+ added: []
+ patterns: [ZenStack access rules follow Projects model pattern for project-scoped operations]
+
+key-files:
+ created: []
+ modified:
+ - testplanit/schema.zmodel
+
+key-decisions:
+ - "Mirrored Projects model access pattern for project admin scoped create/delete rules on CaseExportTemplateProjectAssignment"
+ - "Two rules: one for explicit SPECIFIC_ROLE Project Admin, one for PROJECTADMIN access type assigned to project"
+
+patterns-established:
+ - "Project-scoped access pattern: project.userPermissions?[user == auth() && accessType == 'SPECIFIC_ROLE' && role.name == 'Project Admin']"
+
+requirements-completed: [ADMIN-01]
+
+# Metrics
+duration: 5min
+completed: 2026-03-19
+---
+
+# Phase 26 Plan 01: Admin Assignment UI - Access Rules Summary
+
+**ZenStack access rules updated on CaseExportTemplateProjectAssignment to allow project admins to create/delete template assignments for their own projects**
+
+## Performance
+
+- **Duration:** 5 min
+- **Started:** 2026-03-19T03:42:00Z
+- **Completed:** 2026-03-19T03:47:10Z
+- **Tasks:** 1
+- **Files modified:** 1
+
+## Accomplishments
+- Added `@@allow('create,delete')` rule for users with explicit SPECIFIC_ROLE Project Admin on project
+- Added `@@allow('create,delete')` rule for users with PROJECTADMIN access type assigned to project
+- Ran `pnpm generate` to regenerate ZenStack/Prisma artifacts successfully
+
+## Task Commits
+
+Each task was committed atomically:
+
+1. **Task 1: Add project admin access rules to CaseExportTemplateProjectAssignment** - `30b47758` (feat)
+
+**Plan metadata:** (docs commit follows)
+
+## Files Created/Modified
+- `testplanit/schema.zmodel` - Added two project-admin-scoped create/delete access rules to CaseExportTemplateProjectAssignment model
+
+## Decisions Made
+- Mirrored the Projects model access pattern (lines 395-408) to maintain consistency across project-scoped resources
+- Two distinct rules: one for explicit role-based access (SPECIFIC_ROLE), one for PROJECTADMIN access type
+
+## Deviations from Plan
+
+None - plan executed exactly as written.
+
+## Issues Encountered
+None
+
+## User Setup Required
+None - no external service configuration required.
+
+## Next Phase Readiness
+- Access rules updated: project admins can now create/delete CaseExportTemplateProjectAssignment records for their own projects
+- ZenStack generation succeeded, hooks are updated
+- Ready to build the UI for managing template assignments (remaining tasks in phase 26)
+
+---
+*Phase: 26-admin-assignment-ui*
+*Completed: 2026-03-19*
diff --git a/.planning/phases/26-admin-assignment-ui/26-02-SUMMARY.md b/.planning/phases/26-admin-assignment-ui/26-02-SUMMARY.md
new file mode 100644
index 00000000..153384c8
--- /dev/null
+++ b/.planning/phases/26-admin-assignment-ui/26-02-SUMMARY.md
@@ -0,0 +1,123 @@
+---
+phase: 26-admin-assignment-ui
+plan: 02
+subsystem: ui
+tags: [react, zenstack, next-intl, shadcn, tanstack-query]
+
+# Dependency graph
+requires:
+ - phase: 26-admin-assignment-ui-01
+ provides: ZenStack access control for CaseExportTemplateProjectAssignment create/delete scoped to project admins
+ - phase: 25-default-template-schema
+ provides: defaultCaseExportTemplateId field on Projects model and CaseExportTemplateProjectAssignment join model
+provides:
+ - ExportTemplateAssignmentSection component for per-project template assignment UI
+ - Export template assignment section rendered on quickscript settings page above code repo section
+ - Translation keys for export template assignment under projects.settings.quickScript.exportTemplates
+affects: [27-export-dialog, testing, e2e]
+
+# Tech tracking
+tech-stack:
+ added: []
+ patterns:
+ - "Delete-all/recreate pattern for many-to-many assignment updates (mirrors EditTemplate.tsx)"
+ - "MultiAsyncCombobox for searchable multi-select with async fetch and metadata badges pushed right"
+ - "Alphabetical sort applied client-side in useMemo to avoid ZenStack nested orderBy bugs"
+
+key-files:
+ created:
+ - testplanit/app/[locale]/projects/settings/[projectId]/quickscript/ExportTemplateAssignmentSection.tsx
+ modified:
+ - testplanit/app/[locale]/projects/settings/[projectId]/quickscript/page.tsx
+ - testplanit/messages/en-US.json
+ - testplanit/components/ui/multi-async-combobox.tsx
+
+key-decisions:
+ - "Added translation keys to en-US.json as part of Task 1 because TypeScript type-checks translation keys against the JSON at compile time — component would not compile without them"
+ - "MultiAsyncCombobox chosen over checkbox list after human review — better UX for potentially large template lists"
+ - "selectedTemplates stored as TemplateOption[] objects (not just IDs) so badge data is available without re-lookup"
+ - "Metadata badges (category, language) pushed right with ml-auto so template names stay left-aligned"
+
+patterns-established:
+ - "ExportTemplateAssignmentSection: MultiAsyncCombobox with full object state, useEffect initializer from server data, isDirty flag to enable Save only after local changes"
+
+requirements-completed: [ADMIN-01, ADMIN-02]
+
+# Metrics
+duration: 15min
+completed: 2026-03-19
+---
+
+# Phase 26 Plan 02: Export Template Assignment UI Summary
+
+**ExportTemplateAssignmentSection with MultiAsyncCombobox on QuickScript settings page — admins assign templates, set per-project default, and save via delete-all/recreate ZenStack hooks**
+
+## Performance
+
+- **Duration:** ~45 min (including human review and UI refinement)
+- **Started:** 2026-03-19T03:47:58Z
+- **Completed:** 2026-03-19
+- **Tasks:** 3 of 3 (all complete including checkpoint:human-verify)
+- **Files modified:** 4
+
+## Accomplishments
+- Created ExportTemplateAssignmentSection component with MultiAsyncCombobox for searchable template assignment
+- Admin can assign/unassign templates, set a default from assigned templates, and save with explicit Save button
+- Save uses delete-all/recreate pattern: deleteMany assignments then createMany, plus updateProject for default
+- Unassigning the default template automatically clears it (defaultStillAssigned check before updateProject)
+- Component integrated into page.tsx above the code repository section with defaultCaseExportTemplateId passed as prop
+- Human review approved, UI refined from checkbox list to MultiAsyncCombobox with metadata badges
+
+## Task Commits
+
+Each task was committed atomically:
+
+1. **Task 1: Create ExportTemplateAssignmentSection component** - `14929833` (feat)
+2. **Task 2: Integrate section into page and add translations** - `b6b55c53` (feat)
+3. **Task 3: Verify template assignment UI + UI refinements** - `87e79d2b` (refactor)
+
+**Plan metadata:** `ba1d6edd` (docs: complete export template assignment UI plan)
+
+## Files Created/Modified
+- `testplanit/app/[locale]/projects/settings/[projectId]/quickscript/ExportTemplateAssignmentSection.tsx` - Export template assignment UI with MultiAsyncCombobox, alphabetical sort, metadata badges, default selector, and save handler
+- `testplanit/app/[locale]/projects/settings/[projectId]/quickscript/page.tsx` - Added import, defaultCaseExportTemplateId to query, ExportTemplateAssignmentSection render
+- `testplanit/messages/en-US.json` - Added exportTemplates keys including assignedLabel and selectPlaceholder
+- `testplanit/components/ui/multi-async-combobox.tsx` - Fixed X button alignment and check icon consistency in option rows
+
+## Decisions Made
+- Added translation keys as part of Task 1 commit (not Task 2) because TypeScript validates translation key names against en-US.json at compile time
+- MultiAsyncCombobox chosen over checkbox list after human review — better UX for potentially large template lists with search
+- `selectedTemplates` stored as full `TemplateOption[]` objects so badge data (category, language) is available without re-lookup
+- Metadata badges pushed right with `ml-auto` so template names stay left-aligned in option rows
+
+## Deviations from Plan
+
+### Post-Checkpoint UI Refinements
+
+**1. [Human Review] Replaced checkbox list with MultiAsyncCombobox**
+- **Found during:** Task 3 (human-verify checkpoint)
+- **Issue:** Checkbox list was functional but user wanted a more polished, searchable multi-select
+- **Fix:** Replaced checkbox list with MultiAsyncCombobox — supports search, badge rendering per option, alphabetical sort
+- **Files modified:** ExportTemplateAssignmentSection.tsx, multi-async-combobox.tsx, en-US.json (2 new keys)
+- **Committed in:** `87e79d2b`
+
+---
+
+**Total deviations:** 1 post-checkpoint UI refinement
+**Impact on plan:** Improved UX, all success criteria met. No functional scope change.
+
+## Issues Encountered
+- TypeScript compile-time validation of i18n keys via next-intl means translation keys must exist before the component will type-check. Added translations first, then verified compilation. No functional impact.
+
+## User Setup Required
+None - no external service configuration required.
+
+## Next Phase Readiness
+- ExportTemplateAssignmentSection is fully implemented, integrated, and human-verified
+- Phase 27 (Export Dialog Filtering) can query `useFindManyCaseExportTemplateProjectAssignment` for project-scoped template lists
+- `defaultCaseExportTemplateId` on Project is queryable for pre-selecting the default in the export dialog
+- Backward compatible fallback (show all templates when no assignments exist) documented for Phase 27 to implement
+
+---
+*Phase: 26-admin-assignment-ui*
+*Completed: 2026-03-19*
diff --git a/.planning/phases/27-export-dialog-filtering/27-01-SUMMARY.md b/.planning/phases/27-export-dialog-filtering/27-01-SUMMARY.md
new file mode 100644
index 00000000..922334c5
--- /dev/null
+++ b/.planning/phases/27-export-dialog-filtering/27-01-SUMMARY.md
@@ -0,0 +1,118 @@
+---
+phase: 27-export-dialog-filtering
+plan: 01
+subsystem: ui
+tags: [react, zenstack, tanstack-query, next-intl, export-templates]
+
+# Dependency graph
+requires:
+ - phase: 25-default-template-schema
+ provides: defaultCaseExportTemplateId field on Projects and CaseExportTemplateProjectAssignment join model
+ - phase: 26-admin-assignment-ui
+ provides: Admin UI for assigning templates to projects; CaseExportTemplateProjectAssignment records
+provides:
+ - QuickScript export dialog filtered to project-assigned templates only
+ - Project default template pre-selection in QuickScript dialog
+ - Backward-compatible fallback (no assignments = all enabled templates)
+ - Empty state message when assignments exist but all are disabled/deleted
+affects: [28-future-export-features]
+
+# Tech tracking
+tech-stack:
+ added: []
+ patterns:
+ - "useFindManyCaseExportTemplateProjectAssignment with projectId filter for per-project template scoping"
+ - "useFindUniqueProjects with select: { defaultCaseExportTemplateId } for lightweight project field fetch"
+ - "filteredTemplates useMemo pattern: derive visible list from global fetch + assignment filter"
+
+key-files:
+ created: []
+ modified:
+ - testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx
+ - testplanit/messages/en-US.json
+
+key-decisions:
+ - "Used templateId (not caseExportTemplateId) — the join model field is named templateId per schema.zmodel"
+ - "Removed include: { caseExportTemplate: true } from assignment query — not needed since templates are already fetched globally via useFindManyCaseExportTemplate"
+ - "assignmentsExistButEmpty renders inline empty state message instead of disabling the combobox trigger — better UX clarity"
+ - "Export button also disabled when assignmentsExistButEmpty to prevent submitting without a template"
+
+patterns-established:
+ - "Project-scoped template filtering: fetch global templates + fetch project assignments separately, then filter in useMemo"
+ - "Pre-selection priority chain: explicit user selection > project default > global isDefault > first available"
+
+requirements-completed: [EXPORT-01, EXPORT-02, EXPORT-03]
+
+# Metrics
+duration: 15min
+completed: 2026-03-19
+---
+
+# Phase 27 Plan 01: Export Dialog Filtering Summary
+
+**QuickScript dialog now filters templates to project assignments with project-default pre-selection and backward-compatible fallback when no assignments exist**
+
+## Performance
+
+- **Duration:** 15 min
+- **Started:** 2026-03-19T05:24:02Z
+- **Completed:** 2026-03-19T05:39:00Z
+- **Tasks:** 2
+- **Files modified:** 2
+
+## Accomplishments
+- QuickScript dialog shows only project-assigned templates when assignments exist (EXPORT-01)
+- Project default template is pre-selected with priority over global isDefault (EXPORT-02)
+- No assignments configured = all enabled templates shown (backward compatible, EXPORT-03)
+- Empty state message rendered with export button disabled when assignments exist but all are unavailable
+- Category grouping behavior preserved using filteredTemplates
+
+## Task Commits
+
+Each task was committed atomically:
+
+1. **Task 1: Add empty state translation key** - `c96dcd0b` (feat)
+2. **Task 2: Filter templates by project assignment and pre-select project default** - `398da5c8` (feat)
+
+**Plan metadata:** (docs commit — see below)
+
+## Files Created/Modified
+- `testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx` - Added assignment + project queries, filteredTemplates memo, updated defaultTemplate logic, empty state rendering
+- `testplanit/messages/en-US.json` - Added `noAvailableTemplates` key under `repository.quickScript`
+
+## Decisions Made
+- Used `templateId` field (not `caseExportTemplateId`) on the join model — matches the actual schema.zmodel field name
+- Removed `include: { caseExportTemplate: true }` from the assignments query since the global `useFindManyCaseExportTemplate` fetch already provides the template data; filtering by Set of templateIds avoids redundant data fetch
+- Empty state message is rendered inline (replacing the combobox) rather than just showing an empty combobox, giving the user a clear actionable message
+
+## Deviations from Plan
+
+### Auto-fixed Issues
+
+**1. [Rule 1 - Bug] Fixed field name mismatch: caseExportTemplateId vs templateId**
+- **Found during:** Task 2 (filter templates by project assignment)
+- **Issue:** Plan specified `a.caseExportTemplateId` and `include: { caseExportTemplate: true }` but the schema.zmodel join model uses `templateId` and `template` as field names
+- **Fix:** Changed `caseExportTemplateId` to `templateId` in the Set mapping; removed the `include` clause (not needed and caused a TypeScript error)
+- **Files modified:** QuickScriptModal.tsx
+- **Verification:** `pnpm type-check` passes without errors
+- **Committed in:** `398da5c8` (Task 2 commit)
+
+---
+
+**Total deviations:** 1 auto-fixed (Rule 1 - field name mismatch from plan vs actual schema)
+**Impact on plan:** Essential fix for correctness. No scope creep.
+
+## Issues Encountered
+- TypeScript caught the field name mismatch immediately on first type-check run, making the fix straightforward
+
+## User Setup Required
+None - no external service configuration required.
+
+## Next Phase Readiness
+- v2.1 Per-Project Export Template Assignment feature is complete across all three phases (25, 26, 27)
+- All three requirements (EXPORT-01, EXPORT-02, EXPORT-03) satisfied
+- No blockers
+
+---
+*Phase: 27-export-dialog-filtering*
+*Completed: 2026-03-19*
diff --git a/docs/docs/user-guide/llm-quickscript.md b/docs/docs/user-guide/llm-quickscript.md
index 5d661063..b85d62eb 100644
--- a/docs/docs/user-guide/llm-quickscript.md
+++ b/docs/docs/user-guide/llm-quickscript.md
@@ -33,6 +33,7 @@ Navigate to **Administration > Code Repositories** and add a connection to your
In your project, go to **Settings > QuickScript** to configure:
+- **Export Templates** — Assign which QuickScript templates are available for this project and set a project-specific default. See [Per-Project Template Assignment](./quickscript-templates.md#per-project-template-assignment) for details.
- **Repository** — Select which code repository to connect
- **Path Patterns** — Define glob patterns to select the files the AI should reference. Add patterns that point to your test infrastructure:
- `tests/e2e` with `**/*.ts` — Include all TypeScript files in your E2E test directory
diff --git a/docs/docs/user-guide/projects/quickscript.md b/docs/docs/user-guide/projects/quickscript.md
index 10271420..9d602d5c 100644
--- a/docs/docs/user-guide/projects/quickscript.md
+++ b/docs/docs/user-guide/projects/quickscript.md
@@ -36,7 +36,18 @@ The dialog has two settings:
Choose a template from the dropdown. Templates are grouped by category (e.g., Browser E2E, Unit Testing, API Testing). You can search by name, category, or framework.
-If an administrator has set a default template, it will be pre-selected automatically.
+The templates shown depend on whether a project administrator has assigned specific templates to the project:
+
+- **Templates assigned** — Only the assigned templates appear in the dropdown.
+- **No templates assigned** — All enabled templates are shown (default behavior).
+
+A default template is pre-selected automatically using this priority:
+
+1. **Project default** — If a project-specific default has been set in **Settings > QuickScript**.
+2. **Global default** — If a system-wide default template exists and is available in the current list.
+3. **First available** — The first template in the list.
+
+To configure which templates are available for your project, see [Per-Project Template Assignment](../quickscript-templates.md#per-project-template-assignment).
### Output Mode
diff --git a/docs/docs/user-guide/quickscript-templates.md b/docs/docs/user-guide/quickscript-templates.md
index 398b2feb..22f928d0 100644
--- a/docs/docs/user-guide/quickscript-templates.md
+++ b/docs/docs/user-guide/quickscript-templates.md
@@ -72,7 +72,23 @@ You can filter templates by searching across name, category, framework, language
### Setting a Default Template
-Click the **Default** toggle for any template. The previous default is unset automatically. The default template is pre-selected when users open the QuickScript dialog from the Repository.
+Click the **Default** toggle for any template. The previous default is unset automatically. This global default is pre-selected when users open the QuickScript dialog — unless a project-specific default has been configured (see below).
+
+### Per-Project Template Assignment
+
+By default, all enabled templates are available in every project's QuickScript dialog. Project administrators can restrict which templates appear for their project and set a project-specific default:
+
+1. Navigate to the project's **Settings > QuickScript** page.
+2. In the **Export Templates** section, use the combobox to assign templates to the project.
+3. Optionally select a **Default Template** from the assigned templates.
+4. Click **Save Template Assignments**.
+
+**Behavior:**
+
+- When templates are assigned to a project, only those templates appear in the QuickScript dialog for that project.
+- The project default (if set) is pre-selected, overriding the global default.
+- If no templates are assigned to a project, all enabled templates are shown (backward compatible).
+- If all assigned templates have been disabled or deleted, the QuickScript dialog shows a message indicating no templates are available.
## Template Syntax
diff --git a/testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx b/testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx
index c89cfd02..40fa0f34 100644
--- a/testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx
+++ b/testplanit/app/[locale]/projects/repository/[projectId]/QuickScriptModal.tsx
@@ -13,7 +13,11 @@ import {
fetchCasesForQuickScript,
type QuickScriptCaseData
} from "~/app/actions/quickScriptActions";
-import { useFindManyCaseExportTemplate } from "~/lib/hooks";
+import {
+ useFindManyCaseExportTemplate,
+ useFindManyCaseExportTemplateProjectAssignment,
+ useFindUniqueProjects,
+} from "~/lib/hooks";
import { logDataExport } from "~/lib/services/auditClient";
import { Badge } from "@/components/ui/badge";
@@ -220,19 +224,75 @@ export function QuickScriptModal({
orderBy: [{ isDefault: "desc" }, { name: "asc" }],
});
- // Auto-select the default template when templates load
- const defaultTemplate = templates?.find((t) => t.isDefault);
+ // Fetch project template assignments (EXPORT-01, EXPORT-03)
+ const { data: assignments } = useFindManyCaseExportTemplateProjectAssignment({
+ where: { projectId },
+ });
+
+ // Fetch project for defaultCaseExportTemplateId (EXPORT-02)
+ const { data: project } = useFindUniqueProjects({
+ where: { id: projectId },
+ select: { defaultCaseExportTemplateId: true },
+ });
+
+ // Determine whether the project has any template assignments at all
+ const hasAssignments =
+ assignments !== undefined &&
+ assignments !== null &&
+ assignments.length > 0;
+
+ // Build the filtered template list:
+ // - No assignments exist → show all enabled templates (backward compatible, EXPORT-03)
+ // - Assignments exist → show only assigned templates that are enabled and not deleted (EXPORT-01)
+ const filteredTemplates = useMemo(() => {
+ if (!hasAssignments) {
+ return templates ?? [];
+ }
+ const assignedTemplateIds = new Set(
+ assignments!.map((a) => a.templateId)
+ );
+ return (templates ?? []).filter((t) => assignedTemplateIds.has(t.id));
+ }, [hasAssignments, assignments, templates]);
+
+ // Detect the "assignments exist but all are unavailable" empty state
+ const assignmentsExistButEmpty =
+ hasAssignments && filteredTemplates.length === 0;
+
+ // Template pre-selection priority (EXPORT-02):
+ // 1. User's explicit selection (selectedTemplateId)
+ // 2. Project default (defaultCaseExportTemplateId) — if it's in the filtered list
+ // 3. Global isDefault — if it's in the filtered list
+ // 4. First available template
+ const defaultTemplate = useMemo(() => {
+ if (!filteredTemplates || filteredTemplates.length === 0) return undefined;
+
+ // Project default takes precedence
+ if (project?.defaultCaseExportTemplateId) {
+ const projectDefault = filteredTemplates.find(
+ (t) => t.id === project.defaultCaseExportTemplateId
+ );
+ if (projectDefault) return projectDefault;
+ }
+
+ // Fall back to global isDefault
+ const globalDefault = filteredTemplates.find((t) => t.isDefault);
+ if (globalDefault) return globalDefault;
+
+ // Fall back to first available
+ return filteredTemplates[0];
+ }, [filteredTemplates, project?.defaultCaseExportTemplateId]);
+
const effectiveTemplateId =
selectedTemplateId || (defaultTemplate ? String(defaultTemplate.id) : "");
- const selectedTemplate = templates?.find(
+ const selectedTemplate = filteredTemplates?.find(
(tmpl) => String(tmpl.id) === effectiveTemplateId
);
const groupedTemplates = useMemo(() => {
- if (!templates) return [];
- const groups = new Map();
- for (const tmpl of templates) {
+ if (!filteredTemplates || filteredTemplates.length === 0) return [];
+ const groups = new Map();
+ for (const tmpl of filteredTemplates) {
const category = tmpl.category || "Other";
if (!groups.has(category)) groups.set(category, []);
groups.get(category)!.push(tmpl);
@@ -244,7 +304,7 @@ export function QuickScriptModal({
if (b === defaultCategory) return 1;
return a.localeCompare(b);
});
- }, [templates, defaultTemplate]);
+ }, [filteredTemplates, defaultTemplate]);
// Check AI availability when modal opens (GEN-02)
useEffect(() => {
@@ -425,7 +485,7 @@ export function QuickScriptModal({
const handleExport = useCallback(async () => {
if (!effectiveTemplateId || selectedCaseIds.length === 0) return;
- const template = templates?.find(
+ const template = filteredTemplates?.find(
(t) => t.id === parseInt(effectiveTemplateId)
);
if (!template) return;
@@ -674,7 +734,7 @@ export function QuickScriptModal({
}, [
effectiveTemplateId,
selectedCaseIds,
- templates,
+ filteredTemplates,
aiEnabled,
projectId,
outputMode,
@@ -728,90 +788,96 @@ export function QuickScriptModal({