Skip to content

Fix workflow document picker loading and make workspace documents per-task - #1284

Merged
Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-workflow-task-documents
Aug 18, 2026
Merged

Fix workflow document picker loading and make workspace documents per-task#1284
Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-workflow-task-documents

Conversation

@paullizer

Copy link
Copy Markdown
Collaborator

Fixes #1282

Problem

Two related problems in the workflow builder (personal and group workspaces), on the Tasks step.

1. The workspace document picker never loaded

Choosing a Document action of Search, Analyze, or Compare revealed the picker but it never initialized — Tags stayed disabled on Loading tags... forever, the Document dropdown stayed empty, and there were no console errors. Since the picker is the only way to choose documents, document-backed workflows could not be created or edited at all, with no workaround.

Root cause: initializeWorkflowDocumentPicker() is the only caller of setEffectiveScopes() / ensureDocumentPickerReady(). It ran only from openWorkflowModal(), and returned early whenever the action type was none — always true for a new workflow. The change handler on #workflow-document-action-type was updateDocumentActionFields(), which only toggles visibility. So nothing ever ran, and the tags button simply kept its static markup. Nothing threw, which is why the console stayed clean.

Clicking Refresh selected documents then warned Select one or more <workspace|group> documents in the picker first. — a downstream symptom, since the empty picker offered nothing to select.

2. Workspace documents were workflow-level, not per-task

The Workspace documents card lives in the Tasks step but was bound to the workflow. It did not reset when adding a task or restore when switching back to a configured one. At run time, _execute_workflow_task_sequence() passed include_document_action=task_index == 0, so only task 1 ever received the document action and tasks 2..N always ran with {'type': 'none'}.

Changes

Picker lifecycle

  • New handleWorkflowDocumentActionSelectionChanged() bound to both #workflow-document-action-type and #workflow-analysis-target-mode (the picker card is hidden in Recent documents mode and must reload when switching back).
  • New ensureWorkflowDocumentPickerLoaded(options) drives loads from the live form state.
  • workflowDocumentPickerLoadToken guards overlapping loads so a stale run cannot apply its scopes or selection, or strand the loading flag.
  • loadTagsForScope() now resolves the tags control instead of returning early, so it can never be left in the initial Loading tags... markup.

Refresh button

applySelectedWorkspaceDocumentsToWorkflow() reloads the picker (preserving the current selection) before applying it, and reports what it actually observed — including a distinct message when the scope has no documents. Relabeled Refresh selected documentsRefresh documents in both templates.

Per-task workspace documents

Every task now owns its full document configuration: action type, target mode, recent window, picker selection, Compare source/targets, per-document toggle, window unit/size/percent, and retries.

  • Frontend: createDefaultWorkflowTaskDocumentAction(), normalizeWorkflowTaskDocumentAction(), readWorkflowDocumentActionFromForm(), applyWorkflowDocumentActionToForm(), serializeWorkflowDocumentAction(), buildWorkflowAnalyzeConfig(), validateWorkflowTaskDocumentAction(). syncActiveWorkflowTaskFromEditor() writes the task's action, populateWorkflowTaskEditor() restores it. Validation is per task with task-numbered messages that select the offending task. Task cards and the Review step summarize each task's documents.
  • Backend: _normalize_workflow_tasks() gained task_document_action_normalizer and default_document_action (same injectable-callable style already used for task runners). save_personal_workflow() / save_group_workflow() now compute file_sync and the workflow-level action before tasks. Group workflows wrap the normalizer with _apply_group_document_action_scope(), so no task can widen scope beyond the owning group.
  • Runner: _resolve_workflow_task_document_action() uses the task's own action, and _apply_file_sync_context_to_workflow() now rewrites task-level analyze actions with File Sync changed documents.

Backward compatibility

  • Opening a pre-existing workflow hydrates its workflow-level document_action onto task 1 only, matching how it actually executed.
  • Executing a pre-existing record without re-saving falls back to the workflow-level action for task index 0 and none afterwards.
  • The workflow record still carries a top-level document_action / analyze, mirrored from the first task whose action is not none, so the workflow list summary, run-resume path, and existing API consumers are unchanged.
  • Omitting tasks from a save payload leaves stored tasks untouched, so legacy records are not silently rewritten.

Coupled fixes

A code-review pass found four issues that only became reachable once more than one task could carry documents. All four are fixed here:

  • An invalid task document action aborted the whole run. _build_workflow_task_execution_workflow() now normalizes inside the retry attempt, so a stored action that stops validating (admin disables Analyze/Compare, or lowers the document limit) fails that task through the normal retry and failure strategy and is recorded as a failed run item, instead of killing the run with no task-level error.
  • Two tasks sharing a document overwrote each other's run history. Document run item ids are now task-scoped ({run_id}:task:{task_id}:document:{doc_id}) and record task_id / task_name, so a document that failed in one task is no longer displayed as succeeded because a later task processed it. Legacy single-dispatch runs keep the original id.
  • Resume failed items only narrowed the workflow-level action, which tasks now override. It narrows each task's analyze action to the documents that failed in that task, downgrades analyze tasks with no failures to none, falls back to the full failed set for run items recorded before task scoping, and re-forces group scope per task on group resumes.
  • A stored Retries Per Window of 0 was rewritten to 1 on any task other than the one being edited, because normalizeText() coerces numeric 0 to an empty string. Fixed with an explicit nullish helper.

Validation

  • New functional_tests/test_workflow_task_document_actions.py — 16/16 passing. Covers the picker load wiring and token guard, the refresh contract, the per-task frontend model, backend normalization with legacy task-1 inheritance, task-numbered validation errors, group scoping, per-task runner execution and the legacy fallback, File Sync propagation, task-scoped run items, retry containment, resume-failed narrowing, and zero-retries preservation.
  • functional_tests/test_workflow_task_sequence.py — 10/10. Extended for the new runner helper; its exact-version assertion was replaced with assert_app_version_at_least per the repo's versioning instructions (it was already failing on Development).
  • Updated two tests whose brittle string assertions targeted the inline payload code this PR refactored: test_workflow_document_picker_recent_targets.py and test_workflow_per_document_analysis_mode.py. Both are back to their baseline pass counts.
  • A headless jsdom harness drove the real modal markup for both workspace.html and group_workspaces.html and confirmed end to end that choosing Search resolves the tags control and populates the document list, that adding a task resets the fields and returning restores them, and that Refresh documents preserves the selection without warning.
  • Full test_workflow* sweep: 21 failures, byte-identical to the pre-change baseline (verified against a clean git archive export of the base commit) — one previously failing file now passes and none regressed.
  • functional_tests/route_tests/ all pass. No routes were added or changed.

Version

0.250.2240.250.225, with fix documentation, feature documentation, index entries, and release notes.

Paul Lizer (paullizer) and others added 2 commits August 18, 2026 15:35
…-task

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…k, fix zero retries

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer

Copy link
Copy Markdown
Collaborator Author

Heads up for reviewers: this PR touches _apply_file_sync_context_to_workflow() and _build_workflow_task_execution_workflow(), and deliberately leaves a pre-existing File Sync bug in place rather than expanding scope.

_build_workflow_task_execution_workflow() reads workflow['file_sync_prompt_context'], which nothing in the codebase writes. The producer line was added alongside the task-sequence feature in 79148c84 and later lost in a merge, so the File Sync prompt context never reaches the model in any task-based workflow. Filed separately as #1285 with the full trace.

Also noted while working here, not fixed: getSelectedWorkspaceDocumentIds() in workspace_workflows.js is dead code, and ui_tests/test_workflow_document_action_modal.py still primes window.selectedDocuments, which nothing reads.

@paullizer
Paul Lizer (paullizer) merged commit 06cc0a9 into Development Aug 18, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant