refactor(work-items): split human assignee from execution target - #874
Draft
Harry19081 wants to merge 1 commit into
Draft
refactor(work-items): split human assignee from execution target#874Harry19081 wants to merge 1 commit into
Harry19081 wants to merge 1 commit into
Conversation
The assignee field had two meanings at once: a roster person, or an agent definition / agent organization that would execute the item. The picker offered all three in one list, and both creation paths copied whichever the draft carried straight into the persisted `assignee` / `assigneeType` columns — so an execution choice became an identity claim on the row. Assignment now means a human. Execution targeting lives where it already belonged, in `orchestratorConfig.agent_definition_id` / `org_id`. - New `humanAssignee.ts` boundary: manual and AI creation both route draft assignment through `resolveHumanAssigneeWrite`, which canonicalizes the legacy `member` spelling to `human` and drops agent/org identities instead of persisting them. - `AssigneePropertyField` takes `Person[]` only; the agent and organization groups (and their `agentsGroup` / `orgsGroup` strings) are gone, along with the `availableAgents` / `availableOrgs` / `allAgentList` prop chain through `PeopleSection`, `WorkItemProperties`, `WorkItemDetailBody`, and `InlineCreateWorkItemFields`. - `useAiWorkItemCreator` resolves an execution target from orchestrator config first, then creator selection, then the OS Agent fallback — renamed throughout to `executionTarget` so the two concepts stop sharing vocabulary. Auto-execute now gates on a configured target rather than on `assigneeType`. Existing rows that already carry an agent or org assignee still render; they are simply no longer offered as new assignment options. Historical cleanup is a separate, confirmed remediation task. Pre-commit hook ran. Total eslint: 4, total circular: 0
Harry19081
marked this pull request as draft
August 22, 2026 15:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Work Item
assigneefield meant two different things at once — a roster person, or the agent definition / agent organization that would execute the item. This makes assignment mean a human, and leaves execution targeting where it already belonged:orchestratorConfig.agent_definition_id/org_id.Problem
AssigneePropertyFieldoffered members, agents, and agent orgs in one dropdown, tagging the choice withassigneeType: "human" | "agent" | "org". Both creation paths then copied whatever the draft carried straight into the persisted row:So picking an executor wrote an identity claim onto the row, and auto-execute gated on
assigneeType === "agent" | "org"— meaning you could not run an item on an agent and have it assigned to the person who owns it. The two concepts also shared vocabulary (resolveAiWorkItemAssignee,defaultAiWorkItemAssignee), so the overload was invisible at every call site.Solution
humanAssignee.ts—resolveHumanAssigneeWriteacceptshuman(and the legacymemberspelling, canonicalized tohuman) and returns{}for agent/org identities. Both producing write paths,createWorkItemFromDraftanduseAiWorkItemCreator, spread its result instead of copying draft fields, so an execution identity can no longer reach theassigneecolumn from either direction.AssigneePropertyFieldtakesPerson[]only. The agent/org option groups and theiragentsGroup/orgsGroupstrings are gone, along with theavailableAgents/availableOrgs/allAgentListprop chain threaded throughPeopleSection,WorkItemProperties,WorkItemDetailBody, andInlineCreateWorkItemFields.handleAssigneeChangeloses itsassigneeTypeparameter —buildHumanAssigneeUpdatenow owns the type.useAiWorkItemCreatorresolves anexecutionTargetfrom orchestrator config first, then creator selection, then the OS Agent fallback — renamed throughout (defaultAiWorkItemExecutionTarget,defaultAiExecutionTarget) so the two concepts stop sharing names. Auto-execute gates on a configured target rather than onassigneeType, and the two toasts were renamed to match (chooseExecutionAgentAi,autoExecuteRequiresExecutionAgent, across all 13 locales).Rows that already carry an agent or org assignee still render — the icon branch for non-human identities is deliberately kept. They are simply no longer offered as new options. Historical cleanup is a separate, confirmed remediation task.
Validation / Test plan
pnpm typecheckclean across the repohumanAssignee.test.ts— canonicalizeshuman/member, rejectsagent/org/agent_org/undefinedcreateWorkItemFromDraft.test.ts— anagentdraft produces a request with noassignee/assigneeTypekey at alluseWorkItemPropertyHandlers.test.ts—buildHumanAssigneeUpdatewrites and clears identity + type togetherAssigneePropertyField.test.ts— the picker lists roster members and never agent or org identities, even when legacy props are still passedvitest run src/modules/ProjectManager/WorkItems/ src/engines/ChatPanel/— 206 files / 1363 tests passfrontend-ui-auditreport atdocs/frontend-ui-audit-2026-08-21/AssigneePropertyField.md— 5 fix / 2 keep-with-reason / 1 abstract, 0 cross-file sweep candidatesPotential risks
assigneeType === "agent", it will now see a shrinking set over time. Worth a look at the kanban swimlane and Work Item filter surfaces before merge.orchestratorConfig. An item whose agent was only ever expressed throughassigneeTypewill read as "no execution target" and block auto-execute until an agent is picked — correct, but a visible behavior change for anyone mid-flow.assigneeTypestill accepts the old values; this PR only closes the frontend write paths. A Rust-side validation follow-up would make the invariant total.