Skip to content

feat: issue #11 and issue#12#19

Open
keshxvdayal wants to merge 2 commits intomainfrom
feature/issue-11-12
Open

feat: issue #11 and issue#12#19
keshxvdayal wants to merge 2 commits intomainfrom
feature/issue-11-12

Conversation

@keshxvdayal
Copy link
Collaborator

@keshxvdayal keshxvdayal commented Jan 25, 2026

Issue #11 (SOLVED)

  • Solution -Added SUBMISSION_FAILED state for tasks that fail to submit
    Logged failed submissions to the task list with:
    Error message
    Error type (DNS, timeout, auth, etc.)
    Error code
    Error reason
    TES instance details
    Timestamp
    Captured both failure scenarios:
    -> Connectivity failures (can't reach the instance)
    -> HTTP errors (instance reachable but rejects task: 400, 403, 500, etc.)

issue #12 (SOLVED)

Solution -

  1. Backend Fix task_service.py
    Added 'SUBMISSION_FAILED' to the terminal_states list (line 89). This prevents the background task updater from trying to fetch status updates for failed submissions from the TES instance.

Why this matters: Tasks with SUBMISSION_FAILED state were never actually submitted to the TES instance, so when the background updater tried to fetch their status, it would fail or corrupt the task data, causing them to disappear from the list.

  1. Frontend Fix Tasks.js
    Removed the filter that was excluding error states (ERROR, SYSTEM_ERROR, EXECUTOR_ERROR) and the error_prone_instance check (lines 230-238). Now the filter only checks for essential fields (id, tes_url, state).

Implementation Screenshot:

Screenshot 2026-01-25 at 7 32 34 PM Screenshot 2026-01-25 at 7 33 18 PM

Summary by Sourcery

Handle failed TES task submissions as first-class tasks and ensure error states remain visible in the task list.

New Features:

  • Record failed TES task submissions as SUBMISSION_FAILED tasks with detailed error metadata for connectivity and HTTP failures.

Bug Fixes:

  • Normalize task command handling to correctly support both string and list commands and avoid demo tasks getting stuck.
  • Include SUBMISSION_FAILED in terminal task states to prevent background status polling of never-submitted tasks.
  • Show tasks in all error states on the Tasks page by removing filters that hid error and error-prone instances.

Copilot AI review requested due to automatic review settings January 25, 2026 14:08
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 25, 2026

Reviewer's Guide

Implements robust handling and surface visibility for TES task submission failures by logging failed submissions as SUBMISSION_FAILED tasks, exposing all error states in the UI, and preventing background status polling for tasks that never reached TES; also fixes command handling for the demo Python script task submission path.

Sequence diagram for TES task submission with SUBMISSION_FAILED handling

sequenceDiagram
    actor User
    participant FrontendTasksPage
    participant BackendTasksRoute
    participant TaskService
    participant TESInstance

    User->>FrontendTasksPage: Submit new task (command, tes_url, tes_name)
    FrontendTasksPage->>BackendTasksRoute: POST /tasks with task payload

    Note over BackendTasksRoute: Build executor command
    BackendTasksRoute->>BackendTasksRoute: Normalize command using shlex

    Note over BackendTasksRoute: Check TES service-info endpoints
    BackendTasksRoute->>TESInstance: GET /service-info (one or more endpoints)
    TESInstance-->>BackendTasksRoute: Connectivity failure for all endpoints

    alt No TES endpoint reachable
        BackendTasksRoute->>BackendTasksRoute: Create failed_task with state SUBMISSION_FAILED
        BackendTasksRoute->>TaskService: add_task(failed_task)
        BackendTasksRoute-->>FrontendTasksPage: 503 with error details and task_id
        FrontendTasksPage-->>User: Show SUBMISSION_FAILED task in list
    else At least one endpoint reachable
        BackendTasksRoute->>TESInstance: POST /tasks
        TESInstance-->>BackendTasksRoute: HTTP error 4xx or 5xx

        alt TES returns HTTP error
            BackendTasksRoute->>BackendTasksRoute: Build error_msg and error_info
            BackendTasksRoute->>BackendTasksRoute: Create failed_task with state SUBMISSION_FAILED and http_status_code
            BackendTasksRoute->>TaskService: add_task(failed_task)
            BackendTasksRoute-->>FrontendTasksPage: 400 with error details and task_id
            FrontendTasksPage-->>User: Show SUBMISSION_FAILED task in list
        else TES accepts task
            BackendTasksRoute-->>FrontendTasksPage: 200 with task_id and tes_task_id
            loop Background status updates
                TaskService->>TaskService: update_task_statuses
                TaskService->>TaskService: Skip polling when state in terminal_states including SUBMISSION_FAILED
            end
        end
    end
Loading

Flow diagram for backend task submission and SUBMISSION_FAILED handling

flowchart TD
    A_start[Start task submission] --> B_build_command[Build executor command from input using shlex]
    B_build_command --> C_check_service_info[Check TES service-info endpoints]

    C_check_service_info --> D_any_endpoint_ok{Any TES endpoint reachable?}

    D_any_endpoint_ok -- No --> E_create_failed_connectivity[Create failed_task with state SUBMISSION_FAILED and connectivity error details]
    E_create_failed_connectivity --> F_add_task_connectivity["add_task(failed_task)"]
    F_add_task_connectivity --> G_respond_503[Return 503 with error details and task_id]

    D_any_endpoint_ok -- Yes --> H_post_task[POST task to TES endpoint]
    H_post_task --> I_http_success{HTTP success?}

    I_http_success -- No --> J_create_failed_http[Create failed_task with state SUBMISSION_FAILED and HTTP error details]
    J_create_failed_http --> K_add_task_http["add_task(failed_task)"]
    K_add_task_http --> L_respond_400[Return 400 with error details and task_id]

    I_http_success -- Yes --> M_success_path[Return success response with task_id and tes_task_id]

    subgraph Background_status_updater
        N_update_loop[Loop update_task_statuses] --> O_check_state{Is task.state in terminal_states?}
        O_check_state -- Yes (COMPLETE, CANCELED, SYSTEM_ERROR, EXECUTOR_ERROR, PREEMPTED, SUBMISSION_FAILED) --> P_skip_polling[Skip TES status polling]
        O_check_state -- No --> Q_poll_TES[Poll TES for status and update task]
    end

    M_success_path --> N_update_loop
    F_add_task_connectivity --> N_update_loop
    K_add_task_http --> N_update_loop
Loading

File-Level Changes

Change Details Files
Normalize and harden command construction for submitted tasks so both string and list inputs are handled safely.
  • Factor command construction into a single branch that accepts list commands as-is, uses shlex.split for non-empty strings with a safe fallback, and defaults to ['echo', 'Hello World'] when no command is provided.
  • Wire the computed command into the executor spec instead of in-line conditional logic.
backend/routes/tasks.py
Persist failed TES submissions as SUBMISSION_FAILED tasks with detailed error metadata for both connectivity and HTTP error scenarios.
  • When all service-info endpoints are unreachable, construct a synthetic failed task record with SUBMISSION_FAILED state, attach connectivity error details (message, type, code, reason), store it via add_task, and return the generated task id in the 503 response.
  • When TES task submission returns a non-2xx HTTP response, build a failed task record with SUBMISSION_FAILED state, error metadata (including HTTP body snippet and status), persist it, and return the task id in the 400 response.
backend/routes/tasks.py
Ensure background task updater treats SUBMISSION_FAILED as terminal so it does not attempt status polling for tasks that never reached TES.
  • Extend the terminal_states list to include SUBMISSION_FAILED in the periodic update loop.
backend/services/task_service.py
Expose error-state tasks in the Tasks UI instead of filtering them out.
  • Relax the frontend task filter so it only checks for essential fields (id, tes_url, state) and no longer excludes ERROR, SYSTEM_ERROR, EXECUTOR_ERROR, or error_prone_instance tasks from the list.
frontend/src/pages/Tasks.js

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@keshxvdayal keshxvdayal changed the base branch from main to keshav-dev January 25, 2026 14:08
@keshxvdayal keshxvdayal changed the base branch from keshav-dev to main January 25, 2026 14:08
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The failed_task dict is constructed twice in submit_task with mostly identical fields; consider extracting this into a small helper to avoid duplication and keep the error-handling paths in sync.
  • The new command parsing logic around shlex.split currently falls back to a naive split() on failure and only prints a warning; you might want to either return a 4xx error for invalid commands or at least log via the app logger instead of print so these issues are easier to trace.
  • For the connectivity-failure and HTTP-error branches in submit_task, it could be useful to standardize the structure of the JSON error response (e.g., always include task_id, tes_endpoint, and a clearly named error_type) to make client-side handling simpler and more consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `failed_task` dict is constructed twice in `submit_task` with mostly identical fields; consider extracting this into a small helper to avoid duplication and keep the error-handling paths in sync.
- The new command parsing logic around `shlex.split` currently falls back to a naive `split()` on failure and only prints a warning; you might want to either return a 4xx error for invalid commands or at least log via the app logger instead of `print` so these issues are easier to trace.
- For the connectivity-failure and HTTP-error branches in `submit_task`, it could be useful to standardize the structure of the JSON error response (e.g., always include `task_id`, `tes_endpoint`, and a clearly named `error_type`) to make client-side handling simpler and more consistent.

## Individual Comments

### Comment 1
<location> `backend/routes/tasks.py:195-204` </location>
<code_context>
+            failed_task = {
</code_context>

<issue_to_address>
**suggestion:** The `failed_task` construction is duplicated and could be centralized to reduce drift between error paths.

This dict is assembled almost identically here and in the later 4xx/5xx submission-error branch. Please extract a small helper (e.g. `build_failed_task(...)`) or a shared template so updates to the schema stay consistent between these two paths.

Suggested implementation:

```python
        def build_failed_task(tes_task, task_id='N/A'):
            now_iso = datetime.now(timezone.utc).isoformat()
            return {
                'id': str(uuid.uuid4()),
                'task_id': task_id,
                'name': tes_task['name'],
                'task_name': tes_task['name'],
                'description': tes_task['description'],
                'state': 'SUBMISSION_FAILED',
                'status': 'SUBMISSION_FAILED',
                'creation_time': now_iso,
                'submitted_at': now_iso,
                'start_time': None,
            }

        if not service_is_reachable:
            print(f"All service-info endpoints failed for {tes_name}")

            # feat: add failed submission tasks to task management #11
            failed_task = build_failed_task(tes_task=tes_task)

```

There is a second branch that handles 4xx/5xx submission errors and constructs a nearly identical `failed_task` dictionary. That block should be updated to call `build_failed_task(...)` instead of inlining the dict. If the 4xx/5xx path adds extra fields (e.g. `error`, `http_status`, etc.), extend `build_failed_task` to accept those as optional parameters and include them in the returned dict so both error paths share a single schema definition.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses task submission failure visibility and stability issues by recording failed submissions as tasks and preventing background status updates from breaking task lists.

Changes:

  • Frontend: updates task filtering to no longer exclude error states so failed/error tasks remain visible in Task Management.
  • Backend: treats SUBMISSION_FAILED as terminal to prevent the status updater from querying TES for tasks that never existed on TES.
  • Backend: improves command parsing (supports quoted strings) and records failed submissions (connectivity + HTTP errors) into the in-memory task list.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
frontend/src/pages/Tasks.js Stops filtering out error states so failed/error tasks remain in the task list.
backend/services/task_service.py Adds SUBMISSION_FAILED to terminal states to prevent updater corruption for failed submissions.
backend/routes/tasks.py Parses command strings via shlex.split and appends SUBMISSION_FAILED task records on submission failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +184 to +233
'error_code': connectivity_error_info['error_code'],
'reason': connectivity_error_info['reason'],
'tes_url': tes_url,
'tes_name': tes_name
'tes_name': tes_name,
'task_id': failed_task['id']
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this error response, the field name task_id is now used to return a locally-generated UUID (the dashboard record id), whereas on successful submissions task_id is the TES-assigned task id. This makes the API response semantics inconsistent for clients. Consider returning a separate field (e.g., dashboard_task_id/local_task_id) and reserving task_id for the TES id (or returning both explicitly).

Copilot uses AI. Check for mistakes.
'status_code': response.status_code
'status_code': response.status_code,
'task_id': failed_task['id']
}), 400
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch always returns HTTP 400 even when the TES instance responded with a different status (e.g., 401/403/500). That makes the dashboard API inaccurate and can break client-side handling that depends on status codes. Consider returning the original response.status_code (or mapping to an appropriate upstream code) instead of hardcoding 400.

Suggested change
}), 400
}), response.status_code

Copilot uses AI. Check for mistakes.
Comment on lines +381 to +385
# feat: add failed submission tasks to task management #11
failed_task = {
'id': str(uuid.uuid4()),
'task_id': 'N/A',
'name': tes_task['name'],
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failed_task payload is constructed in multiple places with a largely duplicated dict literal (here and earlier around the service-info reachability failure). This duplication makes it easy for the two branches to drift (missing fields / different timestamps). Consider extracting a small helper/builder for failed-submission task records and reusing it in both branches.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +45
failed_task = {
'id': str(uuid.uuid4()),
'task_id': 'N/A',
'name': tes_task['name'],
'task_name': tes_task['name'],
'description': tes_task['description'],
'state': 'SUBMISSION_FAILED',
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_failed_task hard-codes task_id to 'N/A' while generating a unique UUID in id. The frontend (taskService.listTasks) prefers task.task_id || task.id, so all failed submissions end up with the same displayed id ('N/A'), causing React key collisions and making task details/logs resolve to the wrong failed task. Make the failed submission identifier unique (e.g., set task_id to the generated UUID, or drop task_id and update the frontend mapping), and keep the TES task id as a separate nullable field if needed.

Copilot uses AI. Check for mistakes.
@@ -1,15 +1,108 @@
from flask import Blueprint, jsonify, request
from flask import Blueprint, jsonify, request, current_app
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import: current_app is imported but never referenced in this module. Please remove it to avoid linting/maintenance noise.

Suggested change
from flask import Blueprint, jsonify, request, current_app
from flask import Blueprint, jsonify, request

Copilot uses AI. Check for mistakes.
failed_task['error_code'] = error_code
if error_reason:
failed_task['error_reason'] = error_reason
if http_status_code:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http_status_code is added to the failed task only when truthy (if http_status_code:). For consistency with build_error_response (which checks is not None) and to avoid accidentally dropping valid falsy values, check http_status_code is not None instead.

Suggested change
if http_status_code:
if http_status_code is not None:

Copilot uses AI. Check for mistakes.

def update_task_statuses():
terminal_states = ['COMPLETE', 'CANCELED', 'SYSTEM_ERROR', 'EXECUTOR_ERROR', 'PREEMPTED']
terminal_states = ['COMPLETE', 'CANCELED', 'SYSTEM_ERROR', 'EXECUTOR_ERROR', 'PREEMPTED', 'SUBMISSION_FAILED']
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

terminal_states is now defined in update_task_statuses, but a separate hard-coded terminal-state list still exists in update_single_task_status (used for the "Verified task in terminal state" branch). Consider centralizing terminal states in a single constant so these lists don’t drift over time.

Copilot uses AI. Check for mistakes.
@ninsch3000
Copy link

Copilot comments still to be addressed.

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.

3 participants