From be380ba15574bd9bcb877174477444f1a48a702d Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Tue, 13 Oct 2026 09:26:37 -0500 Subject: [PATCH] fix(ui): hide agent-created tasks by default (#16157) Hide agent-created, system-created, and chat-channel tasks from independent collections by default. Preserve task hierarchy context and existing inbox participation. Add a saved opt-in checkbox without helper text. Co-Authored-By: Paperclip --- doc/PRODUCT.md | 20 ++ doc/SPEC-implementation.md | 20 ++ maps/agent-access-map/inbox-search.md | 8 + maps/agent-access-map/tasks.md | 11 + maps/feature-map/inbox-search.md | 2 + maps/feature-map/tasks.md | 2 + maps/storybook-map/INDEX.md | 101 ++++----- maps/storybook-map/catalog.json | 211 +++++++++--------- packages/shared/src/index.ts | 2 + .../shared/src/issue-creation-source.test.ts | 20 ++ packages/shared/src/issue-creation-source.ts | 17 ++ .../issues-creation-visibility.test.ts | 77 +++++++ server/src/routes/issues.ts | 16 ++ server/src/services/issues.ts | 25 +++ ui/src/api/issues.ts | 5 + ui/src/components/BlockedInboxView.tsx | 7 +- .../components/IssueFiltersPopover.test.tsx | 7 +- ui/src/components/IssueFiltersPopover.tsx | 12 +- ui/src/components/IssuesList.test.tsx | 92 ++++++-- ui/src/components/IssuesList.tsx | 91 +++++--- ui/src/components/LegacyIssuesList.tsx | 121 ++++++---- ui/src/hooks/useIssueHierarchyContext.ts | 50 +++++ ui/src/hooks/useShowAgentCreatedTasks.ts | 30 +++ ui/src/lib/inbox.test.ts | 28 +-- ui/src/lib/inbox.ts | 16 +- ui/src/lib/issue-filters.test.ts | 17 ++ ui/src/lib/issue-filters.ts | 18 +- ui/src/lib/issue-hierarchy-context.test.ts | 50 +++++ ui/src/lib/issue-hierarchy-context.ts | 64 ++++++ ui/src/lib/task-collection-preferences.ts | 5 + ui/src/pages/Inbox.tsx | 14 +- ui/src/pages/Issues.tsx | 6 +- ui/src/pages/LegacyInbox.tsx | 14 +- ui/src/pages/ProjectDetail.tsx | 7 +- ui/storybook/generated/feature-index.json | 7 +- .../tasks/external-objects.stories.tsx | 2 +- .../tasks/issue-management.stories.tsx | 22 +- .../support/issue-creation-visibility.tsx | 103 +++++++++ 38 files changed, 1012 insertions(+), 308 deletions(-) create mode 100644 packages/shared/src/issue-creation-source.test.ts create mode 100644 packages/shared/src/issue-creation-source.ts create mode 100644 server/src/__tests__/issues-creation-visibility.test.ts create mode 100644 ui/src/hooks/useIssueHierarchyContext.ts create mode 100644 ui/src/hooks/useShowAgentCreatedTasks.ts create mode 100644 ui/src/lib/issue-hierarchy-context.test.ts create mode 100644 ui/src/lib/issue-hierarchy-context.ts create mode 100644 ui/storybook/support/issue-creation-visibility.tsx diff --git a/doc/PRODUCT.md b/doc/PRODUCT.md index c95adef49b7..6d408844d3c 100644 --- a/doc/PRODUCT.md +++ b/doc/PRODUCT.md @@ -72,6 +72,26 @@ the latest agent that assigned it to them. If no assignment is available, it use the latest other commenter, or leaves the recipient empty. A manual recipient choice stays selected while task data refreshes. +### Task collection creation filters + +Tasks and project lists hide agent-created and system-created tasks by default. +The Filters menu's **show agent-created tasks** checkbox includes routine runs, +recovery/watchdog tasks, chat-channel tasks, and other automation. Channel tasks +remain bot-created for this filter even when a human starts the conversation. The choice is saved per company +and collection; Clear filters restores the hidden default. Nested lists preserve +ancestors and expandable subtasks as context without promoting unrelated automated +work to independent results. Agent profiles, routine histories, and task-detail +subtasks retain their full history. + +Inbox membership continues to follow human creation, assignment, comments, and +existing qualifying participation actions, including manually running a routine. +Routine ownership or responsibility alone does not subscribe a person to scheduled +runs; opening or reading a task is not participation. Inbox discovery searches and +Blocked hide unrelated automated tasks unless the checkbox is checked, while +preserving tasks admitted by the existing participation rules. Checking it does +not subscribe the user to all automation. Approvals and other non-task items keep +their existing rules. + ### Company Skills and Policy Company skills are shared operating capabilities, not privileged objects by default. Every authenticated agent in a company can create, import, install, edit, update, test, reset, and remove that company's skills unless the company has configured an explicit restriction. diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md index bd4b0738ec2..f32064cb813 100644 --- a/doc/SPEC-implementation.md +++ b/doc/SPEC-implementation.md @@ -2187,3 +2187,23 @@ See [the layer architecture](plans/2026-10-09-task-workspaces.md) for resolution ## Company fast-response service Companies may configure an optional shared API connection and model for short acknowledgements of accepted human turns. Fast responses use direct SDK inference alongside normal agent work, with a three-second admission deadline, bounded prompts/output, current authorization and independent `fast_response` accounting. They are server-marked platform receipts attributed to the agent, never completion, interaction or recovery evidence. Company-sponsored unlinked external senders are explicit; linked-user permission denials are never bypassed. See [fast-responses.md](fast-responses.md) for provider, delivery, history and endpoint contracts. + +### Task collection creation visibility (2026-10-13) + +Tasks and project collections default to hiding agent/system-created tasks and +provide a saved **show agent-created tasks** filter. Classify creation using +creator identity and origin; assignee/responsible users do not imply human +authorship. Routine executions remain automated even when manually triggered. +Creator-less ordinary tasks are system-created. All chat-channel tasks count as +bot-created for this filter, including human-initiated inbound conversations. +Human ordinary and onboarding tasks remain visible. Apply collection creation filters before search limits and +pagination, and retrieve hierarchy context separately under existing access checks. +Context does not count as independent results or board cards. Bounded context +reads retain loaded rows while users reveal more of the tree. + +Inbox membership, archives, unread state, and badges retain existing participation +rules, including assignment, comments, and manual routine runs. Responsibility and +read-only activity do not admit scheduled work. Inbox discovery/Blocked preserve +participated automation and hide unrelated automation by default. Agent profile +history, routine history, and task-detail subtasks remain complete. API consumers +that omit the optional creation filter retain their existing list behavior. diff --git a/maps/agent-access-map/inbox-search.md b/maps/agent-access-map/inbox-search.md index 60e3265de7a..d51d0d989dd 100644 --- a/maps/agent-access-map/inbox-search.md +++ b/maps/agent-access-map/inbox-search.md @@ -11,6 +11,14 @@ for format, permission checks, evidence and maintenance. ## `views` +The issue-list API accepts optional `includeAgentCreatedTasks`; false restricts +independent results to human creation before search/pagination. Omission retains +existing agent/API behavior. `includeParticipatedTasks=true` requires a board user +and retains only that authenticated user's existing participation as a creation +visibility exception. Company/private-task access checks still apply. Collection +preferences and hierarchy presentation are browser state, not additional authority. +Source-reviewed behavior; live agent access verification remains separate. + Review: `baseline`. API: `present` — representative operation IDs: diff --git a/maps/agent-access-map/tasks.md b/maps/agent-access-map/tasks.md index cf040965c7f..c2148348330 100644 --- a/maps/agent-access-map/tasks.md +++ b/maps/agent-access-map/tasks.md @@ -37,6 +37,17 @@ narrower/different APIs. ## `organize` +The issue-list API accepts optional `includeAgentCreatedTasks`; false restricts +independent results to human creation before search/pagination. Omission retains +existing agent/API behavior. `includeParticipatedTasks=true` requires a board user +and retains only that authenticated user's existing participation as a creation +visibility exception. Company/private-task access checks still apply. Collection +preferences and hierarchy presentation are browser state, not additional authority. +Source-reviewed behavior; live agent access verification remains separate. + +All `chat_channel` origins count as bot-created for creation visibility, including +human-initiated conversations. Existing inbox participation remains independent. + Review: `baseline`. API: `present` — representative operation IDs: diff --git a/maps/feature-map/inbox-search.md b/maps/feature-map/inbox-search.md index 687ec84534b..4c5b60696b9 100644 --- a/maps/feature-map/inbox-search.md +++ b/maps/feature-map/inbox-search.md @@ -46,6 +46,8 @@ cover exclusion of historical automatic read cleanup from user participation, unread counts, and activity timestamps while retaining explicit user decisions and audit history. +Manual: Seed a scheduled routine execution owned by the current human. It stays out of Mine until an existing participation rule admits it. Verify comments, assignment, and manual routine runs admit automated tasks while reading alone does not. Inbox discovery and Blocked hide unrelated automation by default; the **show agent-created tasks** checkbox reveals it without subscribing the user. Reload and verify membership, unread badges, archive/resurfacing, and preference persistence. + Manual: Read an unread task, return to Unread, archive a handled item, and reload. Verify personal triage state changes while task status stays correct. Switch users and companies to check isolation. Seed a completed task whose only activity under the viewing user is automatic diff --git a/maps/feature-map/tasks.md b/maps/feature-map/tasks.md index 6ed9fd7f353..b1fce19b313 100644 --- a/maps/feature-map/tasks.md +++ b/maps/feature-map/tasks.md @@ -49,6 +49,8 @@ Use a human/unassigned task to verify persistence without invoking a model. Automated: [task-list helpers](../../ui/src/pages/Issues.test.tsx) cover search URLs, pagination, deduplication, and presentation constants. The suite does not render the page or exercise filtering, grouping, opening a task, and returning; those interactions still need a running-browser check. +Manual: Seed human-created, agent-created, scheduled routine, human-initiated chat-channel, and creator-less tasks. Confirm automation is hidden by default, **show agent-created tasks** reveals it, reload preserves the choice, and Clear filters hides it again. Verify list and board search with automation newer than the human results, and expand contextual ancestors/subtasks without promoting unrelated automated roots. Repeat in project task lists. Agent profiles and task-detail subtasks retain complete history. + Manual: Filter by status and assignee, change grouping/sort, open a task, and return. Verify the correct records and selected view survive navigation and reload. Include an empty result and a failed request. ### `task-detail` diff --git a/maps/storybook-map/INDEX.md b/maps/storybook-map/INDEX.md index b64c3cb40df..ec3a4c08b5a 100644 --- a/maps/storybook-map/INDEX.md +++ b/maps/storybook-map/INDEX.md @@ -4,7 +4,7 @@ [Contract and maintenance](README.md) · [Review findings](REVIEW.md) -223 story files; 2212 indexed stories. Counts describe inventory, not passed tests or complete feature coverage. +223 story files; 2215 indexed stories. Counts describe inventory, not passed tests or complete feature coverage. Status describes product alignment; implementation describes the rendered subject; boundary describes simulated data. Sources and limitations are in catalog.json. Related capabilities cross-list one canonical story file. @@ -30,14 +30,14 @@ Sessions, bootstrap, membership, roles, and CLI authorization. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Getting started and access/Login, invitations, and access/Private tasks/00 Review guide](../../ui/storybook/stories/access/private-tasks/00-review-guide.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/01 Creation](../../ui/storybook/stories/access/private-tasks/01-creation.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 22 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/02 Sharing](../../ui/storybook/stories/access/private-tasks/02-sharing.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 26 / 10 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/03 Task actions](../../ui/storybook/stories/access/private-tasks/03-task-actions.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 11 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/04 References and blockers](../../ui/storybook/stories/access/private-tasks/04-references-and-blockers.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 12 / 5 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/05 Projects](../../ui/storybook/stories/access/private-tasks/05-projects.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 19 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages](../../ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 21 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Getting started and access/Login, invitations, and access/Private tasks/07 User stories](../../ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 13 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | +| [Getting started and access/Login, invitations, and access/Private tasks/00 Review guide](../../ui/storybook/stories/access/private-tasks/00-review-guide.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/01 Creation](../../ui/storybook/stories/access/private-tasks/01-creation.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 22 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/02 Sharing](../../ui/storybook/stories/access/private-tasks/02-sharing.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 26 / 10 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/03 Task actions](../../ui/storybook/stories/access/private-tasks/03-task-actions.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 11 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/04 References and blockers](../../ui/storybook/stories/access/private-tasks/04-references-and-blockers.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 12 / 5 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/05 Projects](../../ui/storybook/stories/access/private-tasks/05-projects.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 19 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages](../../ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 21 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/07 User stories](../../ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 13 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | ### Companies and portability @@ -63,27 +63,27 @@ Creation, assignment, lists, properties, comments, and completion. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards](../../ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments](../../ui/storybook/stories/tasks/chat-comments.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview](../../ui/storybook/stories/tasks/completed-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Composer/Add menu](../../ui/storybook/stories/tasks/composer-add-menu.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 14 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker](../../ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx) | `tasks/create`, `runs-adapters/availability` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker](../../ui/storybook/stories/tasks/composer-model-picker.stories.tsx) | `tasks/create`, `runs-adapters/availability` | mixed / mixed / fixtures | 57 / 30 | Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Product/Control Plane Surfaces](../../ui/storybook/stories/tasks/control-plane-surfaces.stories.tsx) | `tasks/organize`, `questions-and-approvals/governance`, `budgets-costs/budgets`, `activity/activity` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Product/Dialogs & Modals](../../ui/storybook/stories/tasks/dialogs-modals.stories.tsx) | `tasks/create`, `agents/hire`, `projects/lifecycle`, `goals/hierarchy` | unreviewed / production / fixtures | 17 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/External Objects](../../ui/storybook/stories/tasks/external-objects.stories.tsx) | `tasks/properties` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Product/Issue Management](../../ui/storybook/stories/tasks/issue-management.stories.tsx) | `tasks/properties`, `tasks/organize` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Components/Entity pickers/Mobile](../../ui/storybook/stories/tasks/mobile-entity-pickers.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Composer/New task](../../ui/storybook/stories/tasks/new-task-composer.stories.tsx) | `tasks/create`, `workspaces/binding` | current / production / fixtures | 40 / 25 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. WorktreePicker, ReuseWorktree, CreateWithReusedWorktree and NewWorktreeAfterReuse exercise workspace selection and submitted fixture request values. Actual admission/binding and execution remain outside this story. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview](../../ui/storybook/stories/tasks/runner-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff](../../ui/storybook/stories/tasks/successful-run-handoff.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes](../../ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Task chat/Mobile streaming follow](../../ui/storybook/stories/tasks/task-chat-mobile-follow.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Product/Task chat/System notice](../../ui/storybook/stories/tasks/task-chat-system-notice.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Context notices](../../ui/storybook/stories/tasks/task-context-notices.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/00 Review guide](../../ui/storybook/stories/tasks/thread-human-identity/00-evidence.stories.tsx) | `tasks/discussion` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/01 Components](../../ui/storybook/stories/tasks/thread-human-identity/01-components.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/02 Mixed-author journey](../../ui/storybook/stories/tasks/thread-human-identity/02-task-journey.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | +| [Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards](../../ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments](../../ui/storybook/stories/tasks/chat-comments.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview](../../ui/storybook/stories/tasks/completed-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Composer/Add menu](../../ui/storybook/stories/tasks/composer-add-menu.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 14 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker](../../ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx) | `tasks/create`, `runs-adapters/availability` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker](../../ui/storybook/stories/tasks/composer-model-picker.stories.tsx) | `tasks/create`, `runs-adapters/availability` | mixed / mixed / fixtures | 57 / 30 | Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Product/Control Plane Surfaces](../../ui/storybook/stories/tasks/control-plane-surfaces.stories.tsx) | `tasks/organize`, `questions-and-approvals/governance`, `budgets-costs/budgets`, `activity/activity` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Product/Dialogs & Modals](../../ui/storybook/stories/tasks/dialogs-modals.stories.tsx) | `tasks/create`, `agents/hire`, `projects/lifecycle`, `goals/hierarchy` | unreviewed / production / fixtures | 17 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/External Objects](../../ui/storybook/stories/tasks/external-objects.stories.tsx) | `tasks/properties` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Product/Issue Management](../../ui/storybook/stories/tasks/issue-management.stories.tsx) | `tasks/properties`, `tasks/organize` | unreviewed / production / fixtures | 11 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Focused creation-visibility stories mount the production collection with simulated hierarchy reads and assert hidden roots, opt-in inclusion, and the unchecked filter control. Creation visibility examples and fixture setup now live in a support module so the surface matrix stays below 1000 lines; exported story IDs are unchanged. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Components/Entity pickers/Mobile](../../ui/storybook/stories/tasks/mobile-entity-pickers.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Composer/New task](../../ui/storybook/stories/tasks/new-task-composer.stories.tsx) | `tasks/create`, `workspaces/binding` | current / production / fixtures | 40 / 25 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. WorktreePicker, ReuseWorktree, CreateWithReusedWorktree and NewWorktreeAfterReuse exercise workspace selection and submitted fixture request values. Actual admission/binding and execution remain outside this story. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview](../../ui/storybook/stories/tasks/runner-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff](../../ui/storybook/stories/tasks/successful-run-handoff.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes](../../ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Task chat/Mobile streaming follow](../../ui/storybook/stories/tasks/task-chat-mobile-follow.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Product/Task chat/System notice](../../ui/storybook/stories/tasks/task-chat-system-notice.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Context notices](../../ui/storybook/stories/tasks/task-context-notices.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/00 Review guide](../../ui/storybook/stories/tasks/thread-human-identity/00-evidence.stories.tsx) | `tasks/discussion` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/01 Components](../../ui/storybook/stories/tasks/thread-human-identity/01-components.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/02 Mixed-author journey](../../ui/storybook/stories/tasks/thread-human-identity/02-task-journey.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | ### Delegation, dependencies, and signoff @@ -95,23 +95,23 @@ Child work, prerequisites, reviewers, and task-tree controls. | --- | --- | --- | --- | --- | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Decisions/Stalled review actions](../../ui/storybook/stories/task-coordination/issue-review-panel.stories.tsx) | `task-coordination/signoff` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Issue Monitor surfaces](../../ui/storybook/stories/task-coordination/monitor-surfaces.stories.tsx) | `task-coordination/tree-control` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task](../../ui/storybook/stories/task-coordination/originating-tasks.stories.tsx) | `task-coordination/delegation`, `projects/task-context` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. | -| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Sub-issues Workflow Checklist](../../ui/storybook/stories/task-coordination/sub-issues-workflow.stories.tsx) | `task-coordination/dependencies`, `task-coordination/delegation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | +| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task](../../ui/storybook/stories/task-coordination/originating-tasks.stories.tsx) | `task-coordination/delegation`, `projects/task-context` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. | +| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Sub-issues Workflow Checklist](../../ui/storybook/stories/task-coordination/sub-issues-workflow.stories.tsx) | `task-coordination/dependencies`, `task-coordination/delegation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces. | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Task Watchdog surfaces](../../ui/storybook/stories/task-coordination/task-watchdog-surfaces.stories.tsx) | `task-coordination/tree-control` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | ### Inbox, decisions, and search -Personal triage, decision queues, unread/blocked views, and search. +Personal triage, decision queues, unread/blocked views, search, and personal task toasts. [Feature recipe](../feature-map/inbox-search.md) | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab](../../ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx) | `inbox-search/triage`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Tasks and conversations/Inbox, decisions, and search/Decisions/DecisionCard](../../ui/storybook/stories/inbox-search/decision-card.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions Desk](../../ui/storybook/stories/inbox-search/decisions-desk.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Tasks and conversations/Inbox, decisions, and search/Product/Search & Command K](../../ui/storybook/stories/inbox-search/search.stories.tsx) | `inbox-search/search` | unreviewed / prototype / fixtures | 1 / 0 | Local search-page composition mounts production result components; does not establish parity with the production Search route. | -| [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions](../../ui/storybook/stories/inbox-search/what-needs-me.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 13 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | +| [Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab](../../ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx) | `inbox-search/triage`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Inbox, decisions, and search/Decisions/DecisionCard](../../ui/storybook/stories/inbox-search/decision-card.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions Desk](../../ui/storybook/stories/inbox-search/decisions-desk.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Inbox, decisions, and search/Product/Search & Command K](../../ui/storybook/stories/inbox-search/search.stories.tsx) | `inbox-search/search` | unreviewed / prototype / fixtures | 1 / 0 | Local search-page composition mounts production result components; does not establish parity with the production Search route. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions](../../ui/storybook/stories/inbox-search/what-needs-me.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 13 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | ### Agent conversations and project handoff @@ -141,7 +141,7 @@ Question/plan responses, formal approvals, and app-tool review. | [Tasks and conversations/Questions and approvals/Chat & Comments/Issue Thread Interactions](../../ui/storybook/stories/questions-and-approvals/issue-thread-interactions.stories.tsx) | `questions-and-approvals/questions`, `questions-and-approvals/confirmations`, `questions-and-approvals/audience`, `questions-and-approvals/plan-review`, `questions-and-approvals/continuation` | unreviewed / production / fixtures | 53 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Plan approval Pending, Confirmed, ExpiredByTargetChange and ResumeFailed states use shipped interaction cards. The continuation association covers accepted-but-resume-failed presentation only; no successful resumption, retry or duplicate-suppression proof. | | [Tasks and conversations/Questions and approvals/Chat & Comments/Rich ACP interactions](../../ui/storybook/stories/questions-and-approvals/rich-acp-interactions.stories.tsx) | `questions-and-approvals/questions`, `questions-and-approvals/confirmations`, `questions-and-approvals/audience` | unreviewed / production / fixtures | 12 / 8 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Tasks and conversations/Questions and approvals/Composer/Interaction above composer](../../ui/storybook/stories/questions-and-approvals/task-chat-interaction-above-composer.stories.tsx) | `questions-and-approvals/questions`, `questions-and-approvals/confirmations`, `questions-and-approvals/audience` | current / production / fixtures | 15 / 5 | Corrected local dismissal/send harness: questions leave no pending badge; confirmations retain reopen. Production cards/composer; persisted dismissal is covered separately by unanswered-question fixtures. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. | -| [Tasks and conversations/Questions and approvals/Chat & Comments/Task Chat Unanswered Questions](../../ui/storybook/stories/questions-and-approvals/task-chat-unanswered-questions.stories.tsx) | `questions-and-approvals/dismiss-and-reopen`, `tasks/discussion` | unreviewed / production / fixtures | 6 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | +| [Tasks and conversations/Questions and approvals/Chat & Comments/Task Chat Unanswered Questions](../../ui/storybook/stories/questions-and-approvals/task-chat-unanswered-questions.stories.tsx) | `questions-and-approvals/dismiss-and-reopen`, `tasks/discussion` | unreviewed / production / fixtures | 6 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | ### Steering and queued messages @@ -206,14 +206,14 @@ Agent identity, instructions, reporting lines, lifecycle, and built-ins. | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/External agent invitation/Components](../../ui/storybook/stories/agents/external-agent-invite/components.stories.tsx) | `agents/hire`, `access/invitations` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/External agent invitation/Journeys](../../ui/storybook/stories/agents/external-agent-invite/journeys.stories.tsx) | `agents/hire`, `access/invitations` | unreviewed / production / fixtures | 20 / 4 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/New agent](../../ui/storybook/stories/agents/new-agent-onboarding.stories.tsx) | `agents/hire` | historical / prototype / fixtures | 37 / 1 | Retained local wizard predates production ui/src/components/new-agent flow; shared fields do not make the wizard current. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/00 Review guide](../../ui/storybook/stories/agents/primary-agent/00-review-guide.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/01 Components](../../ui/storybook/stories/agents/primary-agent/01-components.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/02 Sidebar](../../ui/storybook/stories/agents/primary-agent/02-sidebar.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 12 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/03 Agents](../../ui/storybook/stories/agents/primary-agent/03-agents.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/04 New task](../../ui/storybook/stories/agents/primary-agent/04-new-task.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/05 Chat](../../ui/storybook/stories/agents/primary-agent/05-chat.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/06 Journeys](../../ui/storybook/stories/agents/primary-agent/06-journeys.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 11 / 11 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/07 Confirmation](../../ui/storybook/stories/agents/primary-agent/07-confirmation.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 4 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/00 Review guide](../../ui/storybook/stories/agents/primary-agent/00-review-guide.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/01 Components](../../ui/storybook/stories/agents/primary-agent/01-components.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/02 Sidebar](../../ui/storybook/stories/agents/primary-agent/02-sidebar.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 12 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/03 Agents](../../ui/storybook/stories/agents/primary-agent/03-agents.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/04 New task](../../ui/storybook/stories/agents/primary-agent/04-new-task.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/05 Chat](../../ui/storybook/stories/agents/primary-agent/05-chat.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/06 Journeys](../../ui/storybook/stories/agents/primary-agent/06-journeys.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 11 / 11 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/07 Confirmation](../../ui/storybook/stories/agents/primary-agent/07-confirmation.stories.tsx) | `agents/primary-agent`, `tasks/create`, `agent-chat/discovery` | unreviewed / production / fixtures | 4 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Summaries/SummarySlotCard](../../ui/storybook/stories/agents/summary-slot-card.stories.tsx) | `agents/built-in` | unreviewed / production / fixtures | 15 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | ### Runs, harnesses, and model accounts @@ -303,9 +303,9 @@ Provisioning, task bindings, services/logs, Git/files, and closure. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Task activity](../../ui/storybook/stories/workspaces/browser-use-activity.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Footer](../../ui/storybook/stories/workspaces/browser-use-footer.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 21 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Panel](../../ui/storybook/stories/workspaces/browser-use.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 28 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | +| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Task activity](../../ui/storybook/stories/workspaces/browser-use-activity.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Footer](../../ui/storybook/stories/workspaces/browser-use-footer.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 21 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Panel](../../ui/storybook/stories/workspaces/browser-use.stories.tsx) | `workspaces/services`, `tasks/discussion` | unreviewed / production / fixtures | 28 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Projects and execution/Workspaces, services, and files/Components/SearchableSelect/Workspace picker](../../ui/storybook/stories/workspaces/searchable-select.stories.tsx) | `workspaces/binding` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. | | [Projects and execution/Workspaces, services, and files/Issue/Workspace File Browser](../../ui/storybook/stories/workspaces/workspace-file-browser.stories.tsx) | `workspaces/files-git` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. | | [Projects and execution/Workspaces, services, and files/Workspaces/Runtime provisioning status](../../ui/storybook/stories/workspaces/workspace-runtime-provisioning.stories.tsx) | `workspaces/provisioning` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed ExecutionWorkspaceDetail null-project handling. This suite mounts only the unchanged RuntimeProvisionStatusValue inside a local context row; it does not exercise the full detail page or projectless queries. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. | @@ -604,6 +604,7 @@ References are navigation links, not proof of parity. An absent link means no ca - `inbox-search/triage`: [Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab](../../ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx) (unreviewed) - `inbox-search/decisions`: [Tasks and conversations/Inbox, decisions, and search/Decisions/DecisionCard](../../ui/storybook/stories/inbox-search/decision-card.stories.tsx) (unreviewed), [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions Desk](../../ui/storybook/stories/inbox-search/decisions-desk.stories.tsx) (unreviewed), [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions](../../ui/storybook/stories/inbox-search/what-needs-me.stories.tsx) (unreviewed) - `inbox-search/search`: [Tasks and conversations/Inbox, decisions, and search/Product/Search & Command K](../../ui/storybook/stories/inbox-search/search.stories.tsx) (unreviewed) +- `inbox-search/toasts`: No cataloged story. - `agent-chat/discovery`: [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Pages](../../ui/storybook/stories/agent-chat/agent-chat-sidebar.stories.tsx) (unreviewed), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat](../../ui/storybook/stories/agent-chat/agent-chat.stories.tsx) (mixed), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Components](../../ui/storybook/stories/agent-chat/agent-conversation-sidebar.stories.tsx) (current), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Chat entry](../../ui/storybook/stories/agent-chat/chat-entry.stories.tsx) (historical), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/00 Review guide](../../ui/storybook/stories/agents/primary-agent/00-review-guide.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/01 Components](../../ui/storybook/stories/agents/primary-agent/01-components.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/02 Sidebar](../../ui/storybook/stories/agents/primary-agent/02-sidebar.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/03 Agents](../../ui/storybook/stories/agents/primary-agent/03-agents.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/04 New task](../../ui/storybook/stories/agents/primary-agent/04-new-task.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/05 Chat](../../ui/storybook/stories/agents/primary-agent/05-chat.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/06 Journeys](../../ui/storybook/stories/agents/primary-agent/06-journeys.stories.tsx) (unreviewed), [Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/07 Confirmation](../../ui/storybook/stories/agents/primary-agent/07-confirmation.stories.tsx) (unreviewed) - `agent-chat/history`: [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Pages](../../ui/storybook/stories/agent-chat/agent-chat-sidebar.stories.tsx) (unreviewed), [Tasks and conversations/Questions and approvals/Chat & Comments/Agent Chat Unanswered Questions](../../ui/storybook/stories/questions-and-approvals/agent-chat-unanswered-questions.stories.tsx) (unreviewed), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat](../../ui/storybook/stories/agent-chat/agent-chat.stories.tsx) (mixed), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Components](../../ui/storybook/stories/agent-chat/agent-conversation-sidebar.stories.tsx) (current), [Tasks and conversations/Agent conversations and project handoff/Design explorations/Chat entry](../../ui/storybook/stories/agent-chat/chat-entry.stories.tsx) (historical) - `agent-chat/handoff`: [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat](../../ui/storybook/stories/agent-chat/agent-chat.stories.tsx) (mixed) diff --git a/maps/storybook-map/catalog.json b/maps/storybook-map/catalog.json index 5e947c7b55c..1c81c9a3adf 100644 --- a/maps/storybook-map/catalog.json +++ b/maps/storybook-map/catalog.json @@ -10,7 +10,7 @@ "ui/storybook/generated/feature-index.json", "ui/storybook/support/feature-map/FeatureOverview.tsx" ], - "fingerprint": "451bee6238c89f00e77fa9fdc5f86c621afffdf9809c4a9ac9cbf1f3f4f60b78" + "fingerprint": "6bdb0d9cc1f9abb1d9b1c1d7b4652ee4f4cf5c9c5d939069dfabef70ed731df3" }, "entries": [ { @@ -1185,7 +1185,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -1196,7 +1196,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx" ], - "fingerprint": "c605c2eac926e4d0765a6289c22ebba5901de3d536b347f20a8cd293fc9142bc", + "fingerprint": "cdb63642517b31c68df9ca8d62e60039f9f4d69d95d1a96dc96235fb7db1487d", "stableId": "paperclip-assigned-backlog-safeguards", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards" }, @@ -1210,8 +1210,8 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", - "reviewedAt": "2026-10-12", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", "maps/feature-map/recovery.md", @@ -1220,7 +1220,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx" ], - "fingerprint": "b6f5f4b7c00e48da7320bef20e5904527f83665e5fdac4fbb44c866e15547502", + "fingerprint": "bcb6c0a3cb34fcd06e7b056521ad84fb968f73ba46a7ba00eda2a86bb060b11b", "stableId": "product-inbox-blocked-tab", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab" }, @@ -1234,7 +1234,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -1245,7 +1245,7 @@ "ui/storybook/fixtures/browser-use.tsx", "ui/storybook/stories/workspaces/browser-use-activity.stories.tsx" ], - "fingerprint": "80aa360434184867ea46f1e508d3e97c29a6958f991c40c1866e10e5c16af1b1", + "fingerprint": "f0e77b99e99a69532f45d58546a2324893543911771e850a47ca3452818bcf3d", "stableId": "tasks-browser-use-task-activity", "sidebarTitle": "Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Task activity" }, @@ -1259,7 +1259,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -1269,7 +1269,7 @@ "ui/storybook/fixtures/browser-use.tsx", "ui/storybook/stories/workspaces/browser-use-footer.stories.tsx" ], - "fingerprint": "ba83c5b16100c735177b19029c587dcd79de2ab537bcaee130326058963bb2dd", + "fingerprint": "b60b13c6ff934b038616eee7da6aeeaac0f595aeb7c8d80d2a2f787c7bb0dbac", "stableId": "tasks-browser-use-footer", "sidebarTitle": "Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Footer" }, @@ -1305,7 +1305,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the expanded workspace recipe; these examples do not establish durable task-folder continuity, repository authorization or sandbox admission. The workspace recipe now requires child boundary, safe reopen and checkpoint ancestry controls; these backend checks are not established by this fixture. The provisioning recipe requires controller script denial for remote selections and copy-only local source choices, including retained denial through metadata changes and reopening; these fixtures do not verify execution placement. Canonical default provisioning follows configured policy, primary, creation time and ID; these fixtures do not run source hooks or establish this backend precedence. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -1316,7 +1316,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/workspaces/browser-use.stories.tsx" ], - "fingerprint": "42889268f5e8c23c9ac252a8eddb6b839f05a47594484122a41941c6da7636d0", + "fingerprint": "7dea88462587a256933271c48cfef7ff339f55acd1f77a38bf89ad537c63f3ae", "stableId": "tasks-browser-use-panel", "sidebarTitle": "Projects and execution/Workspaces, services, and files/Tasks/Browser Use/Panel" }, @@ -1387,7 +1387,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1401,7 +1401,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/chat-comments.stories.tsx" ], - "fingerprint": "9375c8226472f5e94b44c7e5dbe6046d1a01f70e17ead05811d08e6636639d17", + "fingerprint": "af6b855e4223073858c00f3d39b294222d32a7eb4f9c780cb3a1259a4c3edbdb", "stableId": "product-chat-comments", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments" }, @@ -1519,7 +1519,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1529,7 +1529,7 @@ "ui/storybook/prototypes/completed-activity/CompletedActivityPreview.tsx", "ui/storybook/stories/tasks/completed-activity.stories.tsx" ], - "fingerprint": "5a7cb4c48683bde333499fce04c3a0b983f444596ed68ab771b15fc910f05f25", + "fingerprint": "d6a288300082825b36c483a4c1e1dda3ca7ecf4ba7bada065d88ad8f2098c869", "stableId": "tasks-completed-activity-preview", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview" }, @@ -1542,7 +1542,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -1552,7 +1552,7 @@ "ui/storybook/prototypes/composer-model-picker/fixtures.ts", "ui/storybook/stories/tasks/composer-add-menu.stories.tsx" ], - "fingerprint": "e7a0a6c477048bb9410db5fa29552b6045a4a02520c4437ef0062141a677a134", + "fingerprint": "a3d8d5ddf558df4e5118c747bb1c42ef1d43b23202200614a96c7a4c5ab47e91", "stableId": "composer-add-menu", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/Add menu" }, @@ -1566,7 +1566,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1579,7 +1579,7 @@ "ui/storybook/prototypes/composer-model-picker/fixtures.ts", "ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx" ], - "fingerprint": "5d8ab0bf43155faaf7a3791409cff9d2afc0a382feaac92c50916c9b9c8e9201", + "fingerprint": "62cb4bb214ab3848b9008c2456293027e86bb5bc55bda9c8671a5ea972555ee8", "stableId": "composer-mobile-assignee-picker", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker" }, @@ -1593,7 +1593,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1607,7 +1607,7 @@ "ui/storybook/prototypes/composer-model-picker/fixtures.ts", "ui/storybook/stories/tasks/composer-model-picker.stories.tsx" ], - "fingerprint": "28f590da6e27168049fb39edec3b0d3a8344d5afc6921d622f22a3a02677eccd", + "fingerprint": "6fdfb5463f7601772919b8c73e97b2571f5c13bf728390ecffc22a7438d67ce7", "stableId": "composer-model-and-effort-picker", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker" }, @@ -1775,7 +1775,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/activity.md", @@ -1792,7 +1792,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/control-plane-surfaces.stories.tsx" ], - "fingerprint": "76878f4f9b53c94eac1e74fbb50b943b1f5359c3bddd119cc277bc8073bfe52a", + "fingerprint": "ad5842a0179d5b824f4b08170e82ce66fe3929eb93021af3606e7c9dd81ddc12", "stableId": "product-control-plane-surfaces", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Control Plane Surfaces" }, @@ -1928,14 +1928,14 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", - "reviewedAt": "2026-10-12", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", "ui/src/components/DecisionCard.tsx", "ui/storybook/stories/inbox-search/decision-card.stories.tsx" ], - "fingerprint": "220a4f037c1dd4a456ac7df75d6ee7fd0d844ff7db3fd30ca1da2fff9477cbc8", + "fingerprint": "172482180fa305e06238da1fa90a277dd1eb109354d8bfeb0b2b833b9aba798d", "stableId": "decisions-decisioncard", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Decisions/DecisionCard" }, @@ -2022,8 +2022,8 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", - "reviewedAt": "2026-10-12", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", "ui/src/components/AttentionQueueRow.tsx", @@ -2031,7 +2031,7 @@ "ui/src/pages/WhatNeedsMe.tsx", "ui/storybook/stories/inbox-search/decisions-desk.stories.tsx" ], - "fingerprint": "1e49e931cddd7ef77de70f25fc4ee991d3de9d933670ae4412c5d17fad6db0aa", + "fingerprint": "4b39ccddf9d5ed2847fe0bd9f0078b183ffcaeb41b67121410909ef10a73e938", "stableId": "pages-decisions-desk", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Pages/Decisions Desk" }, @@ -2067,7 +2067,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agents.md", @@ -2085,7 +2085,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/dialogs-modals.stories.tsx" ], - "fingerprint": "75e4b0effdb3cd0c426dce00f908ca4657a03e2ffc365558d074858020ac5f23", + "fingerprint": "0106caca8a9ad499602499ab64ad96f7cbb9db6815195218775cc317ba7204ad", "stableId": "product-dialogs-modals", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Dialogs & Modals" }, @@ -2303,7 +2303,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -2318,7 +2318,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/external-objects.stories.tsx" ], - "fingerprint": "5484a9480224ce2e2fdc7376e582d26e6d049e6308a1705e45615594cc7db56a", + "fingerprint": "1a7a6fc735b582b7f0968b97d0159528834a499f6d090c0ec678a1092dc6e3ac", "stableId": "foundations-external-objects", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/External Objects" }, @@ -2883,7 +2883,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Focused creation-visibility stories mount the production collection with simulated hierarchy reads and assert hidden roots, opt-in inclusion, and the unchecked filter control. Creation visibility examples and fixture setup now live in a support module so the surface matrix stays below 1000 lines; exported story IDs are unchanged. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -2902,9 +2902,10 @@ "ui/src/components/PriorityIcon.tsx", "ui/src/components/StatusBadge.tsx", "ui/storybook/fixtures/paperclipData.ts", - "ui/storybook/stories/tasks/issue-management.stories.tsx" + "ui/storybook/stories/tasks/issue-management.stories.tsx", + "ui/storybook/support/issue-creation-visibility.tsx" ], - "fingerprint": "1c8ff1f940a366fabec4f2cb3f5bbf84e8d21be64bb301da65dcc39ca24c9faa", + "fingerprint": "554a97d096cb27b9418fa2dc5a3bc0cb853e8bafa128ef9abd7cf1c3df3a6bd5", "stableId": "product-issue-management", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Issue Management" }, @@ -3050,7 +3051,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -3058,7 +3059,7 @@ "ui/src/components/SearchableSelect.tsx", "ui/storybook/stories/tasks/mobile-entity-pickers.stories.tsx" ], - "fingerprint": "3707254c9f397fa663d1d51b0af5dbf1a811f34d9d4faf2dea5041116b92f1c8", + "fingerprint": "249524a570189f2b01ef83136346a0d1ee099e233e357aa92ffaecd52a376b32", "stableId": "components-entity-pickers-mobile", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Components/Entity pickers/Mobile" }, @@ -3226,7 +3227,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. WorktreePicker, ReuseWorktree, CreateWithReusedWorktree and NewWorktreeAfterReuse exercise workspace selection and submitted fixture request values. Actual admission/binding and execution remain outside this story.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. WorktreePicker, ReuseWorktree, CreateWithReusedWorktree and NewWorktreeAfterReuse exercise workspace selection and submitted fixture request values. Actual admission/binding and execution remain outside this story. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -3236,7 +3237,7 @@ "ui/storybook/stories/tasks/new-task-composer.stories.tsx", "maps/feature-map/workspaces.md" ], - "fingerprint": "56ef4e67f8e6b51ab54a664336fae33a75aece2f56883861ce617d53145b5637", + "fingerprint": "db3b18dcf012cfd2174900456dd8f8189b2f0b4287d84fbb62562bb0325a0001", "stableId": "composer-new-task", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/New task" }, @@ -3344,7 +3345,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/projects.md", @@ -3357,7 +3358,7 @@ "ui/storybook/prototypes/originating-tasks/OriginatingTasks.tsx", "ui/storybook/stories/task-coordination/originating-tasks.stories.tsx" ], - "fingerprint": "2c1e0da025ec60281b4c8179180114d7eee259cf30a1d7216a21bbf476eb7228", + "fingerprint": "59123e73a0286fd11a48376ee152a974d730c59bd5421fd273cfca3a185f6e08", "stableId": "ux-labs-tasks-created-from-a-task", "sidebarTitle": "Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task" }, @@ -3378,7 +3379,7 @@ "ui/storybook/generated/feature-index.json" ], "exception": "Shared foundations or contributor guide; no single business capability owns this suite.", - "fingerprint": "b4a1cb9cf763e78cfc632a5adf859c8827318de12eeb0cd359b4a5b8b838a730", + "fingerprint": "408bb736ebe6591fb5a397db6d8b3d3d0b37741a66ed2a7edc84781c67d5c6c7", "stableId": "overview-storybook-guide", "sidebarTitle": "Overview/Storybook Guide" }, @@ -3460,7 +3461,7 @@ "status": "unreviewed", "implementation": "guide", "boundary": "static", - "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3468,7 +3469,7 @@ "maps/feature-map/tasks.md", "ui/storybook/stories/agents/primary-agent/00-review-guide.stories.tsx" ], - "fingerprint": "d54242a3d8d8096014dd54fe49ca0e86669894ff859d41ff0e50866315a412f4", + "fingerprint": "dc2d1c8a114ba4e6c065170fe2cd6b39c8b6b7dce3d7ae1afbfbddc15adf5beb", "stableId": "primary-agent-00-review-guide", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/00 Review guide" }, @@ -3483,7 +3484,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3503,7 +3504,7 @@ "ui/storybook/stories/agents/primary-agent/01-components.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "b1ae1d45a7e4a89f94a4a10343ddabd085cc758f0ceb17df43806722df874019", + "fingerprint": "1ba218400f92d323d120ad223d92a0ddeee6d104ee36f1873e110a3e0124a1bb", "stableId": "primary-agent-01-components", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/01 Components" }, @@ -3518,7 +3519,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3537,7 +3538,7 @@ "ui/storybook/stories/agents/primary-agent/02-sidebar.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "a98ea7e6c56135bd030b43385bfdb2a5e12f5e78d18ed301c88ff12da07481a3", + "fingerprint": "cf72eaff02b96c8f81a285c96246ef80e9d97e836b47f8bb88734175d5d01664", "stableId": "primary-agent-02-sidebar", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/02 Sidebar" }, @@ -3552,7 +3553,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3571,7 +3572,7 @@ "ui/storybook/stories/agents/primary-agent/03-agents.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "2096810ef9ccf52eaf2811175604550302b314fc2e3d6bb951413a5505c3214b", + "fingerprint": "d8107859209157fdcd775b1e0161aa9b39fd79280b57d2bcd29e76afa1944c2a", "stableId": "primary-agent-03-agents", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/03 Agents" }, @@ -3586,7 +3587,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3605,7 +3606,7 @@ "ui/storybook/stories/agents/primary-agent/04-new-task.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "2ab88c703c7376edd519dc413a562ab802998ae25cafc44925fdd00f48ceb7cb", + "fingerprint": "2e30847e4f8d957c11600f3663358e0eb555f2185cb85c4c1c1b4d57eba62f51", "stableId": "primary-agent-04-new-task", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/04 New task" }, @@ -3620,7 +3621,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3639,7 +3640,7 @@ "ui/storybook/stories/agents/primary-agent/05-chat.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "2b22fe59288fb0d6597f7122ac195038f3ed8d748c2ed80fba7e795576501cd3", + "fingerprint": "fa3248cdb686fb0827390fbabea3c2ed38950e5234141254a53756cd271b4903", "stableId": "primary-agent-05-chat", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/05 Chat" }, @@ -3654,7 +3655,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3673,7 +3674,7 @@ "ui/storybook/stories/agents/primary-agent/06-journeys.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "67c35f4ec63e84d1ccaa4362e2e2289857ecfa583d412c8582168d169ca38a54", + "fingerprint": "68a196913fd72374066dcccf55430d742881a983be2d914de2e02fd98a825fd0", "stableId": "primary-agent-06-journeys", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/06 Journeys" }, @@ -3688,7 +3689,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Primary choice, replacement confirmation, unavailable states and task/chat fallback presentation use local presentation-provider callbacks. Production preference persistence and default-resolution precedence remain unverified by this suite. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -3707,7 +3708,7 @@ "ui/storybook/stories/agents/primary-agent/07-confirmation.stories.tsx", "ui/storybook/support/primary-agent/PrimaryAgentStory.tsx" ], - "fingerprint": "6be424a8d962be786c09ef1d2c3b505e407c7b645367adabfc2f737d73c692c6", + "fingerprint": "0d65a5877c6ef741b40ddd938c8066608307879ed029baafe59e7248e482558b", "stableId": "primary-agent-07-confirmation", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Primary Agent/07 Confirmation" }, @@ -3753,14 +3754,14 @@ "status": "unreviewed", "implementation": "guide", "boundary": "static", - "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", "maps/feature-map/tasks.md", "ui/storybook/stories/access/private-tasks/00-review-guide.stories.tsx" ], - "fingerprint": "ac5d14d14aa3eec4795ff687b25d1c4025abaa29bb890d45086f1e088b62232d", + "fingerprint": "dc46ad819f4c0d925acbd1170d8a9ee4018f2b49de0241a3b3d698f154ee9257", "stableId": "private-tasks-00-review-guide", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/00 Review guide" }, @@ -3774,7 +3775,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3792,7 +3793,7 @@ "ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "8010910a61b5f20de41ead806ef1da41c09903b61d47db038fe31c2142960a1e", + "fingerprint": "d4b5ba6bce33a9657d859d3278b1f84bbc9b77cffd24e370214a0fce24c0a02b", "stableId": "private-tasks-01-creation", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/01 Creation" }, @@ -3806,7 +3807,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3818,7 +3819,7 @@ "ui/storybook/stories/access/private-tasks/02-sharing.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "29ec80e59e9275be25933e1b87b9a49196af7bd946fb0bd9b21edea52ec58663", + "fingerprint": "6595d72c6af25ff292b389d88e8d2019020f4e4a6965cceb2a50b793ec03b882", "stableId": "private-tasks-02-sharing", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/02 Sharing" }, @@ -3832,7 +3833,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3844,7 +3845,7 @@ "ui/storybook/stories/access/private-tasks/03-task-actions.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "249e1e9b5b9f4ff6625734ab176fca3fdfe237238303f843403a506e529517a6", + "fingerprint": "be8143e492deda70c9139b7e9f7022ae6b3c3b7603791074c22adb72a54adb79", "stableId": "private-tasks-03-task-actions", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/03 Task actions" }, @@ -3858,7 +3859,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3875,7 +3876,7 @@ "ui/storybook/stories/access/private-tasks/04-references-and-blockers.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "cb879fe3240a4d1e17e3198c07091d53af898f23c176bba2fc381e4b9bb41dea", + "fingerprint": "371547c4293f4f7e6e922113a9a6a2631bfe73979343fe70fc158b2e1fd3c5a4", "stableId": "private-tasks-04-references-and-blockers", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/04 References and blockers" }, @@ -3890,7 +3891,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3905,7 +3906,7 @@ "ui/storybook/stories/access/private-tasks/05-projects.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "5bbc18f1689862e62cf534729dda878cc5ca9e9d8e19f23286fb19536206d77b", + "fingerprint": "8b773d099d8ae633ab72108c0a4dd8b2c032aac03787ab47cda05426f456fb15", "stableId": "private-tasks-05-projects", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/05 Projects" }, @@ -3920,7 +3921,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3938,7 +3939,7 @@ "ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "2972bb8615897132cd38835f2a744a5348a53ab746db4749c9ea91877e9e2485", + "fingerprint": "5fb218d88c3b6815839732e4226a27f4b40a2df46c8cbad6709cb665546b4248", "stableId": "private-tasks-06-full-product-pages", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages" }, @@ -3952,7 +3953,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3985,7 +3986,7 @@ "ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "024bd786a1800aed8756e66b876637280c00a51ce3372363aa74e79893a2913d", + "fingerprint": "5fbe5c61f8754bc0c1db1c6a990b9c44bc5d941b7b61de77fac76300e6c42a22", "stableId": "private-tasks-07-user-stories", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/07 User stories" }, @@ -4667,7 +4668,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4682,7 +4683,7 @@ "ui/storybook/prototypes/runner-activity/RunnerActivityPreview.tsx", "ui/storybook/stories/tasks/runner-activity.stories.tsx" ], - "fingerprint": "2f7782ae1961a4a8f02c7955edf2a44add21eb2fe241d717cd988ff48d0ca485", + "fingerprint": "99a90f98646e1e307ec6255c7f7bd6ce72958ba83f9968f6e0ae3863949b697a", "stableId": "tasks-runner-activity-preview", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview" }, @@ -4717,8 +4718,8 @@ "status": "unreviewed", "implementation": "prototype", "boundary": "fixtures", - "notes": "Local search-page composition mounts production result components; does not establish parity with the production Search route.", - "reviewedAt": "2026-10-12", + "notes": "Local search-page composition mounts production result components; does not establish parity with the production Search route. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", "ui/src/components/PageTabBar.tsx", @@ -4731,7 +4732,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/inbox-search/search.stories.tsx" ], - "fingerprint": "4ebfe41c086ce2e0ae674263dfa50239a0770e5e6f98c3c24f54955f6cbdd3d2", + "fingerprint": "a6661b568c8bc74f7667aeff4a5da84a05f507dd62679ee9039808e825eeef61", "stableId": "product-search-command-k", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Product/Search & Command K" }, @@ -5087,15 +5088,15 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", - "reviewedAt": "2026-10-12", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/task-coordination.md", "ui/src/components/IssuesList.tsx", "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/task-coordination/sub-issues-workflow.stories.tsx" ], - "fingerprint": "f9ebab21b19cd4aed698c7f1b060c8c6e39788f8224b567d86bf0b4841d038b2", + "fingerprint": "4461d14e90da3e93b82826efdddf131ae014c61a040f33cfae1df1368c6ddb2d", "stableId": "ux-labs-sub-issues-workflow-checklist", "sidebarTitle": "Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Sub-issues Workflow Checklist" }, @@ -5109,7 +5110,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5121,7 +5122,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/successful-run-handoff.stories.tsx" ], - "fingerprint": "8cf0d6e52decc045151c65575984520ac882499baddbeb86ca10b42385863398", + "fingerprint": "2a7db108d46f35f37dd9346d2fc039e61dff8c688e09d9ed7b0e777e7f79af0d", "stableId": "paperclip-successful-run-handoff", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff" }, @@ -5180,7 +5181,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -5192,7 +5193,7 @@ "ui/src/components/task-chat/task-chat-model.ts", "ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx" ], - "fingerprint": "cd4351774fa50e58fda2f249f2870b7aa376b1eb23a25a4e69280501ef0021d7", + "fingerprint": "4747486949d9bca41e7bed36d511eb523651bc1e23ac0427892ff9b47e29cd28", "stableId": "tasks-task-chat-review-fixes", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes" }, @@ -5234,7 +5235,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5245,7 +5246,7 @@ "ui/src/components/task-chat/useWindowAutoFollow.ts", "ui/storybook/stories/tasks/task-chat-mobile-follow.stories.tsx" ], - "fingerprint": "988ca771ff638613760d91ef8151689f3cb4405135906472541915ebff39d165", + "fingerprint": "840c21389fca0bf13a1df0d247a9d63836d8f38f4ca95c04838ca0cf36b881d9", "stableId": "task-chat-mobile-streaming-follow", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Task chat/Mobile streaming follow" }, @@ -5258,7 +5259,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5266,7 +5267,7 @@ "ui/src/components/task-chat/task-chat-model.ts", "ui/storybook/stories/tasks/task-chat-system-notice.stories.tsx" ], - "fingerprint": "07910730b7ef3be833745377575d4abc41a61dd862428b6711031f1ca2f50eeb", + "fingerprint": "1dd5a86762602b618007a3ed83d0500074560bb9168e7f1873aab0aad555deef", "stableId": "product-task-chat-system-notice", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Task chat/System notice" }, @@ -5280,7 +5281,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/questions-and-approvals.md", @@ -5290,7 +5291,7 @@ "ui/storybook/fixtures/unansweredQuestionChat.tsx", "ui/storybook/stories/questions-and-approvals/task-chat-unanswered-questions.stories.tsx" ], - "fingerprint": "4fbe4667a042a8fcd8f96d7041d207bc115c7d79a8988196bc6bf18ecda456da", + "fingerprint": "2eb8af0c7b2ecf6914c9bf5d1351427309a5a3ce31b90124c82c23dc2b50bf54", "stableId": "chat-comments-task-chat-unanswered-questions", "sidebarTitle": "Tasks and conversations/Questions and approvals/Chat & Comments/Task Chat Unanswered Questions" }, @@ -5303,7 +5304,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5312,7 +5313,7 @@ "ui/src/components/chat/ExternallyConnectedTaskBanner.tsx", "ui/storybook/stories/tasks/task-context-notices.stories.tsx" ], - "fingerprint": "737d40d84d6a42dda3366e41f9d6a829219e424c6e770d7e6bb59f2b530b2bfe", + "fingerprint": "10f395d8ecba84925abf8d843deb52af64356008bb96a03668f29870ac430bc8", "stableId": "tasks-context-notices", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Context notices" }, @@ -5414,7 +5415,7 @@ "status": "unreviewed", "implementation": "guide", "boundary": "static", - "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5422,7 +5423,7 @@ "ui/storybook/stories/tasks/thread-human-identity/00-evidence.stories.tsx", "ui/storybook/support/thread-human-identity/fixtures.ts" ], - "fingerprint": "4e6ed831f9d6049b77a6bfa29da2bf60623f33d70a591f64a4f5c15b3fc38e11", + "fingerprint": "cb8b7d0dcdbd27bcaa16d2fcbc86e776b32db875bc6b4de3d752caa5aa9119da", "stableId": "chat-comments-human-identity-00-review-guide", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/00 Review guide" }, @@ -5435,7 +5436,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5446,7 +5447,7 @@ "ui/storybook/stories/tasks/thread-human-identity/01-components.stories.tsx", "ui/storybook/support/thread-human-identity/fixtures.ts" ], - "fingerprint": "ba528b7605db60c2b739862d5f80cbdc1b4379c77587af41dc040fd1015d30f8", + "fingerprint": "d87a48d3be494149116ab8fd366e90c17aa2e23805fb1f0d896464e10ba35b04", "stableId": "chat-comments-human-identity-01-components", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/01 Components" }, @@ -5459,7 +5460,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5469,7 +5470,7 @@ "ui/storybook/stories/tasks/thread-human-identity/02-task-journey.stories.tsx", "ui/storybook/support/thread-human-identity/fixtures.ts" ], - "fingerprint": "462c55f151c3b297851e351615f0f37afb87b93dd32caa6dfbb128289354f130", + "fingerprint": "68b3cca3a5ed9b116b34bd1dde6ca8012c350c2a7c2b60cc928d7e5b30da8694", "stableId": "chat-comments-human-identity-02-mixed-author-journey", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/02 Mixed-author journey" }, @@ -5528,8 +5529,8 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", - "reviewedAt": "2026-10-12", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", "ui/src/components/AttentionQueueRow.tsx", @@ -5537,7 +5538,7 @@ "ui/src/components/ToastViewport.tsx", "ui/storybook/stories/inbox-search/what-needs-me.stories.tsx" ], - "fingerprint": "d8b838185489f6b5009e52d9c2ac3a1fea3b2718211a1ddc393c404a0335a466", + "fingerprint": "0920cb3c4e75ba896955144a41f714a18dc778ec63f7b3abfa7e08b1032c0e94", "stableId": "pages-decisions", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Pages/Decisions" }, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 352ee970915..b1655cf3169 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -2879,3 +2879,5 @@ export * from "./validators/task-workspace.js"; export { prepareWorkspaceRepositorySchema, type PrepareWorkspaceRepository } from "./validators/workspace-repository.js"; export * from "./fast-response.js"; + +export { HUMAN_TASK_ORIGIN_KINDS, isAgentCreatedIssue, type IssueCreationSourceFields } from "./issue-creation-source.js"; diff --git a/packages/shared/src/issue-creation-source.test.ts b/packages/shared/src/issue-creation-source.test.ts new file mode 100644 index 00000000000..afd00ce1ad8 --- /dev/null +++ b/packages/shared/src/issue-creation-source.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest"; +import { isAgentCreatedIssue } from "./issue-creation-source.js"; + +describe("task creation source", () => { + it.each(["manual", "onboarding_first_task"])("preserves human-created %s tasks", (originKind) => { + expect(isAgentCreatedIssue({ createdByUserId: "person", originKind })).toBe(false); + }); + it.each(["chat_channel", "routine_execution", "stranded_issue_recovery", "task_watchdog", "harness_liveness_escalation", "plugin:workflow:operation", "built_in_agent_bundle", "pipeline_automation"])("classifies %s as automated even with human attribution", (originKind) => { + expect(isAgentCreatedIssue({ createdByUserId: "person", originKind })).toBe(true); + }); + it.each([null, "person"])("classifies channel work as automated with creator %s", (createdByUserId) => { + expect(isAgentCreatedIssue({ originKind: "chat_channel", createdByUserId })).toBe(true); + }); + it("recognizes agents and creator-less ordinary tasks", () => { + expect(isAgentCreatedIssue({ createdByAgentId: "agent", originKind: "manual" })).toBe(true); + expect(isAgentCreatedIssue({ originKind: "manual" })).toBe(true); + expect(isAgentCreatedIssue({ createdByUserId: "person", createdByAgentId: "agent" })).toBe(true); + expect(isAgentCreatedIssue({ createdByUserId: "person" })).toBe(false); + }); +}); diff --git a/packages/shared/src/issue-creation-source.ts b/packages/shared/src/issue-creation-source.ts new file mode 100644 index 00000000000..48bd43f183b --- /dev/null +++ b/packages/shared/src/issue-creation-source.ts @@ -0,0 +1,17 @@ +/** Origins that represent a person's ordinary task or onboarding request. */ +export const HUMAN_TASK_ORIGIN_KINDS = ["manual", "onboarding_first_task"] as const; + +export interface IssueCreationSourceFields { + createdByAgentId?: string | null; + createdByUserId?: string | null; + originKind?: string | null; +} + +/** Creation is independent of who is assigned or responsible for execution. */ +export function isAgentCreatedIssue(issue: IssueCreationSourceFields): boolean { + return Boolean(issue.createdByAgentId) + || !issue.createdByUserId + || !HUMAN_TASK_ORIGIN_KINDS.includes( + (issue.originKind ?? "manual") as typeof HUMAN_TASK_ORIGIN_KINDS[number], + ); +} diff --git a/server/src/__tests__/issues-creation-visibility.test.ts b/server/src/__tests__/issues-creation-visibility.test.ts new file mode 100644 index 00000000000..7b19bd5dd60 --- /dev/null +++ b/server/src/__tests__/issues-creation-visibility.test.ts @@ -0,0 +1,77 @@ +import { randomUUID } from "node:crypto"; +import request from "supertest"; +import { expect, it } from "vitest"; +import { activityLog, issueComments, issueReadStates, issues } from "@paperclipai/db"; +import { isAgentCreatedIssue } from "@paperclipai/shared"; +import { issueRoutes } from "../routes/issues.js"; +import { issueService } from "../services/issues.js"; +import { describeEmbeddedPostgres, resetCompanyIssueFixtures, routeApp, seedCompanyWithBoardAccess, useEmbeddedPostgres } from "./helpers/route-test-harness.js"; + +describeEmbeddedPostgres("issue creation visibility", () => { + const ctx = useEmbeddedPostgres("paperclip-creation-visibility-", { + resetEach: async (db) => { + await db.delete(activityLog); + await resetCompanyIssueFixtures(db); + }, + }); + + it("filters ordinary and compact searches before pagination without changing API defaults", async () => { + const { companyId, userId, actor } = await seedCompanyWithBoardAccess(ctx.db, "Creation visibility"); + const other = await seedCompanyWithBoardAccess(ctx.db, "Other company"); + const rows = [ + { id: randomUUID(), companyId, title: "Visibility human", createdByUserId: userId, originKind: "manual" }, + { id: randomUUID(), companyId, title: "Visibility second human", createdByUserId: userId, originKind: "manual" }, + { id: randomUUID(), companyId, title: "Visibility channel", createdByUserId: userId, originKind: "chat_channel" }, + { id: randomUUID(), companyId, title: "Visibility onboarding", createdByUserId: userId, originKind: "onboarding_first_task" }, + { id: randomUUID(), companyId, title: "Visibility inbound email", originKind: "chat_channel", originId: "email:endpoint:thread" }, + { id: randomUUID(), companyId, title: "Visibility outbound email", originKind: "chat_channel", originId: "email-send:tool-call" }, + ...["routine_execution", "task_watchdog", "stranded_issue_recovery", "plugin:workflow:task", "harness_liveness_escalation", "manual"].map((originKind) => ({ id: randomUUID(), companyId, title: `Visibility ${originKind}`, originKind, responsibleUserId: userId })), + { id: randomUUID(), companyId, title: "Visibility manually run routine", createdByUserId: userId, originKind: "routine_execution" }, + ]; + await ctx.db.insert(issues).values([ + ...rows.map((row, index) => ({ ...row, status: "todo", updatedAt: new Date(2026, 9, 13, 0, index) })), + { companyId: other.companyId, title: "Visibility other company", createdByUserId: other.userId }, + { companyId, title: "Visibility private task", createdByUserId: other.userId, visibility: "private" }, + ]); + const app = routeApp(ctx.db, actor, issueRoutes); + const path = `/api/companies/${companyId}/issues`; + const defaults = await request(app).get(`${path}?includeRoutineExecutions=true`).expect(200); + expect(defaults.body).toHaveLength(rows.length); + for (const view of ["", "&view=compact"]) { + const first = await request(app).get(`${path}?includeAgentCreatedTasks=false&q=Visibility&sortField=updated&limit=2${view}`).expect(200); + const second = await request(app).get(`${path}?includeAgentCreatedTasks=false&q=Visibility&sortField=updated&limit=2&offset=2${view}`).expect(200); + expect(first.body).toHaveLength(2); + expect(second.body).toHaveLength(1); + expect([...first.body, ...second.body].every((issue: Parameters[0]) => !isAgentCreatedIssue(issue))).toBe(true); + } + await request(app).get(`${path}?includeAgentCreatedTasks=invalid`).expect(400); + await request(app).get(`${path}?includeParticipatedTasks=invalid`).expect(400); + await request(app).get(`/api/companies/${other.companyId}/issues?includeAgentCreatedTasks=false`).expect(403); + }); + + it("preserves participation but excludes routine ownership and read-only activity", async () => { + const { companyId, userId, actor } = await seedCompanyWithBoardAccess(ctx.db, "Inbox participation"); + const ids = Object.fromEntries(["scheduled", "commented", "assigned", "manual", "read", "touched", "channelScheduled", "channelCommented", "channelManual"].map((name) => [name, randomUUID()])); + await ctx.db.insert(issues).values(Object.entries(ids).map(([name, id]) => ({ + id, companyId, title: `Participation ${name}`, status: "todo", originKind: name.startsWith("channel") ? "chat_channel" : "routine_execution", + responsibleUserId: userId, + assigneeUserId: name === "assigned" ? userId : null, + createdByUserId: name === "manual" || name === "channelManual" ? userId : null, + }))); + await ctx.db.insert(issueComments).values([ids.commented!, ids.channelCommented!].map((issueId) => ({ companyId, issueId, authorUserId: userId, body: "I joined this task" }))); + await ctx.db.insert(issueReadStates).values({ companyId, issueId: ids.read!, userId, lastReadAt: new Date() }); + await ctx.db.insert(activityLog).values([ + { companyId, entityType: "issue", entityId: ids.read!, actorType: "user", actorId: userId, action: "issue.read_marked" }, + { companyId, entityType: "issue", entityId: ids.touched!, actorType: "user", actorId: userId, action: "issue.inbox_touched" }, + ]); + const app = routeApp(ctx.db, actor, issueRoutes); + const path = `/api/companies/${companyId}/issues`; + const mine = await request(app).get(`${path}?touchedByUserId=me&inboxArchivedByUserId=me&includeRoutineExecutions=true`).expect(200); + const expected = [ids.commented, ids.assigned, ids.manual, ids.touched, ids.channelCommented, ids.channelManual].sort(); + expect(mine.body.map((issue: { id: string }) => issue.id).sort()).toEqual(expected); + const discovery = await request(app).get(`${path}?q=Participation&includeAgentCreatedTasks=false&includeParticipatedTasks=true`).expect(200); + expect(discovery.body.map((issue: { id: string }) => issue.id).sort()).toEqual(expected); + const strict = await issueService(ctx.db).list(companyId, { includeAgentCreatedTasks: false }); + expect(strict).toEqual([]); + }); +}); diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index 66b7d0460b8..7af8d4c1239 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -8312,6 +8312,20 @@ export function issueRoutes( const hasPlanDocument = parseOptionalBooleanQuery( req.query.hasPlanDocument, ); + const includeParticipatedTasks = parseOptionalBooleanQuery(req.query.includeParticipatedTasks); + if (includeParticipatedTasks === null) { + res.status(400).json({ error: "includeParticipatedTasks must be true or false when provided" }); + return; + } + if (includeParticipatedTasks && (req.actor.type !== "board" || !req.actor.userId)) { + res.status(403).json({ error: "includeParticipatedTasks requires board authentication" }); + return; + } + const includeAgentCreatedTasks = parseOptionalBooleanQuery(req.query.includeAgentCreatedTasks); + if (includeAgentCreatedTasks === null) { + res.status(400).json({ error: "includeAgentCreatedTasks must be true or false when provided" }); + return; + } const includeLiveDescendantSummary = parseOptionalBooleanQuery( req.query.includeLiveDescendantSummary, ); @@ -8479,6 +8493,8 @@ export function issueRoutes( includeBlockedInboxAttention: req.query.includeBlockedInboxAttention === "true" || req.query.includeBlockedInboxAttention === "1", + includeAgentCreatedTasks, + creationVisibilityUserId: includeParticipatedTasks === true && req.actor.type === "board" ? req.actor.userId : undefined, includeLiveDescendantSummary: includeLiveDescendantSummary === true, hasPlanDocument, q: req.query.q as string | undefined, diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 7c627f7b49f..aeb63a0c157 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -1,4 +1,5 @@ import { automaticReadRecoveryCleanupCondition, touchedByUserCondition } from "./issue-participation.js"; +import { HUMAN_TASK_ORIGIN_KINDS } from "@paperclipai/shared"; import { resolveProjectDefaultWorkspaceId } from "./project-default-workspace.js"; import { type AuthorizationActor, executionWorkspaceReadSqlCondition, projectReadSqlCondition } from "./authorization.js"; import { assertTaskWorkspaceAccess, assertTaskWorkspaceSourceProjectAccess } from "./task-workspace-source-access.js"; @@ -1834,6 +1835,9 @@ export interface IssueFilters { originKind?: string; originKindPrefix?: string; originId?: string; + includeAgentCreatedTasks?: boolean; + /** Authenticated inbox discovery can retain existing participation. */ + creationVisibilityUserId?: string; includeRoutineExecutions?: boolean; excludeRoutineExecutions?: boolean; includePluginOperations?: boolean; @@ -6197,6 +6201,21 @@ function assertValidAssigneeAgentFilter( } } +function humanCreatedIssueCondition() { + return and( + isNull(issues.createdByAgentId), + isNotNull(issues.createdByUserId), + inArray(issues.originKind, [...HUMAN_TASK_ORIGIN_KINDS]), + )!; +} + +function creationVisibilityCondition(companyId: string, filters?: IssueFilters) { + if (filters?.includeAgentCreatedTasks !== false) return undefined; + return filters.creationVisibilityUserId + ? or(humanCreatedIssueCondition(), touchedByUserCondition(companyId, filters.creationVisibilityUserId))! + : humanCreatedIssueCondition(); +} + async function blockedInboxIssueConditions( dbOrTx: any, companyId: string, @@ -6208,6 +6227,8 @@ async function blockedInboxIssueConditions( notInArray(issues.status, [...BLOCKED_INBOX_TERMINAL_STATUSES]), ]; if (filters?.readCondition) conditions.push(filters.readCondition); + const creationCondition = creationVisibilityCondition(companyId, filters); + if (creationCondition) conditions.push(creationCondition); const touchedByUserId = filters?.touchedByUserId?.trim() || undefined; const inboxArchivedByUserId = filters?.inboxArchivedByUserId?.trim() || undefined; @@ -7971,6 +7992,8 @@ export function issueService(db: Db) { } if (filters?.afterId) conditions.push(gt(issues.id, filters.afterId)); if (filters?.readCondition) conditions.push(filters.readCondition); + const creationCondition = creationVisibilityCondition(companyId, filters); + if (creationCondition) conditions.push(creationCondition); const assigneeAgentFilter = parseIssueAssigneeAgentFilter( filters?.assigneeAgentId, ); @@ -8299,6 +8322,8 @@ export function issueService(db: Db) { const conditions = [eq(issues.companyId, companyId), visibleIssueCondition()]; if (filters?.readCondition) conditions.push(filters.readCondition); + const creationCondition = creationVisibilityCondition(companyId, filters); + if (creationCondition) conditions.push(creationCondition); if (!filters?.q?.trim()) { conditions.push(isNull(issues.conversationAgentId)); if (!filters?.touchedByUserId && !filters?.unreadForUserId && !filters?.inboxArchivedByUserId) { diff --git a/ui/src/api/issues.ts b/ui/src/api/issues.ts index 540a8d1b6db..314064ea27a 100644 --- a/ui/src/api/issues.ts +++ b/ui/src/api/issues.ts @@ -102,6 +102,8 @@ export type IssueListFilters = { originId?: string; descendantOf?: string; createdFromIssueId?: string; + includeAgentCreatedTasks?: boolean; + includeParticipatedTasks?: boolean; includeRoutineExecutions?: boolean; includeBlockedBy?: boolean; includeBlockedInboxAttention?: boolean; @@ -143,6 +145,9 @@ function issueListSearchParams(filters?: IssueListFilters) { if (filters?.originId) params.set("originId", filters.originId); if (filters?.descendantOf) params.set("descendantOf", filters.descendantOf); if (filters?.createdFromIssueId) params.set("createdFromIssueId", filters.createdFromIssueId); + if (filters?.includeParticipatedTasks) params.set("includeParticipatedTasks", "true"); + if (filters?.includeAgentCreatedTasks !== undefined) + params.set("includeAgentCreatedTasks", String(filters.includeAgentCreatedTasks)); if (filters?.includeRoutineExecutions) params.set("includeRoutineExecutions", "true"); if (filters?.includeBlockedBy) params.set("includeBlockedBy", "true"); diff --git a/ui/src/components/BlockedInboxView.tsx b/ui/src/components/BlockedInboxView.tsx index dee2ec3c7a3..0cb145a359f 100644 --- a/ui/src/components/BlockedInboxView.tsx +++ b/ui/src/components/BlockedInboxView.tsx @@ -76,10 +76,13 @@ export function BlockedInboxView({ error, refetch, } = useQuery({ - queryKey: [...queryKeys.issues.listBlockedAttention(companyId), "live-descendant-summary"], + queryKey: [...queryKeys.issues.listBlockedAttention(companyId), "live-descendant-summary", issueFilters.showAgentCreatedTasks], queryFn: () => issuesApi.list(companyId, { attention: "blocked", + includeRoutineExecutions: true, + includeAgentCreatedTasks: issueFilters.showAgentCreatedTasks, + includeParticipatedTasks: true, includeBlockedInboxAttention: true, includeBlockedBy: true, includeLiveDescendantSummary: true, @@ -98,7 +101,7 @@ export function BlockedInboxView({ filteredRows.map((row) => row.issue), issueFilters, currentUserId, - true, + false, liveIssueIds, workspaceFilterContext, ).map((issue) => issue.id), diff --git a/ui/src/components/IssueFiltersPopover.test.tsx b/ui/src/components/IssueFiltersPopover.test.tsx index 796735a8eb6..ffe62d905ae 100644 --- a/ui/src/components/IssueFiltersPopover.test.tsx +++ b/ui/src/components/IssueFiltersPopover.test.tsx @@ -65,7 +65,7 @@ describe("IssueFiltersPopover", () => { agents={[{ id: "agent-1", name: "Agent One" }]} projects={[{ id: "project-1", name: "Project One" }]} labels={[{ id: "label-1", name: "Bug", color: "#ff0000" }]} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter />, ); }); @@ -98,7 +98,7 @@ describe("IssueFiltersPopover", () => { agents={[{ id: "agent-1", name: "Agent One" }]} projects={[{ id: "project-1", name: "Project One" }]} labels={[{ id: "label-1", name: "Bug", color: "#ff0000" }]} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter />, ); }); @@ -108,6 +108,9 @@ describe("IssueFiltersPopover", () => { // Status section still renders, Priority section is gated off (PAP-411). expect(popoverContent?.textContent).toContain("Status"); expect(popoverContent?.textContent).not.toContain("Priority"); + const creationToggle = Array.from(popoverContent?.querySelectorAll("label") ?? []).find((label) => label.textContent?.includes("show agent-created tasks")); + expect(creationToggle?.textContent).toBe("show agent-created tasks"); + expect(creationToggle?.querySelector("input")?.checked).toBe(false); }); it("searches long option lists while the popover remains the only scroll owner", () => { diff --git a/ui/src/components/IssueFiltersPopover.tsx b/ui/src/components/IssueFiltersPopover.tsx index 5546054e1f9..97b82dfbea3 100644 --- a/ui/src/components/IssueFiltersPopover.tsx +++ b/ui/src/components/IssueFiltersPopover.tsx @@ -108,7 +108,7 @@ export function IssueFiltersPopover({ labels, currentUserId, enableExternalObjectFilters = true, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, buttonVariant = "ghost", iconOnly = false, creators, @@ -123,7 +123,7 @@ export function IssueFiltersPopover({ labels?: LabelOption[]; currentUserId?: string | null; enableExternalObjectFilters?: boolean; - enableRoutineVisibilityFilter?: boolean; + enableAgentCreatedVisibilityFilter?: boolean; buttonVariant?: "ghost" | "outline"; iconOnly?: boolean; creators?: CreatorOption[]; @@ -520,13 +520,13 @@ export function IssueFiltersPopover({ /> Live runs only - {enableRoutineVisibilityFilter ? ( + {enableAgentCreatedVisibilityFilter ? ( ) : null} diff --git a/ui/src/components/IssuesList.test.tsx b/ui/src/components/IssuesList.test.tsx index f50a3332c04..ab89d8563f5 100644 --- a/ui/src/components/IssuesList.test.tsx +++ b/ui/src/components/IssuesList.test.tsx @@ -13,6 +13,7 @@ import { issueAgeSeparatorLabel, } from "./IssuesList"; import { TooltipProvider } from "@/components/ui/tooltip"; +import { useShowAgentCreatedTasks } from "../hooks/useShowAgentCreatedTasks"; import { taskCollectionPreferencesStorageKey } from "../lib/task-collection-preferences"; const companyState = vi.hoisted(() => ({ @@ -24,6 +25,8 @@ const dialogState = vi.hoisted(() => ({ })); const mockIssuesApi = vi.hoisted(() => ({ + get: vi.fn(), + listAll: vi.fn(), list: vi.fn(), listLabels: vi.fn(), })); @@ -222,7 +225,7 @@ function createIssue(overrides: Partial = {}): Issue { assigneeUserId: null, responsibleUserId: null, createdByAgentId: null, - createdByUserId: null, + createdByUserId: "person", issueNumber: 1, requestDepth: 0, billingCode: null, @@ -354,6 +357,7 @@ describe("IssuesList", () => { mockInstanceSettingsApi.getExperimental.mockReset(); mockExternalObjectsApi.getIssueSummaries.mockReset(); mockIssuesApi.list.mockResolvedValue([]); + mockIssuesApi.listAll.mockResolvedValue([]); mockIssuesApi.listLabels.mockResolvedValue([]); mockAuthApi.getSession.mockResolvedValue({ user: null, session: null }); mockAccessApi.listMembers.mockResolvedValue({ members: [], access: {} }); @@ -1354,7 +1358,7 @@ describe("IssuesList", () => { agents={[]} projects={[]} viewStateKey="paperclip:test-issues" - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter onUpdateIssue={() => undefined} />, container, @@ -1365,11 +1369,13 @@ describe("IssuesList", () => { status: "backlog", limit: 200, includeRoutineExecutions: true, + includeAgentCreatedTasks: false, }), { signal: expect.any(AbortSignal) }); expect(mockIssuesApi.list).toHaveBeenCalledWith("company-1", expect.objectContaining({ status: "done", limit: 200, includeRoutineExecutions: true, + includeAgentCreatedTasks: false, }), { signal: expect.any(AbortSignal) }); expect(mockKanbanBoard).toHaveBeenLastCalledWith(expect.objectContaining({ issues: expect.arrayContaining([ @@ -1561,6 +1567,48 @@ describe("IssuesList", () => { }); }); + it.each([true, false])("retains loaded context while revealing a large automated tree (streamlined=%s)", async (streamlined) => { + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableStreamlinedUi: streamlined }); + const parent = createIssue({ id: "human-root", createdByUserId: "person", originKind: "manual" }); + const children = Array.from({ length: 250 }, (_, index) => createIssue({ + id: `automated-child-${index}`, title: `Automated child ${index}`, + parentId: parent.id, createdByUserId: null, createdByAgentId: "agent", originKind: "manual", + })); + let holdNextRead = false; + let releaseRead: (() => void) | undefined; + mockIssuesApi.list.mockImplementation(async (_companyId, filters) => { + if (filters.parentId !== parent.id) return []; + const rows = children.slice(filters.offset ?? 0, (filters.offset ?? 0) + filters.limit); + if (holdNextRead) { + holdNextRead = false; + return new Promise((resolve) => { releaseRead = () => resolve(rows); }); + } + return rows; + }); + const { root } = renderWithQueryClient( + undefined} />, + container, + ); + await waitForAssertion(() => expect(container.querySelectorAll("[data-testid='issue-row']")).toHaveLength(100)); + holdNextRead = true; + act(() => { + setDocumentScrollMetrics({ innerHeight: 600, scrollY: 1500, scrollHeight: 2000 }); + window.dispatchEvent(new Event("scroll")); + }); + await flushAnimationFrame(); + await waitForAssertion(() => { + expect(releaseRead).toBeDefined(); + expect(container.querySelectorAll("[data-testid='issue-row']").length).toBeGreaterThanOrEqual(100); + }); + await act(async () => releaseRead!()); + await waitForAssertion(() => { + expect(container.querySelectorAll("[data-testid='issue-row']")).toHaveLength(251); + expect(container.textContent).toContain("Automated child 249"); + }, 60); + act(() => root.unmount()); + }); + it("keeps rendering local issue batches while the user stays near the bottom", async () => { const manyIssues = Array.from({ length: 420 }, (_, index) => createIssue({ @@ -2014,11 +2062,17 @@ describe("IssuesList", () => { }); }); - it("shows routine-backed issues by default and hides them when the routine filter is toggled off", async () => { + it.each([true, false])("keeps visibility controls and fetching preferences synchronized (streamlined=%s)", async (streamlined) => { + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableStreamlinedUi: streamlined }); + function PreferenceProbe() { + const included = useShowAgentCreatedTasks("company-1", "paperclip:test-issues"); + return {String(included)}; + } const manualIssue = createIssue({ id: "issue-manual", identifier: "PAP-10", title: "Manual issue", + createdByUserId: "person", originKind: "manual", }); const routineIssue = createIssue({ @@ -2029,20 +2083,20 @@ describe("IssuesList", () => { }); const { root } = renderWithQueryClient( - undefined} - />, + />, container, ); await waitForAssertion(() => { expect(container.textContent).toContain("Manual issue"); - expect(container.textContent).toContain("Routine issue"); + expect(container.textContent).not.toContain("Routine issue"); }); await act(async () => { @@ -2055,26 +2109,38 @@ describe("IssuesList", () => { await waitForAssertion(() => { const toggle = Array.from(document.body.querySelectorAll("label")).find( - (label) => label.textContent?.includes("Hide routine runs"), + (label) => label.textContent?.includes("show agent-created tasks"), ); expect(toggle).not.toBeUndefined(); }); await act(async () => { const toggle = Array.from(document.body.querySelectorAll("label")).find( - (label) => label.textContent?.includes("Hide routine runs"), + (label) => label.textContent?.includes("show agent-created tasks"), ); toggle?.dispatchEvent(new MouseEvent("click", { bubbles: true })); await Promise.resolve(); }); + await waitForAssertion(() => { + expect(container.textContent).toContain("Routine issue"); + expect(container.querySelector("[data-testid='creation-preference']")?.textContent).toBe("true"); + }); + const preferenceKey = taskCollectionPreferencesStorageKey({ companyId: "company-1", collectionKey: "paperclip:test-issues" }); + const envelope = JSON.parse(localStorage.getItem(preferenceKey)!); + await act(async () => { + localStorage.setItem(preferenceKey, JSON.stringify({ ...envelope, viewState: { ...envelope.viewState, showAgentCreatedTasks: false } })); + window.dispatchEvent(new StorageEvent("storage", { key: preferenceKey })); + }); await waitForAssertion(() => { expect(container.textContent).not.toContain("Routine issue"); + expect(container.querySelector("[data-testid='creation-preference']")?.textContent).toBe("false"); + const label = Array.from(document.body.querySelectorAll("label")).find((element) => element.textContent?.includes("show agent-created tasks")); + const checkbox = label?.querySelector("[role='checkbox'], input[type='checkbox']"); + expect(checkbox).not.toBeNull(); + expect(checkbox instanceof HTMLInputElement ? checkbox.checked : checkbox?.getAttribute("aria-checked") === "true").toBe(false); }); - - act(() => { - root.unmount(); - }); + act(() => root.unmount()); }); it("blurs the search input on Enter without clearing the query", async () => { diff --git a/ui/src/components/IssuesList.tsx b/ui/src/components/IssuesList.tsx index 4603c567b9f..019c62a7b48 100644 --- a/ui/src/components/IssuesList.tsx +++ b/ui/src/components/IssuesList.tsx @@ -1,3 +1,6 @@ +import { useShowAgentCreatedTasks } from "../hooks/useShowAgentCreatedTasks"; +import { useIssueHierarchyContext } from "../hooks/useIssueHierarchyContext"; +import { includeIssueHierarchyContext } from "../lib/issue-hierarchy-context"; import { AgentIdentity } from "@/components/AgentIdentity"; import { startTransition, useDeferredValue, useEffect, useMemo, useState, useCallback, useRef } from "react"; import type { ReactNode } from "react"; @@ -495,7 +498,7 @@ interface IssuesListProps { * tokens + wall-clock runtime for every run in the tree. */ parentIssueIdForCostSummary?: string; - enableRoutineVisibilityFilter?: boolean; + enableAgentCreatedVisibilityFilter?: boolean; hasMoreIssues?: boolean; isLoadingMoreIssues?: boolean; mutedIssueIds?: Set; @@ -735,7 +738,7 @@ function StreamlinedIssuesList({ defaultSortField, showProgressSummary = false, parentIssueIdForCostSummary, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, hasMoreIssues = false, isLoadingMoreIssues = false, mutedIssueIds, @@ -822,10 +825,17 @@ function StreamlinedIssuesList({ initialStatuses, ), ); + const showAgentCreatedTasks = useShowAgentCreatedTasks(selectedCompanyId, viewStateKey); + useEffect(() => { + if (!enableAgentCreatedVisibilityFilter) return; + setViewState((previous) => previous.showAgentCreatedTasks === showAgentCreatedTasks + ? previous : { ...previous, showAgentCreatedTasks }); + }, [enableAgentCreatedVisibilityFilter, showAgentCreatedTasks]); const [assigneePickerIssueId, setAssigneePickerIssueId] = useState(null); const [assigneeSearch, setAssigneeSearch] = useState(""); const [issueSearch, setIssueSearch] = useState(initialSearch ?? ""); const [renderedIssueRowLimit, setRenderedIssueRowLimit] = useState(INITIAL_ISSUE_ROW_RENDER_LIMIT); + const [hierarchyRowBudget, setHierarchyRowBudget] = useState(INITIAL_ISSUE_ROW_RENDER_LIMIT); const [visibleIssueColumns, setVisibleIssueColumns] = useState(initialPreferences.columns); const renderedIssueIdsRef = useRef(""); const initialServerFillRequestedRef = useRef(false); @@ -895,6 +905,8 @@ function StreamlinedIssuesList({ // Deleted or reassigned issues leave orphan IDs in localStorage; this keeps // the stored array bounded to only current parent IDs. useEffect(() => { + // Contextual children are loaded separately from independent results. + if (enableAgentCreatedVisibilityFilter) return; const parentIds = new Set(issues.map((i) => i.parentId).filter(Boolean) as string[]); const pruned = viewState.collapsedParents.filter((id) => parentIds.has(id)); if (pruned.length !== viewState.collapsedParents.length) { @@ -909,7 +921,8 @@ function StreamlinedIssuesList({ searchFilters ?? {}, "compact", ISSUE_SEARCH_RESULT_LIMIT, - enableRoutineVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? viewState.showAgentCreatedTasks : undefined, ], queryFn: ({ signal }) => issuesApi.listCompact(selectedCompanyId!, { @@ -917,7 +930,7 @@ function StreamlinedIssuesList({ projectId, limit: ISSUE_SEARCH_RESULT_LIMIT, ...searchFilters, - ...(enableRoutineVisibilityFilter ? { includeRoutineExecutions: true } : {}), + ...(enableAgentCreatedVisibilityFilter ? { includeRoutineExecutions: true, includeAgentCreatedTasks: viewState.showAgentCreatedTasks } : {}), }, { signal }).then((rows) => rows as Issue[]), enabled: !!selectedCompanyId && normalizedIssueSearch.length > 0 && !searchWithinLoadedIssues, placeholderData: (previousData) => previousData, @@ -933,7 +946,8 @@ function StreamlinedIssuesList({ searchFilters ?? {}, "compact", ISSUE_BOARD_COLUMN_RESULT_LIMIT, - enableRoutineVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? viewState.showAgentCreatedTasks : undefined, ], queryFn: ({ signal }: { signal: AbortSignal }) => issuesApi.listCompact(selectedCompanyId!, { @@ -942,7 +956,7 @@ function StreamlinedIssuesList({ projectId, status, limit: ISSUE_BOARD_COLUMN_RESULT_LIMIT, - ...(enableRoutineVisibilityFilter ? { includeRoutineExecutions: true } : {}), + ...(enableAgentCreatedVisibilityFilter ? { includeRoutineExecutions: true, includeAgentCreatedTasks: viewState.showAgentCreatedTasks } : {}), }, { signal }).then((rows) => rows as Issue[]), enabled: !!selectedCompanyId && viewState.viewMode === "board" && !searchWithinLoadedIssues, placeholderData: (previousData: Issue[] | undefined) => previousData, @@ -1202,7 +1216,7 @@ function StreamlinedIssuesList({ searchScopedIssues, viewState, currentUserId, - enableRoutineVisibilityFilter, + enableAgentCreatedVisibilityFilter, liveIssueIds, issueFilterContext, ); @@ -1211,7 +1225,7 @@ function StreamlinedIssuesList({ searchScopedIssues, viewState, currentUserId, - enableRoutineVisibilityFilter, + enableAgentCreatedVisibilityFilter, liveIssueIds, issueFilterContext, ]); @@ -1269,7 +1283,7 @@ function StreamlinedIssuesList({ enabled: !!selectedCompanyId, }); - const activeFilterCount = countActiveIssueFilters(viewState, enableRoutineVisibilityFilter); + const activeFilterCount = countActiveIssueFilters(viewState, enableAgentCreatedVisibilityFilter); const boardHighVolume = viewState.viewMode === "board" && filtered.length > KANBAN_BOARD_HIGH_VOLUME_THRESHOLD; const boardCompactCards = viewState.boardCardDensity === "compact" @@ -1287,7 +1301,13 @@ function StreamlinedIssuesList({ || viewState.boardColdLaneMode !== "auto" || viewState.boardColumnPageSize !== KANBAN_COLUMN_DEFAULT_PAGE_SIZE; - const groupedContent = useMemo(() => { + const hierarchyEnabled = enableAgentCreatedVisibilityFilter && !viewState.showAgentCreatedTasks && viewState.nestingEnabled && viewState.viewMode === "list"; + const hierarchy = useIssueHierarchyContext( + selectedCompanyId, filtered.slice(0, renderedIssueRowLimit), viewState.collapsedParents, + hierarchyEnabled, + hierarchyRowBudget, + ); + const baseGroupedContent = useMemo(() => { if (viewState.groupBy === "none") { return [{ key: "__all", label: null as string | null, items: filtered }]; } @@ -1379,6 +1399,14 @@ function StreamlinedIssuesList({ companyUserLabelMap, projectById, ]); + const groupedContent = useMemo(() => baseGroupedContent.map((group) => ({ + ...group, + items: enableAgentCreatedVisibilityFilter && !viewState.showAgentCreatedTasks && viewState.nestingEnabled && viewState.viewMode === "list" + ? includeIssueHierarchyContext(group.items, [...issues, ...hierarchy.issues]) + : group.items, + })), [baseGroupedContent, hierarchy.issues, issues, enableAgentCreatedVisibilityFilter, viewState.showAgentCreatedTasks, viewState.nestingEnabled, viewState.viewMode]); + const visibleListRowCount = groupedContent.reduce((total, group) => total + group.items.length, 0); + // Flattened visible order (group headers, then tree DFS per group — // collapsed groups keep their header entry but skip their rows) — must @@ -1568,31 +1596,41 @@ function StreamlinedIssuesList({ const previousIssueIds = renderedIssueIdsRef.current; renderedIssueIdsRef.current = nextIssueIds; + const sameResultsOrAppend = previousIssueIds.length > 0 && nextIssueIds.startsWith(previousIssueIds); + if (!sameResultsOrAppend) setHierarchyRowBudget(INITIAL_ISSUE_ROW_RENDER_LIMIT); setRenderedIssueRowLimit((current) => { - const nextInitialLimit = Math.min(filtered.length, INITIAL_ISSUE_ROW_RENDER_LIMIT); - const listAppended = previousIssueIds.length > 0 - && nextIssueIds.startsWith(previousIssueIds) - && filtered.length >= current; - if (listAppended) return Math.min(filtered.length, Math.max(current, nextInitialLimit)); + const nextInitialLimit = Math.min(visibleListRowCount, INITIAL_ISSUE_ROW_RENDER_LIMIT); + // Context arrives asynchronously and must not undo a reveal request. + if (hierarchyEnabled && sameResultsOrAppend) return Math.max(current, nextInitialLimit); + const listAppended = sameResultsOrAppend && visibleListRowCount >= current; + if (listAppended) return Math.min(visibleListRowCount, Math.max(current, nextInitialLimit)); return nextInitialLimit; }); - }, [filtered, viewState.viewMode]); + }, [filtered, visibleListRowCount, viewState.viewMode, hierarchyEnabled]); - const hasMoreRenderedRows = viewState.viewMode === "list" && renderedIssueRowLimit < filtered.length; - const remainingIssueRowCount = Math.max(filtered.length - renderedIssueRowLimit, 0); + const hasMoreHierarchyRows = hierarchyEnabled && hierarchy.hasMore; + const hasMoreRenderedRows = viewState.viewMode === "list" && renderedIssueRowLimit < visibleListRowCount; + const remainingIssueRowCount = Math.max(visibleListRowCount - renderedIssueRowLimit, 0); const loadMoreIssueRows = useCallback(() => { if (viewState.viewMode !== "list") return; + if (hasMoreHierarchyRows && !hierarchy.isFetching) { + setHierarchyRowBudget((current) => current + ISSUE_ROW_RENDER_BATCH_SIZE); + setRenderedIssueRowLimit((current) => current + ISSUE_ROW_RENDER_BATCH_SIZE); + return; + } if (hasMoreRenderedRows) { - setRenderedIssueRowLimit((current) => Math.min(filtered.length, current + ISSUE_ROW_RENDER_BATCH_SIZE)); + setRenderedIssueRowLimit((current) => Math.min(visibleListRowCount, current + ISSUE_ROW_RENDER_BATCH_SIZE)); return; } if (hasMoreIssues && !isLoadingMoreIssues) { onLoadMoreIssues?.(); } }, [ - filtered.length, + visibleListRowCount, hasMoreIssues, hasMoreRenderedRows, + hasMoreHierarchyRows, + hierarchy.isFetching, isLoadingMoreIssues, onLoadMoreIssues, viewState.viewMode, @@ -1600,7 +1638,7 @@ function StreamlinedIssuesList({ const canLoadMoreIssues = viewState.viewMode === "list" && !isLoading - && (hasMoreRenderedRows || (hasMoreIssues && !isLoadingMoreIssues)); + && (hasMoreRenderedRows || (hasMoreHierarchyRows && !hierarchy.isFetching) || (hasMoreIssues && !isLoadingMoreIssues)); useEffect(() => { if (!canLoadMoreIssues) return; @@ -1911,7 +1949,7 @@ function StreamlinedIssuesList({ labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter={enableRoutineVisibilityFilter} + enableAgentCreatedVisibilityFilter={enableAgentCreatedVisibilityFilter} iconOnly presentation={rowPresentation === "task" ? "streamlined" : "legacy"} /> @@ -2016,7 +2054,8 @@ function StreamlinedIssuesList({ Some board columns are showing up to {ISSUE_BOARD_COLUMN_RESULT_LIMIT} tasks. Refine filters or search to reveal the rest.

)} - {!isLoading && !externalObjectFilterLoading && filtered.length === 0 && viewState.viewMode === "list" && ( + {hierarchy.error ?

Failed to load task hierarchy: {hierarchy.error.message}

: null} + {!isLoading && !externalObjectFilterLoading && visibleListRowCount === 0 && viewState.viewMode === "list" && ( ); })} - {(remainingIssueRowCount > 0 || hasMoreIssues || isLoadingMoreIssues) && ( + {(remainingIssueRowCount > 0 || hasMoreHierarchyRows || hasMoreIssues || isLoadingMoreIssues) && (

- {isLoadingMoreIssues + {isLoadingMoreIssues || (hierarchyEnabled && hierarchy.isFetching) ? "Loading more tasks..." : remainingIssueRowCount > 0 - ? `Rendering ${Math.min(renderedIssueRowLimit, filtered.length)} of ${filtered.length} tasks` + ? `Rendering ${Math.min(renderedIssueRowLimit, visibleListRowCount)} of ${visibleListRowCount} tasks` : "Scroll to load more tasks"}

diff --git a/ui/src/components/LegacyIssuesList.tsx b/ui/src/components/LegacyIssuesList.tsx index 5cd5db5358a..cc30fbef84b 100644 --- a/ui/src/components/LegacyIssuesList.tsx +++ b/ui/src/components/LegacyIssuesList.tsx @@ -1,3 +1,7 @@ +import { saveTaskCollectionPreferences } from "../lib/task-collection-preferences"; +import { useShowAgentCreatedTasks } from "../hooks/useShowAgentCreatedTasks"; +import { useIssueHierarchyContext } from "../hooks/useIssueHierarchyContext"; +import { includeIssueHierarchyContext } from "../lib/issue-hierarchy-context"; import { AgentIdentity } from "@/components/AgentIdentity"; import { startTransition, useDeferredValue, useEffect, useMemo, useState, useCallback, useRef } from "react"; import type { ReactNode } from "react"; @@ -208,10 +212,6 @@ function getViewState(key: string): IssueViewState { return { ...defaultViewState }; } -function saveViewState(key: string, state: IssueViewState) { - localStorage.setItem(key, JSON.stringify(state)); -} - function getInitialViewState( key: string, initialAssignees?: string[], @@ -269,17 +269,6 @@ function loadIssueColumns(key: string): InboxIssueColumn[] { } } -function saveIssueColumns(key: string, columns: InboxIssueColumn[]) { - try { - localStorage.setItem( - getIssueColumnsStorageKey(key), - JSON.stringify(normalizeInboxIssueColumns(columns)), - ); - } catch { - // Ignore localStorage failures. - } -} - function sortIssues(issues: Issue[], state: IssueViewState): Issue[] { if (state.sortField === "workflow") { const ordered = workflowSort(issues); @@ -489,7 +478,7 @@ interface IssuesListProps { * tokens + wall-clock runtime for every run in the tree. */ parentIssueIdForCostSummary?: string; - enableRoutineVisibilityFilter?: boolean; + enableAgentCreatedVisibilityFilter?: boolean; hasMoreIssues?: boolean; isLoadingMoreIssues?: boolean; mutedIssueIds?: Set; @@ -700,7 +689,7 @@ export function IssuesList({ defaultSortField, showProgressSummary = false, parentIssueIdForCostSummary, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, hasMoreIssues = false, isLoadingMoreIssues = false, mutedIssueIds, @@ -766,10 +755,17 @@ export function IssuesList({ const [viewState, setViewState] = useState(() => getInitialWorkspaceViewState(scopedKey, initialAssignees, initialWorkspaces, defaultSortField), ); + const showAgentCreatedTasks = useShowAgentCreatedTasks(selectedCompanyId, viewStateKey); + useEffect(() => { + if (!enableAgentCreatedVisibilityFilter) return; + setViewState((previous) => previous.showAgentCreatedTasks === showAgentCreatedTasks + ? previous : { ...previous, showAgentCreatedTasks }); + }, [enableAgentCreatedVisibilityFilter, showAgentCreatedTasks]); const [assigneePickerIssueId, setAssigneePickerIssueId] = useState(null); const [assigneeSearch, setAssigneeSearch] = useState(""); const [issueSearch, setIssueSearch] = useState(initialSearch ?? ""); const [renderedIssueRowLimit, setRenderedIssueRowLimit] = useState(INITIAL_ISSUE_ROW_RENDER_LIMIT); + const [hierarchyRowBudget, setHierarchyRowBudget] = useState(INITIAL_ISSUE_ROW_RENDER_LIMIT); const [visibleIssueColumns, setVisibleIssueColumns] = useState(() => loadIssueColumns(scopedKey)); const renderedIssueIdsRef = useRef(""); const initialServerFillRequestedRef = useRef(false); @@ -801,10 +797,13 @@ export function IssuesList({ const updateView = useCallback((patch: Partial) => { setViewState((prev) => { const next = { ...prev, ...patch }; - saveViewState(scopedKey, next); + saveTaskCollectionPreferences({ + companyId: selectedCompanyId ?? "__unscoped__", collectionKey: viewStateKey, + legacyViewStorageKey: scopedKey, legacyColumnsStorageKey: getIssueColumnsStorageKey(scopedKey), + }, { viewState: next, columns: visibleIssueColumns }); return next; }); - }, [scopedKey]); + }, [scopedKey, selectedCompanyId, viewStateKey, visibleIssueColumns]); useEffect(() => { if (!experimentalSettingsLoaded || externalObjectsEnabled || viewState.externalObjectStatuses.length === 0) return; @@ -815,6 +814,8 @@ export function IssuesList({ // Deleted or reassigned issues leave orphan IDs in localStorage; this keeps // the stored array bounded to only current parent IDs. useEffect(() => { + // Contextual children are loaded separately from independent results. + if (enableAgentCreatedVisibilityFilter) return; const parentIds = new Set(issues.map((i) => i.parentId).filter(Boolean) as string[]); const pruned = viewState.collapsedParents.filter((id) => parentIds.has(id)); if (pruned.length !== viewState.collapsedParents.length) { @@ -829,7 +830,8 @@ export function IssuesList({ searchFilters ?? {}, "compact", ISSUE_SEARCH_RESULT_LIMIT, - enableRoutineVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? viewState.showAgentCreatedTasks : undefined, ], queryFn: ({ signal }) => issuesApi.listCompact(selectedCompanyId!, { @@ -837,7 +839,7 @@ export function IssuesList({ projectId, limit: ISSUE_SEARCH_RESULT_LIMIT, ...searchFilters, - ...(enableRoutineVisibilityFilter ? { includeRoutineExecutions: true } : {}), + ...(enableAgentCreatedVisibilityFilter ? { includeRoutineExecutions: true, includeAgentCreatedTasks: viewState.showAgentCreatedTasks } : {}), }, { signal }).then((rows) => rows as Issue[]), enabled: !!selectedCompanyId && normalizedIssueSearch.length > 0 && !searchWithinLoadedIssues, placeholderData: (previousData) => previousData, @@ -853,7 +855,8 @@ export function IssuesList({ searchFilters ?? {}, "compact", ISSUE_BOARD_COLUMN_RESULT_LIMIT, - enableRoutineVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? "with-routine-executions" : "without-routine-executions", + enableAgentCreatedVisibilityFilter ? viewState.showAgentCreatedTasks : undefined, ], queryFn: ({ signal }: { signal: AbortSignal }) => issuesApi.listCompact(selectedCompanyId!, { @@ -862,7 +865,7 @@ export function IssuesList({ projectId, status, limit: ISSUE_BOARD_COLUMN_RESULT_LIMIT, - ...(enableRoutineVisibilityFilter ? { includeRoutineExecutions: true } : {}), + ...(enableAgentCreatedVisibilityFilter ? { includeRoutineExecutions: true, includeAgentCreatedTasks: viewState.showAgentCreatedTasks } : {}), }, { signal }).then((rows) => rows as Issue[]), enabled: !!selectedCompanyId && viewState.viewMode === "board" && !searchWithinLoadedIssues, placeholderData: (previousData: Issue[] | undefined) => previousData, @@ -1118,7 +1121,7 @@ export function IssuesList({ searchScopedIssues, viewState, currentUserId, - enableRoutineVisibilityFilter, + enableAgentCreatedVisibilityFilter, liveIssueIds, issueFilterContext, ); @@ -1127,7 +1130,7 @@ export function IssuesList({ searchScopedIssues, viewState, currentUserId, - enableRoutineVisibilityFilter, + enableAgentCreatedVisibilityFilter, liveIssueIds, issueFilterContext, ]); @@ -1185,7 +1188,7 @@ export function IssuesList({ enabled: !!selectedCompanyId, }); - const activeFilterCount = countActiveIssueFilters(viewState, enableRoutineVisibilityFilter); + const activeFilterCount = countActiveIssueFilters(viewState, enableAgentCreatedVisibilityFilter); const boardHighVolume = viewState.viewMode === "board" && filtered.length > KANBAN_BOARD_HIGH_VOLUME_THRESHOLD; const boardCompactCards = viewState.boardCardDensity === "compact" @@ -1203,7 +1206,13 @@ export function IssuesList({ || viewState.boardColdLaneMode !== "auto" || viewState.boardColumnPageSize !== KANBAN_COLUMN_DEFAULT_PAGE_SIZE; - const groupedContent = useMemo(() => { + const hierarchyEnabled = enableAgentCreatedVisibilityFilter && !viewState.showAgentCreatedTasks && viewState.nestingEnabled && viewState.viewMode === "list"; + const hierarchy = useIssueHierarchyContext( + selectedCompanyId, filtered.slice(0, renderedIssueRowLimit), viewState.collapsedParents, + hierarchyEnabled, + hierarchyRowBudget, + ); + const baseGroupedContent = useMemo(() => { if (viewState.groupBy === "none") { return [{ key: "__all", label: null as string | null, items: filtered }]; } @@ -1295,6 +1304,14 @@ export function IssuesList({ companyUserLabelMap, projectById, ]); + const groupedContent = useMemo(() => baseGroupedContent.map((group) => ({ + ...group, + items: enableAgentCreatedVisibilityFilter && !viewState.showAgentCreatedTasks && viewState.nestingEnabled && viewState.viewMode === "list" + ? includeIssueHierarchyContext(group.items, [...issues, ...hierarchy.issues]) + : group.items, + })), [baseGroupedContent, hierarchy.issues, issues, enableAgentCreatedVisibilityFilter, viewState.showAgentCreatedTasks, viewState.nestingEnabled, viewState.viewMode]); + const visibleListRowCount = groupedContent.reduce((total, group) => total + group.items.length, 0); + // Flattened visible order (group headers, then tree DFS per group — // collapsed groups keep their header entry but skip their rows) — must @@ -1484,31 +1501,41 @@ export function IssuesList({ const previousIssueIds = renderedIssueIdsRef.current; renderedIssueIdsRef.current = nextIssueIds; + const sameResultsOrAppend = previousIssueIds.length > 0 && nextIssueIds.startsWith(previousIssueIds); + if (!sameResultsOrAppend) setHierarchyRowBudget(INITIAL_ISSUE_ROW_RENDER_LIMIT); setRenderedIssueRowLimit((current) => { - const nextInitialLimit = Math.min(filtered.length, INITIAL_ISSUE_ROW_RENDER_LIMIT); - const listAppended = previousIssueIds.length > 0 - && nextIssueIds.startsWith(previousIssueIds) - && filtered.length >= current; - if (listAppended) return Math.min(filtered.length, Math.max(current, nextInitialLimit)); + const nextInitialLimit = Math.min(visibleListRowCount, INITIAL_ISSUE_ROW_RENDER_LIMIT); + // Context arrives asynchronously and must not undo a reveal request. + if (hierarchyEnabled && sameResultsOrAppend) return Math.max(current, nextInitialLimit); + const listAppended = sameResultsOrAppend && visibleListRowCount >= current; + if (listAppended) return Math.min(visibleListRowCount, Math.max(current, nextInitialLimit)); return nextInitialLimit; }); - }, [filtered, viewState.viewMode]); + }, [filtered, visibleListRowCount, viewState.viewMode, hierarchyEnabled]); - const hasMoreRenderedRows = viewState.viewMode === "list" && renderedIssueRowLimit < filtered.length; - const remainingIssueRowCount = Math.max(filtered.length - renderedIssueRowLimit, 0); + const hasMoreHierarchyRows = hierarchyEnabled && hierarchy.hasMore; + const hasMoreRenderedRows = viewState.viewMode === "list" && renderedIssueRowLimit < visibleListRowCount; + const remainingIssueRowCount = Math.max(visibleListRowCount - renderedIssueRowLimit, 0); const loadMoreIssueRows = useCallback(() => { if (viewState.viewMode !== "list") return; + if (hasMoreHierarchyRows && !hierarchy.isFetching) { + setHierarchyRowBudget((current) => current + ISSUE_ROW_RENDER_BATCH_SIZE); + setRenderedIssueRowLimit((current) => current + ISSUE_ROW_RENDER_BATCH_SIZE); + return; + } if (hasMoreRenderedRows) { - setRenderedIssueRowLimit((current) => Math.min(filtered.length, current + ISSUE_ROW_RENDER_BATCH_SIZE)); + setRenderedIssueRowLimit((current) => Math.min(visibleListRowCount, current + ISSUE_ROW_RENDER_BATCH_SIZE)); return; } if (hasMoreIssues && !isLoadingMoreIssues) { onLoadMoreIssues?.(); } }, [ - filtered.length, + visibleListRowCount, hasMoreIssues, hasMoreRenderedRows, + hasMoreHierarchyRows, + hierarchy.isFetching, isLoadingMoreIssues, onLoadMoreIssues, viewState.viewMode, @@ -1516,7 +1543,7 @@ export function IssuesList({ const canLoadMoreIssues = viewState.viewMode === "list" && !isLoading - && (hasMoreRenderedRows || (hasMoreIssues && !isLoadingMoreIssues)); + && (hasMoreRenderedRows || (hasMoreHierarchyRows && !hierarchy.isFetching) || (hasMoreIssues && !isLoadingMoreIssues)); useEffect(() => { if (!canLoadMoreIssues) return; @@ -1624,8 +1651,11 @@ export function IssuesList({ const setIssueColumns = useCallback((next: InboxIssueColumn[]) => { const normalized = normalizeInboxIssueColumns(next); setVisibleIssueColumns(normalized); - saveIssueColumns(scopedKey, normalized); - }, [scopedKey]); + saveTaskCollectionPreferences({ + companyId: selectedCompanyId ?? "__unscoped__", collectionKey: viewStateKey, + legacyViewStorageKey: scopedKey, legacyColumnsStorageKey: getIssueColumnsStorageKey(scopedKey), + }, { viewState, columns: normalized }); + }, [scopedKey, selectedCompanyId, viewStateKey, viewState]); const toggleIssueColumn = useCallback((column: InboxIssueColumn, enabled: boolean) => { if (enabled) { @@ -1803,7 +1833,7 @@ export function IssuesList({ labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter={enableRoutineVisibilityFilter} + enableAgentCreatedVisibilityFilter={enableAgentCreatedVisibilityFilter} iconOnly /> @@ -1906,7 +1936,8 @@ export function IssuesList({ Some board columns are showing up to {ISSUE_BOARD_COLUMN_RESULT_LIMIT} tasks. Refine filters or search to reveal the rest.

)} - {!isLoading && !externalObjectFilterLoading && filtered.length === 0 && viewState.viewMode === "list" && ( + {hierarchy.error ?

Failed to load task hierarchy: {hierarchy.error.message}

: null} + {!isLoading && !externalObjectFilterLoading && visibleListRowCount === 0 && viewState.viewMode === "list" && ( ); })} - {(remainingIssueRowCount > 0 || hasMoreIssues || isLoadingMoreIssues) && ( + {(remainingIssueRowCount > 0 || hasMoreHierarchyRows || hasMoreIssues || isLoadingMoreIssues) && (

- {isLoadingMoreIssues + {isLoadingMoreIssues || (hierarchyEnabled && hierarchy.isFetching) ? "Loading more tasks..." : remainingIssueRowCount > 0 - ? `Rendering ${Math.min(renderedIssueRowLimit, filtered.length)} of ${filtered.length} tasks` + ? `Rendering ${Math.min(renderedIssueRowLimit, visibleListRowCount)} of ${visibleListRowCount} tasks` : "Scroll to load more tasks"}

diff --git a/ui/src/hooks/useIssueHierarchyContext.ts b/ui/src/hooks/useIssueHierarchyContext.ts new file mode 100644 index 00000000000..39808380c5a --- /dev/null +++ b/ui/src/hooks/useIssueHierarchyContext.ts @@ -0,0 +1,50 @@ +import { loadIssueHierarchyChildren } from "../lib/issue-hierarchy-context"; +import { useQueries, useQuery } from "@tanstack/react-query"; +import type { Issue } from "@paperclipai/shared"; +import { issuesApi } from "../api/issues"; +import { ApiError } from "../api/client"; +import { queryKeys } from "../lib/queryKeys"; + +export function useIssueHierarchyContext(companyId: string | null, matches: Issue[], collapsedParents: string[], enabled: boolean, rowBudget: number) { + const parents = [...new Set(matches.map((issue) => issue.parentId).filter((id): id is string => Boolean(id)))]; + const ancestorQueries = useQueries({ queries: parents.map((parentId) => ({ + queryKey: [...queryKeys.issues.list(companyId ?? "__unscoped__"), "hierarchy-ancestors", parentId], + enabled: enabled && Boolean(companyId), + staleTime: 30_000, + queryFn: async ({ signal }: { signal: AbortSignal }) => { + const ancestors: Issue[] = []; + const visited = new Set(); + let id: string | null = parentId; + while (id && !visited.has(id)) { + visited.add(id); + try { + const parent: Issue = await issuesApi.get(id, { signal }); + if (parent.companyId !== companyId) break; + ancestors.push(parent); + id = parent.parentId; + } catch (error) { + if (error instanceof ApiError && (error.status === 403 || error.status === 404)) break; + throw error; + } + } + return ancestors; + }, + })) }); + const children = useQuery>>({ + queryKey: [...queryKeys.issues.list(companyId ?? "__unscoped__"), "hierarchy-children", matches.map((issue) => issue.id), collapsedParents, rowBudget], + enabled: enabled && Boolean(companyId) && matches.length > 0, + staleTime: 30_000, + placeholderData: (previous) => previous ? { + ...previous, issues: previous.issues.filter((issue) => issue.companyId === companyId), + } : undefined, + queryFn: ({ signal }) => loadIssueHierarchyChildren(companyId!, matches, collapsedParents, rowBudget, + (parentId, limit, offset) => issuesApi.list(companyId!, { parentId, limit, offset, includeRoutineExecutions: true }, { signal })), + }); + const queries = [...ancestorQueries, children]; + return { + issues: [...ancestorQueries.flatMap((query) => query.data ?? []), ...(children.data?.issues ?? [])], + hasMore: children.data?.hasMore ?? false, + isFetching: children.isFetching, + error: queries.find((query) => query.error)?.error, + }; +} diff --git a/ui/src/hooks/useShowAgentCreatedTasks.ts b/ui/src/hooks/useShowAgentCreatedTasks.ts new file mode 100644 index 00000000000..cdd6835540c --- /dev/null +++ b/ui/src/hooks/useShowAgentCreatedTasks.ts @@ -0,0 +1,30 @@ +import { useCallback, useSyncExternalStore } from "react"; +import { taskCollectionPreferencesStorageKey, TASK_COLLECTION_PREFERENCES_CHANGED, TASK_COLLECTION_PREFERENCES_VERSION } from "../lib/task-collection-preferences"; + +/** Subscribe the collection's fetching owner to its persisted filter control. */ +export function useShowAgentCreatedTasks(companyId: string | null, collectionKey: string): boolean { + const getSnapshot = useCallback(() => { + try { + const key = taskCollectionPreferencesStorageKey({ companyId: companyId ?? "__unscoped__", collectionKey }); + const current = localStorage.getItem(key); + const legacy = localStorage.getItem(companyId ? `${collectionKey}:${companyId}` : collectionKey); + const envelope = current ? JSON.parse(current) : null; + const valid = envelope?.version === TASK_COLLECTION_PREFERENCES_VERSION + && envelope.companyId === (companyId ?? "__unscoped__") && envelope.collectionKey === collectionKey; + const view = valid ? envelope.viewState : legacy ? JSON.parse(legacy) : null; + return view?.showAgentCreatedTasks === true; + } catch { + return false; + } + }, [companyId, collectionKey]); + return useSyncExternalStore(subscribe, getSnapshot, () => false); +} + +function subscribe(onChange: () => void) { + window.addEventListener(TASK_COLLECTION_PREFERENCES_CHANGED, onChange); + window.addEventListener("storage", onChange); + return () => { + window.removeEventListener(TASK_COLLECTION_PREFERENCES_CHANGED, onChange); + window.removeEventListener("storage", onChange); + }; +} diff --git a/ui/src/lib/inbox.test.ts b/ui/src/lib/inbox.test.ts index 79d0afbf397..836da02bc75 100644 --- a/ui/src/lib/inbox.test.ts +++ b/ui/src/lib/inbox.test.ts @@ -18,7 +18,6 @@ import { buildInboxKeyboardNavEntries, buildInboxDismissedAtByKey, computeInboxBadgeData, - filterInboxIssues, getArchivedInboxSearchIssues, getAvailableInboxIssueColumns, getInboxWorkItemKey, @@ -943,7 +942,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, }).map((issue) => issue.id), ).toEqual(["remote-match"]); @@ -964,7 +963,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, }), ).toEqual([]); @@ -985,7 +984,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, }), ).toEqual([]); @@ -1033,7 +1032,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: ["failed"], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, issueFilterContext: { externalObjectSummaryByIssueId: summaries, @@ -1129,7 +1128,7 @@ describe("inbox helpers", () => { workspaces: ["workspace-1"], liveOnly: true, externalObjectStatuses: [], - hideRoutineExecutions: false, + showAgentCreatedTasks: true, }, }); saveInboxFilterPreferences("company-2", { @@ -1145,7 +1144,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, }); @@ -1162,7 +1161,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: true, externalObjectStatuses: [], - hideRoutineExecutions: false, + showAgentCreatedTasks: true, }, }); expect(loadInboxFilterPreferences("company-2")).toEqual({ @@ -1178,7 +1177,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: true, + showAgentCreatedTasks: false, }, }); }); @@ -1196,7 +1195,7 @@ describe("inbox helpers", () => { projects: ["project-1"], workspaces: ["workspace-1", false], liveOnly: "yes", - hideRoutineExecutions: "yes", + showAgentCreatedTasks: "yes", }, })); @@ -1213,7 +1212,7 @@ describe("inbox helpers", () => { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: false, + showAgentCreatedTasks: false, }, }); }); @@ -1377,13 +1376,6 @@ describe("inbox helpers", () => { expect(getInboxKeyboardSelectionIndex(0, 3, "previous")).toBe(0); }); - it("hides routine execution issues when the hide toggle is enabled", () => { - const manualIssue = { ...makeIssue("manual", true), originKind: "manual" as const }; - const routineIssue = { ...makeIssue("routine", true), originKind: "routine_execution" as const }; - - expect(filterInboxIssues([manualIssue, routineIssue], false)).toEqual([manualIssue, routineIssue]); - expect(filterInboxIssues([manualIssue, routineIssue], true)).toEqual([manualIssue]); - }); it("groups mixed inbox items by type while preserving item order within each group", () => { const items: InboxWorkItem[] = [ diff --git a/ui/src/lib/inbox.ts b/ui/src/lib/inbox.ts index 368cc213a48..89c03541a15 100644 --- a/ui/src/lib/inbox.ts +++ b/ui/src/lib/inbox.ts @@ -425,17 +425,7 @@ export function shouldResetInboxWorkspaceGrouping( return experimentalSettingsLoaded && groupBy === "workspace" && !isolatedWorkspacesEnabled; } -export function shouldIncludeRoutineExecutionIssue( - issue: Pick, - hideRoutineExecutions: boolean, -): boolean { - return !hideRoutineExecutions || issue.originKind !== "routine_execution"; -} -export function filterInboxIssues(issues: Issue[], hideRoutineExecutions: boolean): Issue[] { - if (!hideRoutineExecutions) return issues; - return issues.filter((issue) => shouldIncludeRoutineExecutionIssue(issue, hideRoutineExecutions)); -} export function matchesInboxIssueSearch( issue: Pick, @@ -505,7 +495,7 @@ export function getInboxSearchSupplementIssues({ remoteIssues, issueFilters, currentUserId, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, liveIssueIds, issueFilterContext = {}, }: { @@ -515,7 +505,7 @@ export function getInboxSearchSupplementIssues({ remoteIssues: Issue[]; issueFilters: IssueFilterState; currentUserId?: string | null; - enableRoutineVisibilityFilter?: boolean; + enableAgentCreatedVisibilityFilter?: boolean; liveIssueIds?: ReadonlySet; issueFilterContext?: IssueFilterWorkspaceContext; }): Issue[] { @@ -531,7 +521,7 @@ export function getInboxSearchSupplementIssues({ remoteIssues, issueFilters, currentUserId, - enableRoutineVisibilityFilter, + enableAgentCreatedVisibilityFilter, liveIssueIds, issueFilterContext, ) diff --git a/ui/src/lib/issue-filters.test.ts b/ui/src/lib/issue-filters.test.ts index c17d2d1c500..e8c26f54455 100644 --- a/ui/src/lib/issue-filters.test.ts +++ b/ui/src/lib/issue-filters.test.ts @@ -6,6 +6,7 @@ import { applyIssueFilters, countActiveIssueFilters, defaultIssueFilterState, + normalizeIssueFilterState, resolveIssueFilterWorkspaceId, searchIssueFilterOptions, shouldIncludeIssueFilterWorkspaceOption, @@ -69,6 +70,22 @@ function makeExternalObjectSummary(overrides: Partial = { } describe("issue filters", () => { + it("hides automation by default while an opt-in includes every creation source", () => { + const issues = [makeIssue({ id: "human", createdByUserId: "person" }), makeIssue({ id: "agent", createdByAgentId: "agent" }), makeIssue({ id: "routine", originKind: "routine_execution", createdByUserId: "person" }), makeIssue({ id: "system" })]; + expect(applyIssueFilters(issues, defaultIssueFilterState, null, true).map((issue) => issue.id)).toEqual(["human"]); + expect(applyIssueFilters(issues, { ...defaultIssueFilterState, showAgentCreatedTasks: true }, null, true)).toEqual(issues); + // Membership and task-detail collections can explicitly bypass creation visibility. + expect(applyIssueFilters(issues, defaultIssueFilterState, null, false)).toEqual(issues); + expect(countActiveIssueFilters(defaultIssueFilterState, true)).toBe(0); + expect(countActiveIssueFilters({ ...defaultIssueFilterState, showAgentCreatedTasks: true }, true)).toBe(1); + }); + + it("defaults legacy and invalid preferences to hidden without losing other filters", () => { + expect(normalizeIssueFilterState({ hideRoutineExecutions: false, statuses: ["todo"] })).toMatchObject({ showAgentCreatedTasks: false, statuses: ["todo"] }); + expect(normalizeIssueFilterState({ showAgentCreatedTasks: "true" }).showAgentCreatedTasks).toBe(false); + expect(normalizeIssueFilterState({ showAgentCreatedTasks: true }).showAgentCreatedTasks).toBe(true); + }); + it("searches filter options case-insensitively without mutating the source", () => { const options = [{ name: "Alpha Agent" }, { name: "Release QA" }]; expect(searchIssueFilterOptions(options, " qa ", (option) => option.name)).toEqual([ diff --git a/ui/src/lib/issue-filters.ts b/ui/src/lib/issue-filters.ts index ce6178102a3..0e7c0c16b98 100644 --- a/ui/src/lib/issue-filters.ts +++ b/ui/src/lib/issue-filters.ts @@ -1,4 +1,4 @@ -import type { ExternalObjectSummary, Issue } from "@paperclipai/shared"; +import { isAgentCreatedIssue, type ExternalObjectSummary, type Issue } from "@paperclipai/shared"; export type IssueFilterWorkspaceLookup = { mode?: string | null; @@ -36,7 +36,7 @@ export type IssueFilterState = { * - `none` — issues with zero external objects */ externalObjectStatuses: string[]; - hideRoutineExecutions: boolean; + showAgentCreatedTasks: boolean; }; export const defaultIssueFilterState: IssueFilterState = { @@ -49,7 +49,7 @@ export const defaultIssueFilterState: IssueFilterState = { workspaces: [], liveOnly: false, externalObjectStatuses: [], - hideRoutineExecutions: false, + showAgentCreatedTasks: false, }; export const externalObjectFilterOrder = [ @@ -115,7 +115,7 @@ export function normalizeIssueFilterState(value: unknown): IssueFilterState { workspaces: normalizeIssueFilterValueArray(candidate.workspaces), liveOnly: candidate.liveOnly === true, externalObjectStatuses: normalizeIssueFilterValueArray(candidate.externalObjectStatuses), - hideRoutineExecutions: candidate.hideRoutineExecutions === true, + showAgentCreatedTasks: candidate.showAgentCreatedTasks === true, }; } @@ -210,7 +210,7 @@ export function applyIssueFilters( issues: Issue[], state: IssueFilterState, currentUserId?: string | null, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, liveIssueIds?: ReadonlySet, workspaceContext: IssueFilterWorkspaceContext = {}, ): Issue[] { @@ -218,8 +218,8 @@ export function applyIssueFilters( if (state.liveOnly) { result = result.filter((issue) => liveIssueIds?.has(issue.id) === true); } - if (enableRoutineVisibilityFilter && state.hideRoutineExecutions) { - result = result.filter((issue) => issue.originKind !== "routine_execution"); + if (enableAgentCreatedVisibilityFilter && !state.showAgentCreatedTasks) { + result = result.filter((issue) => !isAgentCreatedIssue(issue)); } if (state.statuses.length > 0) result = result.filter((issue) => state.statuses.includes(issue.status)); if (state.priorities.length > 0) result = result.filter((issue) => state.priorities.includes(issue.priority)); @@ -269,7 +269,7 @@ export function applyIssueFilters( export function countActiveIssueFilters( state: IssueFilterState, - enableRoutineVisibilityFilter = false, + enableAgentCreatedVisibilityFilter = false, ): number { let count = 0; if (state.statuses.length > 0) count += 1; @@ -281,6 +281,6 @@ export function countActiveIssueFilters( if (state.workspaces.length > 0) count += 1; if (state.liveOnly) count += 1; if (state.externalObjectStatuses.length > 0) count += 1; - if (enableRoutineVisibilityFilter && state.hideRoutineExecutions) count += 1; + if (enableAgentCreatedVisibilityFilter && state.showAgentCreatedTasks) count += 1; return count; } diff --git a/ui/src/lib/issue-hierarchy-context.test.ts b/ui/src/lib/issue-hierarchy-context.test.ts new file mode 100644 index 00000000000..1d63fa27689 --- /dev/null +++ b/ui/src/lib/issue-hierarchy-context.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import type { Issue } from "@paperclipai/shared"; +import { includeIssueHierarchyContext, loadIssueHierarchyChildren } from "./issue-hierarchy-context"; + +const task = (id: string, parentId: string | null = null) => ({ id, parentId, companyId: "company" } as Issue); + +describe("task hierarchy context", () => { + it("retains ancestors and descendants without promoting siblings or orphans", () => { + const match = task("human", "agent-parent"); + const context = [task("agent-parent", "grandparent"), task("grandparent"), task("agent-child", "human"), task("grandchild", "agent-child"), task("unrelated"), task("orphan", "missing"), task("sibling", "agent-parent")]; + expect(includeIssueHierarchyContext([match], context).map((issue) => issue.id)).toEqual([ + "human", "agent-parent", "grandparent", "agent-child", "grandchild", + ]); + expect(includeIssueHierarchyContext([], context)).toEqual([]); + }); + it("deduplicates matches and terminates cycles", () => { + const match = task("human", "parent"); + expect(includeIssueHierarchyContext([match], [match, task("parent", "human")]).map((issue) => issue.id)).toEqual(["human", "parent"]); + }); +}); + +describe("bounded hierarchy reads", () => { + it("caps rows and requests, and shares overlapping matching subtrees", async () => { + const root = task("root"); + const child = task("child", "root"); + const calls: string[] = []; + const read = async (id: string, limit: number, offset: number) => { + calls.push(id); + const rows = id === "root" ? [child] : id === "child" + ? Array.from({ length: 500 }, (_, index) => task(`nested-${index}`, "child")) : []; + return rows.slice(offset, offset + limit); + }; + const first = await loadIssueHierarchyChildren("company", [root, child], [], 5, read); + expect(first.issues).toHaveLength(5); + expect(first.hasMore).toBe(true); + expect(calls).toEqual(["root", "child"]); + calls.length = 0; + expect((await loadIssueHierarchyChildren("company", [root, child], [], 10, read)).issues).toHaveLength(10); + expect(calls).toEqual(["root", "child"]); + }); + it("probes collapsed parents without fetching their tree and rejects foreign-company rows", async () => { + const calls: Array<[string, number]> = []; + const rows = await loadIssueHierarchyChildren("company", [task("root")], ["root"], 5, + async (id, limit) => { calls.push([id, limit]); return [task("child", "root")]; }); + expect(rows.issues.map((issue) => issue.id)).toEqual(["child"]); + expect(calls).toEqual([["root", 1]]); + expect(await loadIssueHierarchyChildren("company", [task("root")], [], 1, + async () => [{ ...task("foreign", "root"), companyId: "other" }])).toEqual({ issues: [], hasMore: true }); + }); +}); diff --git a/ui/src/lib/issue-hierarchy-context.ts b/ui/src/lib/issue-hierarchy-context.ts new file mode 100644 index 00000000000..2dc328e1a62 --- /dev/null +++ b/ui/src/lib/issue-hierarchy-context.ts @@ -0,0 +1,64 @@ +import type { Issue } from "@paperclipai/shared"; + +/** Context never promotes an unrelated or orphaned automated task to a result. */ +export function includeIssueHierarchyContext(matches: Issue[], context: Issue[]): Issue[] { + const byId = new Map([...context, ...matches].map((issue) => [issue.id, issue])); + const result = new Map(matches.map((issue) => [issue.id, issue])); + for (const match of matches) { + const visited = new Set([match.id]); + let parentId = match.parentId; + while (parentId && !visited.has(parentId)) { + visited.add(parentId); + const parent = byId.get(parentId); + if (!parent) break; + result.set(parent.id, parent); + parentId = parent.parentId; + } + } + // Descendants are rooted at matches, not at contextual ancestors. + const descendantIds = new Set(matches.map((issue) => issue.id)); + let changed = true; + while (changed) { + changed = false; + for (const issue of context) { + if (!descendantIds.has(issue.id) && issue.parentId && descendantIds.has(issue.parentId)) { + descendantIds.add(issue.id); + result.set(issue.id, issue); + changed = true; + } + } + } + return [...result.values()]; +} + +/** Fetch each parent once and stop at the collection's current rendering budget. */ +export async function loadIssueHierarchyChildren( + companyId: string, + matches: Issue[], + collapsedParents: string[], + rowBudget: number, + readChildren: (parentId: string, limit: number, offset: number) => Promise, +): Promise<{ issues: Issue[]; hasMore: boolean }> { + const context: Issue[] = []; + const queue = matches.map((issue) => ({ issue, offset: 0 })); + const visited = new Set(); + const fetched = new Set(); + let requests = 0; + while (queue.length > 0 && context.length < rowBudget && requests < rowBudget) { + const { issue, offset } = queue.shift()!; + if (offset === 0 && visited.has(issue.id)) continue; + visited.add(issue.id); + const collapsed = collapsedParents.includes(issue.id); + const limit = collapsed ? 1 : Math.min(100, rowBudget - context.length); + const rows = await readChildren(issue.id, limit, offset); + requests += 1; + for (const child of rows) { + if (child.companyId !== companyId || fetched.has(child.id)) continue; + fetched.add(child.id); + context.push(child); + if (!collapsed) queue.push({ issue: child, offset: 0 }); + } + if (!collapsed && rows.length === limit) queue.unshift({ issue, offset: offset + rows.length }); + } + return { issues: context, hasMore: queue.length > 0 }; +} diff --git a/ui/src/lib/task-collection-preferences.ts b/ui/src/lib/task-collection-preferences.ts index 858d1cc44da..4df34bf790c 100644 --- a/ui/src/lib/task-collection-preferences.ts +++ b/ui/src/lib/task-collection-preferences.ts @@ -1,3 +1,5 @@ +export const TASK_COLLECTION_PREFERENCES_CHANGED = "paperclip:task-collection-preferences-changed"; + export const TASK_COLLECTION_PREFERENCES_VERSION = 1 as const; type StorageLike = Pick; @@ -79,6 +81,9 @@ export function saveTaskCollectionPreferences window.dispatchEvent(new Event(TASK_COLLECTION_PREFERENCES_CHANGED))); + } // Dual-write during migration so older builds using the same worktree keep // reading the latest presentation state. if (location.legacyViewStorageKey) { diff --git a/ui/src/pages/Inbox.tsx b/ui/src/pages/Inbox.tsx index 88ae042b349..972647bc8e1 100644 --- a/ui/src/pages/Inbox.tsx +++ b/ui/src/pages/Inbox.tsx @@ -1114,17 +1114,19 @@ function StreamlinedInbox({ ...queryKeys.issues.search(selectedCompanyId!, normalizedSearchQuery, undefined, 25), "compact", "inbox-supplement", + issueFilters.showAgentCreatedTasks, "live-descendant-summary", ], queryFn: () => issuesApi.listCompact(selectedCompanyId!, { q: normalizedSearchQuery, limit: 25, + includeAgentCreatedTasks: issueFilters.showAgentCreatedTasks, + includeParticipatedTasks: true, includeRoutineExecutions: true, includeLiveDescendantSummary: true, }).then((rows) => rows as Issue[]), enabled: shouldUseIssueSearchSupplement, - placeholderData: (previousData) => previousData, }); const liveIssueIds = useMemo( () => collectLiveIssueIds(liveRuns, [ @@ -1155,11 +1157,11 @@ function StreamlinedInbox({ externalObjectSummariesReady: externalObjectSummariesReady && !externalObjectSummariesLoading, }), [externalObjectSummariesLoading, externalObjectSummariesReady, externalObjectSummaryByIssueId]); const visibleMineIssues = useMemo( - () => applyIssueFilters(mineIssues, issueFilters, currentUserId, true, liveIssueIds, issueFilterContext), + () => applyIssueFilters(mineIssues, issueFilters, currentUserId, false, liveIssueIds, issueFilterContext), [mineIssues, issueFilters, currentUserId, liveIssueIds, issueFilterContext], ); const visibleTouchedIssues = useMemo( - () => applyIssueFilters(touchedIssues, issueFilters, currentUserId, true, liveIssueIds, issueFilterContext), + () => applyIssueFilters(touchedIssues, issueFilters, currentUserId, false, liveIssueIds, issueFilterContext), [touchedIssues, issueFilters, currentUserId, liveIssueIds, issueFilterContext], ); const unreadTouchedIssues = useMemo( @@ -1466,7 +1468,7 @@ function StreamlinedInbox({ remoteIssues: remoteIssueSearchResults, issueFilters, currentUserId, - enableRoutineVisibilityFilter: true, + enableAgentCreatedVisibilityFilter: false, liveIssueIds, issueFilterContext, }), @@ -2485,7 +2487,7 @@ function StreamlinedInbox({ labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" iconOnly presentation={streamlinedUiEnabled ? "streamlined" : "legacy"} @@ -2590,7 +2592,7 @@ function StreamlinedInbox({ labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" iconOnly presentation={streamlinedUiEnabled ? "streamlined" : "legacy"} diff --git a/ui/src/pages/Issues.tsx b/ui/src/pages/Issues.tsx index 0653d275848..ac11e1ba02f 100644 --- a/ui/src/pages/Issues.tsx +++ b/ui/src/pages/Issues.tsx @@ -1,3 +1,4 @@ +import { useShowAgentCreatedTasks } from "../hooks/useShowAgentCreatedTasks"; import { useEffect, useMemo, useCallback, useRef, useState } from "react"; import type { ReactNode } from "react"; import { useLocation, useNavigate, useSearchParams } from "@/lib/router"; @@ -179,6 +180,7 @@ function OrganizationIssues({ } return urlSearch; }, [searchOverride, urlSearch, location.search]); + const showAgentCreatedTasks = useShowAgentCreatedTasks(selectedCompanyId, "paperclip:issues-view"); const participantAgentId = searchParams.get("participantAgentId") ?? undefined; const initialWorkspaces = searchParams.getAll("workspace").filter((workspaceId) => workspaceId.length > 0); const workspaceIdFilter = initialWorkspaces.length === 1 ? initialWorkspaces[0] : undefined; @@ -254,6 +256,7 @@ function OrganizationIssues({ workspaceIdFilter ?? "__all__", "compact", "with-routine-executions", + showAgentCreatedTasks, "infinite", issuePageSize, ], @@ -261,6 +264,7 @@ function OrganizationIssues({ participantAgentId, workspaceId: workspaceIdFilter, includeRoutineExecutions: true, + includeAgentCreatedTasks: showAgentCreatedTasks, limit: issuePageSize, offset: pageParam, sortField: "updated", @@ -323,7 +327,7 @@ function OrganizationIssues({ toolbarContext={toolbarContext} initialSearch={syncedSearch} onSearchChange={handleSearchChange} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter hasMoreIssues={hasMoreServerIssues} onLoadMoreIssues={loadMoreServerIssues} onUpdateIssue={(id, data) => updateIssue.mutate({ id, data })} diff --git a/ui/src/pages/LegacyInbox.tsx b/ui/src/pages/LegacyInbox.tsx index 21d1a885e15..b925ac6d2ef 100644 --- a/ui/src/pages/LegacyInbox.tsx +++ b/ui/src/pages/LegacyInbox.tsx @@ -974,17 +974,19 @@ export function Inbox() { ...queryKeys.issues.search(selectedCompanyId!, normalizedSearchQuery, undefined, 25), "compact", "inbox-supplement", + issueFilters.showAgentCreatedTasks, "live-descendant-summary", ], queryFn: () => issuesApi.listCompact(selectedCompanyId!, { q: normalizedSearchQuery, limit: 25, + includeAgentCreatedTasks: issueFilters.showAgentCreatedTasks, + includeParticipatedTasks: true, includeRoutineExecutions: true, includeLiveDescendantSummary: true, }).then((rows) => rows as Issue[]), enabled: shouldUseIssueSearchSupplement, - placeholderData: (previousData) => previousData, }); const liveIssueIds = useMemo( () => collectLiveIssueIds(liveRuns, [ @@ -1015,11 +1017,11 @@ export function Inbox() { externalObjectSummariesReady: externalObjectSummariesReady && !externalObjectSummariesLoading, }), [externalObjectSummariesLoading, externalObjectSummariesReady, externalObjectSummaryByIssueId]); const visibleMineIssues = useMemo( - () => applyIssueFilters(mineIssues, issueFilters, currentUserId, true, liveIssueIds, issueFilterContext), + () => applyIssueFilters(mineIssues, issueFilters, currentUserId, false, liveIssueIds, issueFilterContext), [mineIssues, issueFilters, currentUserId, liveIssueIds, issueFilterContext], ); const visibleTouchedIssues = useMemo( - () => applyIssueFilters(touchedIssues, issueFilters, currentUserId, true, liveIssueIds, issueFilterContext), + () => applyIssueFilters(touchedIssues, issueFilters, currentUserId, false, liveIssueIds, issueFilterContext), [touchedIssues, issueFilters, currentUserId, liveIssueIds, issueFilterContext], ); const unreadTouchedIssues = useMemo( @@ -1321,7 +1323,7 @@ export function Inbox() { remoteIssues: remoteIssueSearchResults, issueFilters, currentUserId, - enableRoutineVisibilityFilter: true, + enableAgentCreatedVisibilityFilter: false, liveIssueIds, issueFilterContext, }), @@ -2350,7 +2352,7 @@ export function Inbox() { labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" iconOnly /> @@ -2448,7 +2450,7 @@ export function Inbox() { labels={labels?.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId={currentUserId} enableExternalObjectFilters={externalObjectsEnabled} - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" iconOnly /> diff --git a/ui/src/pages/ProjectDetail.tsx b/ui/src/pages/ProjectDetail.tsx index 8ce3fe22b7a..c65a04d77d7 100644 --- a/ui/src/pages/ProjectDetail.tsx +++ b/ui/src/pages/ProjectDetail.tsx @@ -1,3 +1,4 @@ +import { useShowAgentCreatedTasks } from "../hooks/useShowAgentCreatedTasks"; import { useCallback, useEffect, useMemo, useState, useRef } from "react"; import { Link, useParams, useNavigate, useLocation, Navigate } from "@/lib/router"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; @@ -186,6 +187,7 @@ function ProjectTilePicker({ /* ── List (issues) tab content ── */ function ProjectIssuesList({ projectId, companyId }: { projectId: string; companyId: string }) { + const showAgentCreatedTasks = useShowAgentCreatedTasks(companyId, "paperclip:project-issues-view"); const queryClient = useQueryClient(); const { data: agents } = useQuery({ @@ -218,8 +220,8 @@ function ProjectIssuesList({ projectId, companyId }: { projectId: string; compan }); const { data: issues, isLoading, error } = useQuery({ - queryKey: queryKeys.issues.listByProject(companyId, projectId), - queryFn: () => issuesApi.list(companyId, { projectId }), + queryKey: [...queryKeys.issues.listByProject(companyId, projectId), showAgentCreatedTasks], + queryFn: () => issuesApi.listAll(companyId, { projectId, includeRoutineExecutions: true, includeAgentCreatedTasks: showAgentCreatedTasks }), enabled: !!companyId, }); const liveIssueIds = useMemo(() => collectLiveIssueIds(liveRuns, issues), [issues, liveRuns]); @@ -242,6 +244,7 @@ function ProjectIssuesList({ projectId, companyId }: { projectId: string; compan projects={projects} liveIssueIds={liveIssueIds} projectId={projectId} + enableAgentCreatedVisibilityFilter viewStateKey="paperclip:project-issues-view" onUpdateIssue={(id, data) => updateIssue.mutate({ id, data })} /> diff --git a/ui/storybook/generated/feature-index.json b/ui/storybook/generated/feature-index.json index b7d02a273be..88bf72fdfc0 100644 --- a/ui/storybook/generated/feature-index.json +++ b/ui/storybook/generated/feature-index.json @@ -171,7 +171,7 @@ { "id": "inbox-search", "title": "Inbox, decisions, and search", - "description": "Personal triage, decision queues, unread/blocked views, and search.", + "description": "Personal triage, decision queues, unread/blocked views, search, and personal task toasts.", "capabilities": [ { "id": "inbox-search/views", @@ -192,6 +192,11 @@ "id": "inbox-search/search", "label": "Search", "description": "query company work and follow a result to its original record." + }, + { + "id": "inbox-search/toasts", + "label": "Toasts", + "description": "receive live task notifications only for personal participation or explicit mentions." } ] }, diff --git a/ui/storybook/stories/tasks/external-objects.stories.tsx b/ui/storybook/stories/tasks/external-objects.stories.tsx index 7e45ad9b13d..2a0069bc1ac 100644 --- a/ui/storybook/stories/tasks/external-objects.stories.tsx +++ b/ui/storybook/stories/tasks/external-objects.stories.tsx @@ -647,7 +647,7 @@ function FilterPopoverWithExternalChecked() { projects={storybookProjects.map((project) => ({ id: project.id, name: project.name }))} labels={storybookIssueLabels.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId="user-board" - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" creators={[ { id: "user:user-board", label: "Riley Board", kind: "user", searchText: "board user human" }, diff --git a/ui/storybook/stories/tasks/issue-management.stories.tsx b/ui/storybook/stories/tasks/issue-management.stories.tsx index ae060fdb15d..e147873165a 100644 --- a/ui/storybook/stories/tasks/issue-management.stories.tsx +++ b/ui/storybook/stories/tasks/issue-management.stories.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useRef, useState } from "react"; import type { Meta, StoryObj } from "@storybook/react-vite"; +import { creationVisibilityStories } from "../../support/issue-creation-visibility"; import type { Issue, IssueLabel, Project } from "@paperclipai/shared"; import type { RunForIssue } from "@/api/activity"; import { useQueryClient } from "@tanstack/react-query"; @@ -594,7 +595,7 @@ function OpenFiltersPopover() { projects={storybookProjects.map((project) => ({ id: project.id, name: project.name }))} labels={storybookIssueLabels.map((label) => ({ id: label.id, name: label.name, color: label.color }))} currentUserId="user-board" - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter buttonVariant="outline" creators={[ { id: "user:user-board", label: "Riley Board", kind: "user", searchText: "board user human" }, @@ -771,7 +772,7 @@ function IssueManagementStories() { viewStateKey={issueListViewKey} onUpdateIssue={() => undefined} createIssueLabel="issue" - enableRoutineVisibilityFilter + enableAgentCreatedVisibilityFilter /> @@ -939,3 +940,20 @@ export const IssuePropertiesRelationshipBadgesInline: Story = { name: "IssueProperties - relationship badges inline", render: () => , }; + + +export const AgentCreatedTasksHidden: Story = { + ...creationVisibilityStories.hidden, + name: "Agent-created tasks — hidden by default", + play: creationVisibilityStories.hidden.play, +}; +export const AgentCreatedTasksIncluded: Story = { + ...creationVisibilityStories.included, + name: "Agent-created tasks — explicit opt-in", + play: creationVisibilityStories.included.play, +}; +export const AgentCreatedTasksFilter: Story = { + ...creationVisibilityStories.filter, + name: "Agent-created tasks — filter control", + play: creationVisibilityStories.filter.play, +}; diff --git a/ui/storybook/support/issue-creation-visibility.tsx b/ui/storybook/support/issue-creation-visibility.tsx new file mode 100644 index 00000000000..1c6f81cd3e4 --- /dev/null +++ b/ui/storybook/support/issue-creation-visibility.tsx @@ -0,0 +1,103 @@ +import { useState } from "react"; +import type { StoryObj } from "@storybook/react-vite"; +import { expect, within } from "storybook/test"; +import { useQueryClient } from "@tanstack/react-query"; +import { IssuesList } from "@/components/IssuesList"; +import { defaultIssueFilterState } from "@/lib/issue-filters"; +import { saveTaskCollectionPreferences } from "@/lib/task-collection-preferences"; +import { queryKeys } from "@/lib/queryKeys"; +import { createIssue, storybookAgents, storybookAuthSession, storybookCompanies, storybookProjects } from "../fixtures/paperclipData"; + +const companyId = "company-storybook"; +type Story = StoryObj; + +function hydrateStorybookQueries(queryClient: ReturnType) { + queryClient.setQueryData(queryKeys.companies.all, { companies: storybookCompanies, unauthorized: false }); + queryClient.setQueryData(queryKeys.auth.session, storybookAuthSession); + queryClient.setQueryData(queryKeys.agents.list(companyId), storybookAgents); + queryClient.setQueryData(queryKeys.projects.list(companyId), storybookProjects); + queryClient.setQueryData(queryKeys.instance.experimentalSettings, { enableIsolatedWorkspaces: false, enableStreamlinedUi: true }); +} + + +const creationVisibilityTasks = [ + createIssue({ id: "visibility-human", identifier: "PAP-1701", title: "Human request", createdByUserId: "user-board", createdByAgentId: null }), + createIssue({ id: "visibility-agent", identifier: "PAP-1702", title: "Agent root task", createdByUserId: null, createdByAgentId: "agent-codex" }), + createIssue({ id: "visibility-channel", identifier: "PAP-1707", title: "Chat channel task", originKind: "chat_channel", createdByUserId: "user-board", createdByAgentId: null }), + createIssue({ id: "visibility-routine", identifier: "PAP-1703", title: "Scheduled routine run", originKind: "routine_execution", createdByUserId: null, createdByAgentId: null, responsibleUserId: "user-board" }), + createIssue({ id: "visibility-child", identifier: "PAP-1704", title: "Agent subtask", parentId: "visibility-human", createdByUserId: null, createdByAgentId: "agent-codex" }), + createIssue({ id: "visibility-ancestor", identifier: "PAP-1705", title: "Agent ancestor", createdByUserId: null, createdByAgentId: "agent-codex" }), + createIssue({ id: "visibility-human-child", identifier: "PAP-1706", title: "Human follow-up", parentId: "visibility-ancestor", createdByUserId: "user-board", createdByAgentId: null }), +].map((issue) => ({ ...issue, status: "todo" as const, checkoutRunId: null, executionRunId: null, executionAgentNameKey: null, executionLockedAt: null, executionWorkspaceId: null, currentExecutionWorkspace: null, startedAt: null, labelIds: [], labels: [] })); + +function CreationVisibilityStory({ included = false }: { included?: boolean }) { + const queryClient = useQueryClient(); + const viewStateKey = `storybook:agent-created-tasks:${included ? "included" : "hidden"}`; + useState(() => { + hydrateStorybookQueries(queryClient); + saveTaskCollectionPreferences({ companyId, collectionKey: viewStateKey, legacyViewStorageKey: `${viewStateKey}:${companyId}` }, { + viewState: { ...defaultIssueFilterState, showAgentCreatedTasks: included, viewMode: "list", groupBy: "none", sortField: "updated", sortDir: "desc", nestingEnabled: true, collapsedParents: [], collapsedGroups: [] }, + columns: ["status", "id", "assignee", "updated"], + }); + const listKey = queryKeys.issues.list(companyId); + const ancestor = creationVisibilityTasks.find((issue) => issue.id === "visibility-ancestor")!; + queryClient.setQueryData([...listKey, "hierarchy-ancestors", ancestor.id], [ancestor]); + queryClient.setQueryData([...listKey, "hierarchy-children", ["visibility-human", "visibility-human-child"], [], 100], { issues: [creationVisibilityTasks.find((issue) => issue.id === "visibility-child")!], hasMore: false }); + return true; + }); + return ( +
+ undefined} + /> +
+ ); +} + +const hidden: Story = { + name: "Agent-created tasks — hidden by default", + render: () => , + parameters: { docs: { description: { story: "Production task collection with simulated creation metadata and hierarchy reads. Agent and routine roots are hidden; agent subtasks and ancestors remain as context. Server participation and pagination are covered separately." } } }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(await canvas.findByText("Human request")).toBeVisible(); + await expect(canvas.getByText("Agent subtask")).toBeVisible(); + await expect(canvas.getByText("Agent ancestor")).toBeVisible(); + await expect(canvas.queryByText("Agent root task")).not.toBeInTheDocument(); + await expect(canvas.queryByText("Scheduled routine run")).not.toBeInTheDocument(); + await expect(canvas.queryByText("Chat channel task")).not.toBeInTheDocument(); + }, +}; + +const included: Story = { + name: "Agent-created tasks — explicit opt-in", + render: () => , + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(await canvas.findByText("Agent root task")).toBeVisible(); + await expect(canvas.getByText("Scheduled routine run")).toBeVisible(); + await expect(canvas.getByText("Chat channel task")).toBeVisible(); + }, +}; + +const filter: Story = { + name: "Agent-created tasks — filter control", + render: () => , + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(await canvas.findByText("Human request")).toBeVisible(); + canvas.getByTitle("Filter").click(); + const page = within(canvasElement.ownerDocument.body); + await expect(await page.findByRole("checkbox", { name: /show agent-created tasks/ })).not.toBeChecked(); + }, +}; + +export const creationVisibilityStories = { hidden, included, filter };