feat(jira): support a default saved view (#3924)

This commit is contained in:
Carlos Florêncio
2026-09-25 15:00:32 +01:00
committed by GitHub
parent 719a755a45
commit 1b9c2de146
50 changed files with 2570 additions and 301 deletions
+109
View File
@@ -0,0 +1,109 @@
---
created: 2026-09-24
status: implemented
requirements:
- REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001
system_design:
- ../../specs/integrations/system-design/jira-default-view.md
legacy_specs: []
---
# Implementation Plan: Jira Default View
## Overview
Issue [#3921](https://github.com/kdlbs/kandev/issues/3921) reports that every `/jira` visit starts on **Assigned to me** even when a user always works from the same saved custom-JQL view. `useFilterState` initializes `activeViewId` to `builtin:assigned`, while `useSavedViews` hydrates saved views later without selecting one. Add a per-user default ID, resolve it before the first ticket search, and expose set/clear controls in the existing view picker. Implement the settings contract first, then selection and persistence, then the rendered controls and end-to-end proof.
## Scope
### In scope
- One user-owned default that may identify a built-in or custom Jira view.
- Exact saved-filter and custom-JQL restoration, missing-view fallback, and unchanged no-default behavior.
- Desktop and phone picker actions, translations, public documentation, and focused regression coverage.
### Out of scope
- Active-view URL parameters, bookmarks, and sharing.
- Last-selected-view persistence or defaults for other providers.
- Changing Jira workspace configuration or saved-view query semantics.
## Technical approach
1. Add scalar `jira_default_view_id` to the existing user-settings GET/PATCH path: `apps/backend/internal/user/models/models.go`, `dto/dto.go`, `controller/controller.go`, `service/service.go`, `store/sqlite.go`, and `internal/settingscatalog/defaults.go`. Regenerate `apps/web/lib/settings-discovery/contract.generated.json`. Add the web transport field in `apps/web/lib/types/http-user-settings.ts`. Empty string clears the choice; an omitted field preserves it.
2. Extend `apps/web/components/jira/my-jira/use-saved-views.ts` to hydrate the default and views together, expose readiness and an acknowledged set/clear operation, and clear the default in the same PATCH as default-view deletion. Add a pure resolver for an available default and the legacy **Assigned to me** fallback. Use `apps/web/app/jira/jira-page-client.tsx` to apply the resolved view once, protect manual interaction during hydration, restore custom JQL, and gate `useJiraSearch` until initial selection is known.
3. Add marker and sibling set/clear action to `apps/web/components/jira/my-jira/list-toolbar.tsx`. The selected view and default view are distinct states. Reuse the existing picker and deletion confirmation. Localize new copy in all Jira catalogs and document the user workflow in `docs/public/integrations.md`.
## ASCII UI preview
`UI-01` is the existing `/jira` saved-view picker. The primary row action selects a view; the star action changes the future default. The drawing shows control order and states, not pixel spacing or final icon styling.
Current desktop and phone picker (source: `ListToolbar`):
```text
Views: Assigned to me
Built in
[check] Assigned to me
Unassigned
Saved
My open tickets [Delete]
```
Proposed desktop picker, after **My open tickets** is marked default:
```text
Views: Assigned to me
Built in
[check] Assigned to me [Set default]
Unassigned [Set default]
Saved
My open tickets [star: default] [Delete]
```
Proposed phone picker, using the current inset picker surface:
```text
Views: Assigned to me [tap]
Built in
[check] Assigned to me [star, 44px]
Unassigned [star, 44px]
Saved
My open tickets [star, 44px] [Delete, 44px]
(one vertical scroll region; actions stay inside the viewport)
```
The visible star identifies the default; its accessible name says **Set ... as default view** or **Clear ... as default view**. A default action leaves the current view unchanged. The phone surface keeps a visible touch target for each action and does not rely on hover. This preview covers `AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.5` and `.6`.
## Tests
| Acceptance criteria | Focused evidence |
| --- | --- |
| `.1`, `.7` | Backend GET/PATCH and SQLite round-trip tests; `use-saved-views.test.ts` acknowledged writes, replacement, clear, and failure tests. |
| `.2`, `.3`, `.4`, `.5` | New pure resolver and `jira-page-client` state tests, including exact custom JQL, no-default project key, deleted view, and late hydration after manual selection. |
| `.6` | `list-toolbar.test.tsx` selection isolation, accessible names, and phone hit-target assertions. |
## E2E tests
- `apps/web/e2e/tests/integrations/jira-default-view.spec.ts` (desktop): mark a custom JQL view as default, revisit `/jira`, inspect the query and results, delete it, and verify the next visit falls back to **Assigned to me**. Covers `.1` through `.4`.
- `apps/web/e2e/tests/integrations/mobile-jira-default-view.spec.ts` (`mobile-chrome`): set and clear a default in the phone picker, revisit the page, and verify touch reachability and no horizontal overflow. Covers `.1`, `.2`, `.5`, and `.6`.
## Work orders
- [x] [Task 01: Persist the Jira default preference](task-01-persist-jira-default.md)
- [x] [Task 02: Restore the default Jira view](task-02-restore-jira-default.md)
- [x] [Task 03: Add picker controls and prove the flow](task-03-jira-default-controls.md)
Work orders run sequentially in the primary session. Planning a dependency wave does not authorize delegation.
## Verification results
All three work orders are complete. The user settings contract persists a per-user default view ID. `/jira` restores an available built-in or custom view, including exact custom JQL, before its displayed ticket search, and retains a manual choice made during hydration. The picker provides localized set/clear actions on desktop and phone. Deleting the active default clears it and returns the current and next visit to **Assigned to me**. Status reconciliation runs only after authoritative metadata arrives and only for structured filters; a failed status lookup preserves saved statuses and custom JQL. The results remain loading until initial selection resolves. Saved-view list mutations serialize, new views publish only after an acknowledged settings write, and a later user selection takes precedence over a pending save or deletion. The desktop picker keeps its active default star visible without hover. Public Jira documentation explains how to choose or clear a default.
Focused backend tests pass. The six-file Jira web suite passes 47 tests. Web typecheck, ESLint, i18n, and E2E sleep checks pass. The rebuilt desktop and Pixel 5 Playwright scenarios each pass. The production web build and both public documentation validators pass.
## Risks
- Settings hydrate after the page mounts. Initial search must wait for the resolved target, and late responses must not overwrite a user action.
- Saved views and the default ID share one user-settings document. Deleting the default must clear both fields in one PATCH; failed writes must retain the prior state.
- A custom JQL may refer to projects unavailable in another workspace. The page should preserve the saved query and show the existing Jira search error rather than silently rewrite it.
- The picker is narrow on phones; star and delete targets must remain reachable without horizontal scrolling.
@@ -0,0 +1,75 @@
---
id: 01-persist-jira-default
title: Persist the Jira default preference
status: done
wave: 1
depends_on: []
plan: plan.md
requirements:
- REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001
acceptance_criteria:
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.1
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.3
system_design:
- ../../specs/integrations/system-design/jira-default-view.md
---
# Task 01: Persist the Jira default preference
## Summary
Expose one portable, per-user default view ID through the existing user-settings contract. This task provides storage and transport only; it does not select a view in the browser.
## Scope and likely files
- `apps/backend/internal/user/models/models.go`
- `apps/backend/internal/user/dto/dto.go` and `dto/dto_test.go`
- `apps/backend/internal/user/controller/controller.go`
- `apps/backend/internal/user/service/service.go` and `service/service_test.go`
- `apps/backend/internal/user/store/sqlite.go` and `sqlite_test.go`
- `apps/backend/internal/settingscatalog/defaults.go` and relevant catalog tests
- `apps/web/lib/types/http-user-settings.ts`
- Regenerated `apps/web/lib/settings-discovery/contract.generated.json`
## Exclusions
- No Jira connection-setting change, dedicated database column, or URL contract.
## Implementation acceptance
1. GET returns the user's saved ID, and PATCH distinguishes omitted, nonempty, and empty-string values without changing other settings.
2. The preference survives a SQLite round-trip and appears in settings discovery as a caller-owned string.
3. Existing user records return the empty default without a migration.
## TDD and verification
First add tests for omitted/set/clear requests and SQLite round-trip; confirm the new assertions fail before implementation. Then implement the contract and run:
```bash
cd apps/backend && go test ./internal/user/dto ./internal/user/service ./internal/user/store ./internal/settingscatalog
cd apps/backend && go run ./cmd/settings-catalog
node --test scripts/settings-contract-ci.test.mjs
```
Run the last command from the repository root.
Red: the focused backend test command failed to compile because the expected `JiraDefaultViewID` model and DTO fields were absent; the catalog assertion also reported that `user_settings.jira_default_view_id` was missing.
Green:
```text
cd apps/backend && go test ./internal/user/dto ./internal/user/service ./internal/user/store ./internal/settingscatalog ./internal/user/controller
PASS
cd apps/backend && go run ./cmd/settings-catalog
PASS
node --test scripts/settings-contract-ci.test.mjs
3 passed, 0 failed
```
## Dependencies and risks
No prior work order. Existing JSON payload persistence must include the new field on both save and load; a response-only DTO change would silently lose it.
## Result
The scalar preference now flows through the user model, GET/PATCH DTOs, controller and service, SQLite JSON payload, settings catalog, generated web contract, and frontend HTTP settings types. Omitted PATCH fields preserve the prior value, nonempty IDs are trimmed, and an empty ID clears the preference. Existing payloads without the field decode to the empty default without a schema migration.
@@ -0,0 +1,77 @@
---
id: 02-restore-jira-default
title: Restore the default Jira view
status: done
wave: 2
depends_on:
- 01-persist-jira-default
plan: plan.md
requirements:
- REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001
acceptance_criteria:
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.1
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.2
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.3
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.4
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.5
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.7
system_design:
- ../../specs/integrations/system-design/jira-default-view.md
---
# Task 02: Restore the default Jira view
## Summary
Hydrate the saved views and default ID together, then resolve the first Jira search from the selected view. Keep a manual choice ahead of late hydration and preserve the current no-default landing behavior.
## Scope and likely files
- `apps/web/components/jira/my-jira/use-saved-views.ts` and `use-saved-views.test.ts`
- A small pure resolver and focused tests under `apps/web/components/jira/my-jira/`
- `apps/web/app/jira/jira-page-client.tsx` and a focused page-state test
- `apps/web/components/jira/my-jira/use-jira-search.ts` only if its current empty-JQL gate cannot safely delay the initial request
## Exclusions
- No toolbar markup, new URL state, or changes to Jira search composition.
## Implementation acceptance
1. A valid default restores its exact saved filters or custom JQL before the first ticket search; missing defaults and absent preferences use the specified fallback.
2. Set/clear mutations publish only after persistence succeeds; default deletion clears the ID and view together, and a failed write restores prior state.
3. A manual selection during hydration and later default changes do not replace the active view.
## TDD and verification
Add regression tests for a custom JQL default, a deleted ID, the no-default project-key case, manual selection during delayed hydration, and failed/ordered mutations. Confirm expected red failures, implement, then run:
```bash
cd apps/web && pnpm exec vitest run components/jira/my-jira/use-saved-views.test.ts components/jira/my-jira/jira-default-view.test.ts app/jira/jira-page-client.test.tsx
cd apps/web && pnpm run typecheck
```
If a new test file gets a different final name, update this command in the work order before implementation.
Red: the initial frontend run showed that the search fired before settings resolved, readiness/default state was absent from `useSavedViews`, and the resolver/page-state modules did not exist.
Green:
```text
cd apps/web && pnpm exec vitest run components/jira/my-jira/use-saved-views.test.ts components/jira/my-jira/jira-default-view.test.ts app/jira/jira-page-client.test.tsx
19 passed, 0 failed
cd apps/web && pnpm run typecheck
PASS
```
The expanded focused run also passed all 28 tests across those three files, search gating, and project-status hydration.
## Dependencies and risks
Depends on Task 01's settings field. `useSavedViews` currently stages mutations before hydration and uses a queued array write; extend that queue without dropping concurrent view saves or default changes. `useJiraSearch` must never run the old query first when a saved default exists.
## Result
Saved views and the default ID hydrate from the same response. An available built-in or custom view restores its saved filters and exact custom JQL before the first ticket search. Missing IDs use the existing Assigned to me filters with the workspace default project key. Selection and filter edits made during hydration win over the late response. Default writes publish only after acknowledgment; deleting the default clears its ID and saved-view row in one PATCH, and a failed write retains both.
Review follow-up keeps saved structured statuses when any selected project-status lookup fails, and holds the results in a loading state until initial selection resolves. A newer selection or filter edit also wins when an earlier save or default deletion finishes later. These cases pass in the focused six-file web suite.
@@ -0,0 +1,137 @@
---
id: 03-jira-default-controls
title: Add picker controls and prove the flow
status: done
wave: 3
depends_on:
- 02-restore-jira-default
plan: plan.md
requirements:
- REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001
acceptance_criteria:
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.1
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.2
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.4
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.5
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.6
- AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.7
system_design:
- ../../specs/integrations/system-design/jira-default-view.md
---
# Task 03: Add picker controls and prove the flow
## Summary
Add a default marker and accessible set/clear action to every Jira view row, then verify the behavior in desktop and phone browsers. Update the public Jira guide.
## Scope and likely files
- `apps/web/components/jira/my-jira/list-toolbar.tsx` and `list-toolbar.test.tsx`
- `apps/web/app/jira/jira-page-client.tsx` for action props and user-visible failure feedback
- `apps/web/src/locales/{en,pt-pt,zh-cn,zh-hk,zh-tw,ja,pseudo}/jira.json`; use `pnpm run i18n:zh-hant` for the Traditional Chinese pair
- `apps/web/e2e/tests/integrations/jira-default-view.spec.ts`
- `apps/web/e2e/tests/integrations/mobile-jira-default-view.spec.ts`
- `docs/public/integrations.md`
## Exclusions
- No new phone navigation surface and no other provider's picker changes.
## Implementation acceptance
1. Both built-in and custom rows show their default state; set/clear actions leave the current view and delete action untouched, including while a write is pending or fails.
2. Desktop and phone browser flows prove a custom JQL default on revisit and fallback after deletion; phone actions have at least 44px touch targets and no document horizontal overflow.
3. All new copy is localized in the required catalogs, and the public Jira guide explains how to choose or clear a default.
## ASCII UI preview
`UI-01`, excerpt from the [combined plan preview](plan.md#ascii-ui-preview). The star is a sibling action, not part of the select button.
```text
Desktop: [check] Assigned to me [Set default]
My open tickets [star: default] [Delete]
Phone: [check] Assigned to me [star, 44px]
My open tickets [star, 44px] [Delete, 44px]
```
The picker remains the entry point and its list owns scrolling. The marker is visible; its localized accessible name distinguishes set from clear. This covers `AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.6`.
## TDD and verification
Add a toolbar regression for action isolation and accessibility and browser scenarios for desktop and phone. Confirm expected red failures, implement, then run:
```bash
cd apps/web && pnpm exec vitest run components/jira/my-jira/list-toolbar.test.tsx
cd apps/web && pnpm run i18n:check
make build-web
cd apps/web && pnpm e2e:run tests/integrations/jira-default-view.spec.ts
cd apps/web && pnpm e2e:run --project mobile-chrome tests/integrations/mobile-jira-default-view.spec.ts
node --test scripts/validate-public-docs.test.mjs
node scripts/validate-public-docs.mjs
```
Run `make build-web` and both documentation commands from the repository root. Record exact red and green results here.
## Dependencies and risks
Depends on Task 02's mutation and selection API. The current picker is narrow; verify actual touch hit boxes and viewport containment in the phone scenario. A pending default update must not allow a conflicting delete or star action.
## Result
The Jira saved-view picker now marks the active default and provides a sibling set/clear action for built-in and custom views. The action has a localized accessible name without a duplicate toggle-state announcement, does not select or delete its row, and stays at least 48px square on phones. The active default star remains visible on fine-pointer desktop. The picker owns vertical scrolling. Default writes publish only after persistence succeeds; pending writes disable save, star, and delete controls. Save and delete failures show localized retryable feedback. Jira project discovery no longer blocks the page while the initial view resolves, and status reconciliation prunes saved statuses only after authoritative metadata arrives for the current workspace and project set.
The public Jira guide now explains how to set or clear the default. The required Jira copy is present in English, Portuguese, Simplified Chinese, both generated Traditional Chinese catalogs, Japanese, and the pseudo-locale.
Red: the toolbar tests failed because the default buttons were absent. The desktop browser scenario timed out waiting for the missing set-default action. A new first-render status-readiness regression test failed because an effect observed the previous key set as loaded.
Green:
```text
pnpm exec vitest run components/jira/my-jira/use-project-statuses.test.ts components/jira/my-jira/jira-default-view.test.ts components/jira/my-jira/use-saved-views.test.ts app/jira/jira-page-client.test.tsx components/jira/my-jira/use-jira-search.test.ts components/jira/my-jira/list-toolbar.test.tsx
6 files passed, 33 tests passed
pnpm run typecheck
PASS
pnpm run i18n:check
PASS
pnpm e2e:run tests/integrations/jira-default-view.spec.ts
1 passed
pnpm e2e:run --project mobile-chrome tests/integrations/mobile-jira-default-view.spec.ts
1 passed
make build-web
PASS
go test ./internal/user/dto ./internal/user/service ./internal/user/store ./internal/settingscatalog ./internal/user/controller
PASS
node --test scripts/validate-public-docs.test.mjs
62 passed
node scripts/validate-public-docs.mjs
47 pages validated
git diff --check
PASS
```
The broad `pnpm run i18n:zh-hant` command was blocked by two pre-existing Simplified Chinese-looking workflow catalog entries. The Jira Traditional Chinese pair was generated successfully with `node scripts/convert-zh-cn-to-zh-hant.mjs --locale all --namespace jira --write`; the full i18n check passes.
## Review follow-up
Follow-up review fixes preserve saved structured statuses and custom JQL when project-status metadata is unavailable. They serialize saved-view list mutations, publish a new view only after its settings write succeeds, and keep the active default star visible on fine-pointer desktop. A newer selection or filter edit also wins over a pending save or deletion response. Save is disabled during settings mutations; rejected saves leave no local view to mark default and show localized failure feedback. The desktop and mobile browser specs use the causal HTTP wait helper for settings writes.
The focused regressions cover failed status lookups for saved queries, a deferred save or deletion followed by a newer selection, rejected saves followed by an attempt to set the missing ID as default, both pending-mutation states, active desktop-star visibility, and initial-search loading.
```text
pnpm exec vitest run components/jira/my-jira/use-project-statuses.test.ts components/jira/my-jira/use-saved-views.test.ts components/jira/my-jira/jira-default-view.test.ts components/jira/my-jira/use-jira-search.test.ts components/jira/my-jira/list-toolbar.test.tsx app/jira/jira-page-client.test.tsx
6 files passed, 47 tests passed
pnpm run typecheck
PASS
pnpm exec eslint app/jira/jira-page-client.tsx app/jira/jira-page-client.test.tsx components/jira/my-jira/jira-default-view.ts components/jira/my-jira/jira-default-view.test.ts components/jira/my-jira/jira-default-view-action.tsx components/jira/my-jira/list-toolbar.test.tsx components/jira/my-jira/use-jira-filter-state.ts components/jira/my-jira/use-project-statuses.ts components/jira/my-jira/use-project-statuses.test.ts components/jira/my-jira/use-saved-views.ts e2e/tests/integrations/jira-default-view.spec.ts e2e/tests/integrations/mobile-jira-default-view.spec.ts
PASS
pnpm run i18n:check
PASS
pnpm run e2e:sleep-ratchet
PASS
pnpm e2e:run tests/integrations/jira-default-view.spec.ts
1 passed
pnpm e2e:run --project mobile-chrome tests/integrations/mobile-jira-default-view.spec.ts
1 passed
```
+2
View File
@@ -609,6 +609,8 @@ Azure DevOps currently uses PAT authentication. It does not yet provide an Entra
Jira configuration is workspace-specific. Use `/jira` to search with JQL, save views, open issue details, run supported transitions, and launch tasks with Jira prompt presets. Launch copies Jira URL/content into the task title and description; it does not store a durable Jira issue association on the task.
In the **Views** picker, select the star beside a built-in or saved view to make it the default for future visits to `/jira`. Select the star again to clear the default. This does not change the current search.
Enter the site URL (a missing scheme is normalized to HTTPS), choose **Cloud** or **Server/Data Center**, and optionally set a default project key. Authentication options are:
| Deployment | Method | Required values |
@@ -0,0 +1,43 @@
---
status: active
system: integrations
created: 2026-09-24
owners:
- kandev
---
# Jira Default View Requirements
## Overview
People who repeatedly use one Jira saved view must select it again on every visit to `/jira`. The integration system owns this behavior because the choice restores Jira-specific filters and JQL. The preference belongs to the user, while the Jira connection and its default project key belong to the workspace.
## Terminology
- **Default view:** The one saved Jira view a user explicitly chooses for future visits to `/jira`.
- **Available view:** A built-in view or a valid custom view still present in the user's saved views.
## Requirements
### REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001: Personal Jira landing view
**Intent:** Let a user return to their preferred Jira ticket search without selecting it on every visit.
**User story:** As a developer, I want `/jira` to open my chosen view so I can work from the same filtered ticket list each day.
#### Acceptance criteria
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.1:** A user can mark one available built-in or custom Jira view as their default, replace that choice, or clear it. The choice persists for that user across visits and does not change another user's choice.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.2:** When `/jira` opens with an available default view, the page selects it and applies its saved filters or exact custom JQL before searching for the displayed ticket list.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.3:** When no default is set, `/jira` opens on **Assigned to me** and retains the existing workspace default-project-key behavior.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.4:** When the chosen default is missing, invalid, or deleted, `/jira` falls back to **Assigned to me** without failing. Deleting the default clears the choice; deleting the active default also updates the current page to the fallback view.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.5:** Changing the default affects future visits. It does not replace the view currently on screen. A user selection made while settings load also remains active after loading finishes.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.6:** The saved-view picker identifies the default and offers accessible set and clear actions on desktop and phone. The action does not select or delete its view, and phone actions remain reachable by touch.
- **AC-INTEGRATIONS-JIRA-DEFAULT-VIEW-001.7:** If saving the default fails, the prior default remains selected for future visits and the page shows an error that permits retry.
## Out of scope
- Remembering the last selected view without an explicit default.
- Encoding the active view in the URL or changing bookmark and sharing behavior.
- Applying this preference to Linear, GitHub, or other providers.
- Changing Jira's workspace default project key or the saved-view filter model.
@@ -0,0 +1,44 @@
---
status: current
system: integrations
requirements:
- REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001
---
# Jira Default View System Design
## Purpose and boundaries
The Jira integration owns view selection and restoration. The existing user-settings service provides per-user storage; Jira connection settings remain workspace-scoped. The default references a saved-view identity and does not duplicate filters or JQL.
## Requirement mapping
| Requirement | Design sections |
| --- | --- |
| `REQ-INTEGRATIONS-JIRA-DEFAULT-VIEW-001` | [Persistence and settings contract](#persistence-and-settings-contract), [Initial selection](#initial-selection), [Picker and mutations](#picker-and-mutations), [Failure and recovery](#failure-and-recovery) |
## Persistence and settings contract
Add `jira_default_view_id` beside `jira_saved_views` in `GET` and `PATCH /api/v1/user/settings`. It is a string containing either a built-in ID such as `builtin:assigned`, a custom saved-view ID, or `""` for no default. An omitted PATCH field leaves the value unchanged. The backend trims an explicitly supplied string; `""` clears it. This field flows through `internal/user` model, DTO, controller, service, SQLite JSON payload, and the settings catalog, then through the web HTTP type. No database migration or separate settings endpoint is needed because the existing user-settings payload stores new fields by JSON name. Only the authenticated user's settings are read or changed.
The frontend treats an ID as effective only when it matches a current built-in view or a normalized custom view in `useSavedViews`. It never trusts a dangling or malformed ID as a query. The current saved-view format and exact `customJql` string remain unchanged. Existing users have an empty default and retain existing landing behavior.
## Initial selection
`useSavedViews` hydrates saved views and the default ID from one user-settings response and exposes readiness. `useFilterState` resolves an initial target after that hydration. A valid custom default supplies its saved filters and `customJql`; a valid built-in supplies its built-in filters. The JQL editor opens for a JQL-backed default, following manual view selection. An explicit default does not inherit the workspace default project key. With no effective default, `initialFilters(defaultProjectKey)` preserves the current **Assigned to me** behavior.
`AuthenticatedView` does not start its ticket search until this initial target is resolved, so a default does not first issue an unrelated **Assigned to me** search. A manual selection or filter edit made before hydration wins over the late settings response. Subsequent default changes do not reinitialize the current view.
## Picker and mutations
The existing `ListToolbar` view rows gain a visible default marker and sibling set/clear action. The primary row button still selects the view. The default action uses an accessible name, pressed state, and localized copy; it never triggers row selection. Fine-pointer controls retain toolbar density, and phone/coarse-pointer controls expose a visible hit target of at least 44px. The existing picker remains the phone entry point and vertical scroll owner.
`useSavedViews` owns default mutations alongside saved-view mutations. Setting or clearing a default persists an acknowledged user-settings PATCH before publishing the new marker. Removing the default custom view sends the updated `jira_saved_views` array and an empty `jira_default_view_id` in the same PATCH. A default mutation in progress blocks conflicting default actions and deletion of that default. Removing the active default selects the legacy fallback on the current page.
## Failure and recovery
If settings hydration fails, the page remains usable with **Assigned to me** and can retry loading settings through the existing hook path. A missing or malformed default ID uses the fallback without writing settings merely because the page opened. If a default mutation fails, retain the prior rendered and persisted marker, surface a localized error, and permit retry. If a combined deletion PATCH fails, restore the prior saved-view list and default marker.
## Verification boundaries
Backend tests cover omitted, set, and clear PATCH semantics and SQLite round-trip. Hook tests cover hydration, default replacement, missing-view fallback, custom JQL, mutation ordering, and failure recovery. Component tests cover desktop and phone row actions. Desktop and mobile Playwright scenarios cover marking a custom default, reloading `/jira`, applying its exact JQL, and deleting it to reach the fallback.