Tasks & Statuses
Tasks, the per-team triage queue, statuses, notes, and activity records in the Operation plugin.
Backend code lives under backend/plugins/operation_api/src/modules/{task,status,note,activity}/. The task module also contains the triage model and the task segment definitions in task/meta/segments/.
Task data model
operation_tasks (modules/task/db/definitions/task.ts):
| Field | Type | Notes |
|---|---|---|
name, description | string | name required |
status | ObjectId | references a team Status; required |
teamId | ObjectId | required; permission scope field |
priority, estimatePoint | number | priority defaults 0; estimatePoint defaults 0 |
assigneeId, createdBy | string | Core user ids (indexed, sparse) |
labelIds, tagIds | string[] | Core labels/tags |
cycleId, projectId, milestoneId | ObjectId | optional rollups |
startDate, targetDate, statusChangedDate | Date | statusChangedDate determines "completed" reporting |
number, statusType | number | statusType mirrors the status's type |
githubIssueNumber, githubIssueUrl, githubRepoName | set by the GitHub sync | |
propertiesData | mixed | operation:task property values, keyed by field id |
segmentIds | string[] | written only by the segmentation worker |
Task _id is an ObjectId
taskSchema is a plain new Schema(...) without schemaWrapper, so _id stays a Mongo ObjectId. Segment and tRPC code always goes through the Mongoose model so string ids cast correctly. Do not bypass it with a raw driver handle.
Statuses
Status documents are team-scoped ({ name, description, color, type, teamId, order }). type is one of STATUS_TYPES (status/constants/types.ts): STARTED=1, UNSTARTED=2, BACKLOG=3, COMPLETED=4, CANCELLED=5, TRIAGE=6. Creating a team seeds the five default statuses (backlog, todo, in progress, done, cancelled); see status/utils.ts.
Triage queue
Triage items (operation_triage) are inbox work for a team: { name, description, teamId, createdBy, priority, status, number } plus GitHub issue fields. operationConvertTriageToTask creates a task in the same team (optionally looking up status by status type), carries over GitHub linkage, stores the reason as a note on the new task, and deletes the triage record.
operationCancelTriage is not implemented
operationCancelTriage(_id) is declared in the triage GraphQL schema but has no resolver; calling it errors at runtime. To retire a triage item, update its status with operationUpdateTriage or convert it with operationConvertTriageToTask (which deletes the record).
Queries and mutations
| Operation | Kind | Permission |
|---|---|---|
getTasks(filter: ITaskFilter) / getTask(_id) | query | taskRead |
createTask(…) / updateTask(…) / removeTask(_id) | mutation | taskCreate / taskUpdate / taskRemove |
operationGetTriage(_id) / operationGetTriageList(filter) | query | triageRead |
operationAddTriage / operationUpdateTriage | mutation | triageCreate / triageUpdate |
operationConvertTriageToTask(_id, status: Int, reason) | mutation | triageConvert |
getStatus(_id) / getStatusesByType(type, teamId) / getStatusesChoicesByTeam(teamId) | query | statusRead |
addStatus / updateStatus / deleteStatus | mutation | statusCreate / statusUpdate / statusRemove |
getOperationActivities(contentId, …) | query | taskRead |
getNote(_id) / createNote / updateNote / deleteNote | query/mutation | noteRead / noteCreate / noteUpdate / noteRemove |
ITaskFilter accepts status, priority, assigneeId, createdBy, cycleId, projectId, milestoneId, teamId, labelIds, tagIds, estimatePoint, statusType, cycleFilter (noCycle, anyPastCycle, previousCycle, currentCycle, upcomingCycle, anyFutureCycle), projectStatus/projectPriority/projectLeadId, date bounds, and cursor pagination params.
Example createTask call:
mutation {
createTask(
name: "Prepare release checklist"
teamId: "team_1"
status: "status_todo"
assigneeId: "user_1"
priority: 3
estimatePoint: 5
) {
_id
name
}
}
Notes and activity
Note ({ content, contentId, createdBy, mentions, statusId }) attaches to a task or project by contentId; mentions trigger a note notification to the mentioned users, and removeNote is author-only. OperationActivity ({ action, contentId, module, metadata: {newValue, previousValue}, createdBy }) is the audit trail; subscribe to operationActivityChanged(contentId) for live updates.
Subscriptions
operationTaskChanged(_id), operationTaskListChanged(filter: ITaskFilter) (filtered server-side via withFilter), and operationActivityChanged(contentId) publish on create/update through graphqlPubsub.
tRPC
Merged in src/trpc/init-trpc.ts:
| Procedure | Type | Notes |
|---|---|---|
task.tag({ tagIds, targetIds, type, action }) | mutation | Bulk-set tagIds on tasks |
task.findOne({ _ids }) | query | First task among the ids ({ _id, name, teamId } or null); non-ObjectId ids skipped |
task.createFromSource({ userId, doc }) | mutation | Creates a task as userId; reads teamId from doc.status (must be a status ObjectId); publishes the task subscriptions; returns { _id } |
createFromSource is how frontline converts a conversation into a task. It checks no permission; the caller must enforce taskCreate for the acting user.
"Status not found" from createFromSource
doc.status must be a status _id (ObjectId), not a status type number. Only operationConvertTriageToTask accepts a type.
Segments and import/export
operation:task.tasksis a segment content type with 19 filterable fields, member listing/counting, materialisedsegmentIds, and relationsuser.assignedTasks/user.createdTasks(the field-joined relations rely on theassigneeId/createdByindexes). The content type must be exactlyoperation:task.tasks; the event dispatcher keys on it.- Import type
operation:task.task(taskImportManage) and export typesoperation:task.task/operation:project.project(taskExportManage/projectExportManage) are registered viameta/import-export. Repeating property groups export as numbered<Group> <n> / <Field>columns. - Notifications:
taskAssignee(task assigned) andtaskStatus(status changed).
Permissions
Modules task, triage, note, status (and project, cycle, milestone, team elsewhere) use scoped permissions: own (records you created), group (records in your teams via scopeField: 'teamId'), all. Read actions are always: true; write actions are grantable. taskAssign is a custom action for assignment control.