Skip to content

feat: track model execution status with failure icon in explorer#54

Merged
abhizipstack merged 5 commits intomainfrom
feat/model-execution-status-tracking
Apr 16, 2026
Merged

feat: track model execution status with failure icon in explorer#54
abhizipstack merged 5 commits intomainfrom
feat/model-execution-status-tracking

Conversation

@abhizipstack
Copy link
Copy Markdown
Contributor

What

  • Add RunStatus enum and 4 new fields to ConfigModels: run_status, failure_reason, last_run_at, run_duration
  • New migration 0003_add_model_run_status.py
  • Update file_explorer.load_models() to return status fields for each model
  • Add _update_model_status() to DAG executor in visitran.py — called before execution (RUNNING), after success (SUCCESS), and on exception (FAILED)
  • Update execute/views.py to return 400 on DAG execution failures
  • Fix clear_cache decorator to re-raise exceptions instead of silently swallowing them
  • Frontend: add colored status dot badges next to model names in the explorer tree (blue=running, green=success, red=failed)
  • Frontend: Popover on hover over failed status shows full error message (scrollable) and last run timestamp
  • Frontend: trigger explorer refresh via setRefreshModels after runTransformation succeeds or fails

Why

After a model build/run, users had no visual indication of which models succeeded or failed. They had to manually open each model to check. This change shows failure icons directly in the explorer so users can quickly identify broken models and fix them via prompts or manual edits.

How

Status is tracked at the DAG node level (not API level). When the DAG executor processes each node, it updates the corresponding ConfigModels row in the database with the current status. This ensures accurate per-model status even when Model A succeeds but downstream Model B fails — Model A is marked SUCCESS while Model B is marked FAILED.

The frontend file_explorer API already returns the full model tree — we just add the new status fields to each model entry. The explorer renders a small colored dot inline before the model name based on run_status. Failed models show a Popover on hover with the full failure_reason and last_run_at.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

Low risk:

  • The 4 new DB fields are nullable with sensible defaults (NOT_STARTED), so existing models continue to work
  • Status tracking wraps the existing execution logic with try/except — if status update fails, the error is logged but does not interrupt execution
  • The clear_cache decorator fix actually addresses a pre-existing silent bug where execution errors were being swallowed and returning NoneType to DRF (causing 500 errors instead of proper error responses)
  • CLI mode (no Django) is handled — ConfigModels import is wrapped in try/except so visitran CLI still works without Django
  • Frontend badge rendering is additive — models without status fields render unchanged

Database Migrations

  • backend/backend/core/migrations/0003_add_model_run_status.py — adds 4 nullable fields to configmodels table (run_status, failure_reason, last_run_at, run_duration)

Env Config

None

Relevant Docs

None

Related Issues or PRs

None

Dependencies Versions

No changes

Notes on Testing

Tested locally with Python 3.11:

  • Migration applied cleanly
  • Model RunStatus choices verified via Django shell
  • DAG execution correctly updates status per-node (RUNNING → SUCCESS/FAILED)
  • Explorer renders status dots correctly with inline vertical-align
  • Popover on failed models shows full error message with scroll
  • clear_cache fix verified — MultipleColumnDependency exception now returns proper 400 error response instead of 500 NoneType

Screenshots

TBD — will add after testing locally

Checklist

I have read and understood the Contribution Guidelines.

🤖 Generated with Claude Code

Implement model-level execution status tracking (NOT_STARTED, RUNNING,
SUCCESS, FAILED) at DAG node level with visual indicators in the file
explorer. Status is tracked per individual DAG node, ensuring accurate
status for each model even when downstream dependencies fail.

Backend changes:
- Add RunStatus enum and 4 new fields to ConfigModels (run_status,
  failure_reason, last_run_at, run_duration)
- Migration 0003_add_model_run_status
- Update file_explorer.load_models to return status fields
- Add _update_model_status to DAG executor — called before execution
  (RUNNING), after success (SUCCESS), and on exception (FAILED with
  reason)
- Update execute/views.py to return 400 on DAG execution failures
- Fix clear_cache decorator to re-raise exceptions instead of
  swallowing them silently

Frontend changes:
- Add getModelRunStatus helper to render colored dot badges next to
  model names in the explorer tree
- Running: blue, Success: green, Failed: red
- Show Popover on hover over failed status with full error message
  and last run timestamp
- Trigger explorer refresh via setRefreshModels after runTransformation
  succeeds or fails

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@abhizipstack abhizipstack requested review from a team as code owners April 10, 2026 09:29
Comment thread backend/backend/core/routers/execute/views.py Fixed
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 10, 2026

Greptile Summary

This PR adds per-model execution status tracking (RunStatus enum, 4 new DB fields, migration) and surfaces it in the explorer as colored dot badges with a failure popover. It also fixes a pre-existing bug where the clear_cache decorator silently swallowed exceptions, and moves close_db_connection to a finally block to prevent connection leaks on failure.

Two P1 bugs are present in visitran.py:

  • The CLI fallback for timezone does from datetime import datetime, which shadows the module-level import datetime — all datetime.datetime.now() calls in the file would raise AttributeError in CLI (no-Django) mode.
  • Both except handlers in execute_graph call _update_model_status(...FAILED...) unconditionally, so non-executable dependency nodes (materialized only for DAG resolution) can be written as FAILED in the DB and display a red dot in the explorer, misattributing the failure.

Confidence Score: 4/5

Safe to merge for Django-mode users, but two P1 logic bugs in visitran.py should be addressed before merge.

Both P1 issues are in visitran.py: datetime shadowing only manifests in CLI mode (Django mode is unaffected), and the non-executable FAILED status would only trigger if a dependency node fails during materialization — a less common path. All other changes (DB, migration, frontend, views) are clean with no critical issues. Score is 4 rather than 3 because the primary Django user path is unbroken.

backend/visitran/visitran.py — datetime shadowing in the CLI fallback block and missing is_executable guard in exception handlers

Important Files Changed

Filename Overview
backend/visitran/visitran.py Adds _update_model_status() and per-node status tracking in execute_graph(); has two P1 bugs: datetime module shadowed in CLI fallback and non-executable nodes incorrectly marked FAILED
backend/backend/core/migrations/0003_add_model_run_status.py Adds 4 nullable fields to configmodels and bumps project_schema max_length; safe additive migration with sensible defaults
backend/backend/core/models/config_models.py Adds RunStatus TextChoices enum and 4 new execution-tracking fields; clean and well-structured
backend/backend/application/file_explorer/file_explorer.py Builds a model lookup dict and adds run_status/failure_reason/last_run_at/run_duration to each node entry; correct and efficient
backend/backend/core/routers/execute/views.py Wraps execution in try/except/finally, fixes the DB connection leak via finally, and returns 400 on DAG failures instead of crashing
backend/backend/utils/cache_service/decorators/cache_decorator.py Adds raise to re-raise view-function exceptions instead of silently returning None; fixes pre-existing bug
frontend/src/ide/explorer/explorer-component.jsx Adds getModelRunStatus() helper and renders colored dot badges + FAILED popover; getExplorer + setRefreshModels called together on success and failure
frontend/src/ide/editor/no-code-model/no-code-model.jsx Calls setRefreshModels(true) after both success and error paths of runTransformation; simple additive change
backend/backend/core/models/project_details.py Bumps project_schema max_length from 20 to 1024; safe change with matching migration

Sequence Diagram

sequenceDiagram
    participant FE as Frontend (Explorer)
    participant API as execute/views.py
    participant DAG as Visitran.execute_graph()
    participant DB as ConfigModels (DB)

    FE->>API: POST /execute/run {file_name}
    API->>DAG: execute_visitran_run_command()
    loop Each DAG node
        DAG->>DB: UPDATE run_status=RUNNING, last_run_at=now()
        alt Execution succeeds
            DAG->>DB: UPDATE run_status=SUCCESS, run_duration=…
        else VisitranBaseException
            DAG->>DB: UPDATE run_status=FAILED, failure_reason=…
            DAG-->>API: raise
        else Generic Exception
            DAG->>DB: UPDATE run_status=FAILED, failure_reason=repr(err)
        end
    end
    alt All nodes succeeded
        API-->>FE: 200 {status: success}
    else Any exception raised
        API-->>FE: 400 {status: failed}
    end
    Note over API: close_db_connection() always in finally
    FE->>API: GET /file_explorer (run_status, failure_reason, last_run_at per model)
    FE->>FE: Render status dot badge in explorer tree
Loading

Fix All in Claude Code

Prompt To Fix All With AI
This is a comment left during a code review.
Path: backend/visitran/visitran.py
Line: 20-27

Comment:
**`datetime` module shadowed by the CLI fallback import**

`from datetime import datetime` inside the `except ImportError:` block rebinds the module-level name `datetime` (introduced at line 4 via `import datetime`) to the `datetime.datetime` class. In CLI mode every subsequent call to `datetime.datetime.now()` (e.g. in `BaseResult` construction) would raise `AttributeError: type object 'datetime' has no attribute 'datetime'`.

Use a private alias so the module reference is preserved:

```suggestion
except ImportError:
    from datetime import datetime as _datetime, timezone as _tz

    class timezone:
        @staticmethod
        def now():
            return _datetime.now(_tz.utc)
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: backend/visitran/visitran.py
Line: 353-371

Comment:
**Non-executable dependency nodes incorrectly receive FAILED status**

The RUNNING update at line 300–301 is guarded by `if is_executable:`, but both `except` handlers call `_update_model_status(...FAILED...)` unconditionally. If `_apply_model_config_override()` or `node.materialize()` throws for a non-executable parent node (materialized only for DAG dependency resolution), that model gets written as FAILED in `ConfigModels` — showing a red dot in the explorer — even though it was never marked RUNNING and was not the model being executed.

Add the same `is_executable` guard to the FAILED paths:

```python
except VisitranBaseExceptions as visitran_err:
    if is_executable:
        self._update_model_status(
            str(node_name),
            ConfigModels.RunStatus.FAILED,
            failure_reason=str(visitran_err),
            run_duration=time.monotonic() - start_time,
        )
    raise visitran_err
except Exception as err:
    ...
    if is_executable:
        self._update_model_status(
            str(node_name),
            ConfigModels.RunStatus.FAILED,
            failure_reason=err_trace,
            run_duration=time.monotonic() - start_time,
        )
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (5): Last reviewed commit: "fix: address Greptile P1s — CLI-safe tim..." | Re-trigger Greptile

Comment thread backend/visitran/visitran.py Outdated
Comment thread backend/visitran/visitran.py Outdated
Comment thread backend/visitran/visitran.py Outdated
Comment thread backend/visitran/visitran.py
Comment thread frontend/src/ide/explorer/explorer-component.jsx Outdated
Comment thread frontend/src/ide/explorer/explorer-component.jsx Outdated
wicky-zipstack added a commit that referenced this pull request Apr 13, 2026
- Remove unused .project-list-card-clickable-content CSS class
- Reduce !important usage in schema select styles, increase specificity instead
- Replace duplicate API call with explorerService.setProjectSchema
- Replace message.success/error with useNotificationService for consistency
- Show error InfoChip when schema list is empty instead of hiding dropdown
- Revert project_schema max_length change (will be added to PR #54 with migration)
- Drop optional ConfigModels import; it's always present in the OSS build
- Track run_duration via time.monotonic() and persist on SUCCESS/FAILED
- Skip RUNNING update for non-executable parent nodes so they don't get
  stuck with a permanent blue badge in selective execution
- Raise explicit errors when session/project_id missing instead of silent no-op
- Stop returning raw exception strings from execute_run_command (CodeQL)
- Refresh explorer tree on context-menu run failure so FAILED badge appears
- Move getModelRunStatus to module scope and use antd theme tokens instead
  of hardcoded hex colors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@abhizipstack
Copy link
Copy Markdown
Contributor Author

Addressed all review feedback in 55332f7:

Greptile P1s

  • Non-executable parent nodes stuck in RUNNING → wrapped the RUNNING status update with if is_executable so parents imported only for DAG resolution no longer get a permanent blue badge.
  • Context-menu run failures not refreshing the explorer → handleModelRun's .catch() now calls getExplorer(projectId) and setRefreshModels(true), so the red FAILED badge appears immediately.

Greptile P2

  • run_duration now populated via time.monotonic() around each node; persisted on SUCCESS and both FAILED branches.

@tahierhussain

  • Dropped the conditional ConfigModels import and the if ConfigModels else "FAILED" fallbacks.
  • Missing session/project_id now raise ValueError (still caught + logged via logging.exception).
  • getModelRunStatus hoisted to module scope; uses antd theme tokens (colorInfo / colorError / colorSuccess / colorTextSecondary) instead of hardcoded hex.

CodeQL

  • Response no longer includes the raw exception string; full traceback is logged server-side and a generic error message is returned to the client.

Comment thread backend/visitran/visitran.py Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread backend/backend/core/routers/execute/views.py
Comment thread backend/backend/core/migrations/0003_add_model_run_status.py
Requested by @tahierhussain in PR #55 review — the project_schema
field was too short for schemas with long names or multiple schemas.
Bundled into migration 0003 alongside the model run-status fields
since both target the core app and depend on 0002_seed_data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wicky-zipstack
Copy link
Copy Markdown
Contributor

wicky-zipstack commented Apr 16, 2026

🔴 Blockers

  1. Top-level from backend.core.models.config_models import ConfigModels in visitran.py:75 breaks CLI mode. PR description says it's wrapped in try/except — the diff isn't. Either guard the import or (better) emit an event from the executor and let a Django-side listener persist status — keeps visitran/ Django-free.

  2. Fragile class-name parsingvisitran.py:246: model_name.split("'")[1].split(".")[-2] parses repr() of a class. PR FIX: Reduce logs panel noise and make execution messages readable (OR-1457) #59 just moved away from str(cls) for this exact reason. Use node.__class__.__module__.rsplit('.', 1)[-1] (or whatever maps to ConfigModels.model_name).

🟠 Should split out

  1. clear_cache re-raise (cache_decorator.py:99) is the right fix but wraps every mutating view — please ship as its own PR so it can be reverted independently if it surfaces 500s elsewhere.

  2. project_schema max_length 20 → 1024 is unrelated to this feature and silently bundled into the migration. Pull into its own PR.

🟡 Minor

  • views.pyexcept Exception → 400 should be 500 for unknown DAG failures.
  • Per-node DB writes in the DAG hot loop — is the RUNNING write actually consumed by the UI mid-run, or only after setRefreshModels(true) post-completion? If post-completion, drop the RUNNING write.
  • getModelRunStatus (90 lines) — extract to its own file.

…leak

P1: `from django.utils import timezone` at module level crashes when
    visitran is used as a standalone CLI without Django installed.
    Wrapped in try/except with a minimal fallback that provides
    timezone.now() via stdlib datetime.

P1: close_db_connection() was only called in the success path of
    execute_run_command. On exception the connection leaked. Moved
    to a finally block so it always runs regardless of outcome.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread backend/visitran/visitran.py
Comment thread backend/visitran/visitran.py
Comment thread backend/visitran/visitran.py
Copy link
Copy Markdown
Contributor

@tahierhussain tahierhussain left a comment

Choose a reason for hiding this comment

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

LGTM

@abhizipstack abhizipstack merged commit 73ead92 into main Apr 16, 2026
8 checks passed
@abhizipstack abhizipstack deleted the feat/model-execution-status-tracking branch April 16, 2026 11:50
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.

5 participants