feat(platform): operate automation-owned tasks from the task board#2827
Open
larryro wants to merge 3 commits into
Open
feat(platform): operate automation-owned tasks from the task board#2827larryro wants to merge 3 commits into
larryro wants to merge 3 commits into
Conversation
One declaration, every surface: an automation can declare a subjects.task contract in its automation.json (workflow, folder input, template create, start gating, review), and the generic task surfaces consume it instead of desk-view-only config: - Task modal run actions: Start / Request changes / Cancel, gated by the contract's when-predicate over live facts (status, hasFiles) and the subject run — same startTaskWorkflow choreography as the desk. - The Attachments zone swaps to the bound folder's upload card for folder-input tasks, so input lands where the run actually reads it. - The create dialog gains contract templates (board-side New quarter) through the same createTaskFromExternalIssue upsert seam the desk uses — board- and desk-created subjects converge on ONE task. - App ownership decoupled from run-on-create: upsert + create accept automationSlug and stamp createdByType:'app' without starting a run; pre-stamp tasks resolve via a unique externalSystem match. - Task modal Outcome section on the shared surface:"outcome" projection (OutcomeRows extracted from the desk's OutcomeStrip). - Task comments render newest-first with the composer on top.
…flow
Drop the bolted-on Start/Cancel buttons: the board's NATIVE verbs are
the interface. A status change on an automation-owned task routes
through decideTaskStatusTransition (pure, unit-tested matrix):
- drag/set to In progress -> start the run (contract start.when over
{status, hasFiles}); when ONLY the input is missing, block with
feedback and snap the card back instead of writing a status the run
will never pick up
- In review -> In progress -> request changes (the rerun reads the
timeline feedback since the last anchor)
- leaving a running In progress -> cancel first, then land the chosen
column (cancel itself parks at Cancelled)
- everything else, and unowned tasks -> the plain status write
Wired at both native entry points: board drag (useTaskBoardDnd) and
the modal StatusPicker. The workflow's own ack step remains the only
status writer for choreographed transitions.
Close the discoverability gap the status choreography opened — special behavior must announce itself before it fires, from data, never product literals: - TaskAutomationBadge (icon on the board card, icon + name in the modal): the visible 'this task is operated' signal, name resolved through the owning manifest's i18n, tooltip explains the status choreography. - Status picker pre-flight hints: each option that would start / request changes / cancel says so inline, derived from plannedTransitionKind — the SAME matrix that executes the transition, so hint and behavior cannot drift. - FolderUploadCard anchors to its folder on contract surfaces: name in the title (Files — 2026Q3) plus an open-in-Knowledge link. - getTaskOpsIndicators unions subject-linked workflow executions into runningTaskIds, so a choreographed run pulses the board card's standard working indicator (active runs are capacity-bounded; the org-wide scan stays tiny).
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.
Problem
For automation-driven tasks (e.g. the VAT return desk), the task board was a second-class surface: the flow could only complete inside the automation's own desk view. From the board, a task like VAT return — 2026Q1 could not be started or re-run, dropped files landed in task Attachments (blobs the pipeline never reads — the run reads the quarter FOLDER), produced outcome files were invisible, and a board-created task became a zombie duplicate the desk's reconcile would never adopt. Root cause: the task-surface semantics lived only in view config (
views/desk.jsonwhen/args) plus implicit conventions (externalId= folder id), which only the desk view can read.Change
One declaration, every surface — and the board's NATIVE verbs stay the interface. Automations declare a
subjects.taskcontract inautomation.json(taskSubjectContractSchema:workflow,externalSystem, folderinput, templatecreate,start.when,review), exposed throughlistAutomationssummaries, and the generic task surfaces consume it:useTaskStatusChoreography): no bolted-on Start button — a status change on an owned task routes through a pure, unit-tested transition matrix (decideTaskStatusTransition), wired at both native entry points (board draguseTaskBoardDnd+ modal StatusPicker):start.whenover{status, hasFiles}); when ONLY the input is missing, block with feedback and snap the card back instead of writing a status the run would never pick upTaskSubjectFiles): for folder-input tasks the Attachments zone swaps to the bound folder'sFolderUploadCard(now accepts explicit org/project ids outside an automation runtime); legacy attachments still list below when present.create.enabledcontracts as task types (single contract field with per-locale label/help,namingregex validated inline, title derived viatitleTemplate). Submission goes through the SAMEcreateTaskFromExternalIssueupsert seam the desk's New-quarter button uses, so board- and desk-created subjects converge on one task by(externalSystem, externalId)— the zombie-duplicate failure mode is structurally impossible.agentUpsertTaskByExternalRef+createTaskFromExternalIssueacceptautomationSlugand stampcreatedByType:'app'/createdBy:<slug>WITHOUT starting a workflow (the desks' create-now-run-later shape;runWorkflowSlugderivation unchanged). Tasks born before stamping resolve via a UNIQUEexternalSystemmatch — ambiguity resolves to none.TaskOutcomeSection): the latest subject run'ssurface:"outcome"deliverables — documents preview, plain files link out, text renders as markdown, pending slots pulse — viaOutcomeRows, extracted from the desk'sOutcomeStrip(desk chrome/behavior unchanged).The vat-return-desk pack declares the contract in tale-project/configs (separate commits there; its desk New-quarter also passes
automationSlugnow). Manifest schema stays.passthrough()— packs with the declaration remain valid on older platforms.Verification
2026Q3created (folder + Setup binding +Author: vat-return-desk) → modal shows the folder card instead of Attachments → uploaded an invoice → set status to In progress → the run started and the workflow itself wrote the status (activity: "vat-return-desk: status changed Cancelled → In progress") → changed status to To do while running → run cancelled, task landed To do → empty-folder task (2026Q4) set to In progress → transition refused, status stayed Backlog.Definition of done
bun run lint:sastclean (0 findings)migrations:checkgreen:subjects+automationSlugargs are data-safe growth (new optional fields), no data migration; baseline snapshot deliberately not refreshed here (a refresh rewrites ~19k lines of pre-existing drift — separate maintenance change)tasks.create.*,tasks.run.*,tasks.detail.outcomein en/de/fr; parity suite greentaskSubjectContractSchemaaria-pressed, contract field wiresaria-invalid+ described-by help, pending outcome slots keeprole=status; status choreography rides the existing keyboard-reachable status controlsmain(contract surfaces; status-verb choreography)Follow-ups (out of scope): assignee policy (starter-as-assignee vs pack-declared), a task-choreographed
start_task_workflowagent tool (genericrun_workflowlacks subject binding), adoption of hand-created tasks into a contract, vat pack task-description copy still says "press Start on this row" (desk wording), config snapshot baseline refresh.