Files
Richard Osborne ddcb9cd02e feat: Phase 5 BYOB foundation + Phase 3 GitHub integration
Phase 5 - BYOB Backend (TASK-007A/B):
- LocalSQL Adapter with full CloudStore API compatibility
- QueryBuilder translates Parse-style queries to SQL
- SchemaManager with PostgreSQL/Supabase export
- LocalBackendServer with REST endpoints
- BackendManager with IPC handlers for Electron
- In-memory fallback when better-sqlite3 unavailable

Phase 3 - GitHub Panel (GIT-004):
- Issues tab with list/detail views
- Pull Requests tab with list/detail views
- GitHub API client with OAuth support
- Repository info hook integration

Phase 3 - Editor UX Bugfixes (TASK-013):
- Legacy runtime detection banners
- Read-only enforcement for legacy projects
- Code editor modal close improvements
- Property panel stuck state fix
- Blockly node deletion and UI polish

Phase 11 - Cloud Functions Planning:
- Architecture documentation for workflow automation
- Execution history storage schema design
- Canvas overlay concept for debugging

Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
2026-01-15 17:37:15 +01:00
..

CF11-006: Execution History Panel UI

Metadata

Field Value
ID CF11-006
Phase Phase 11
Series 2 - Execution History
Priority 🔴 Critical
Difficulty 🟡 Medium
Estimated Time 12-16 hours
Prerequisites CF11-004, CF11-005
Branch feature/cf11-006-execution-history-panel

Objective

Create a sidebar panel in the editor that displays workflow execution history, allowing users to view past executions, inspect node data, and debug failed workflows.

Background

With execution data being captured (CF11-004, CF11-005), users need a way to:

  • View all past executions for a workflow
  • See execution status at a glance (success/error)
  • Drill into individual executions to see node-by-node data
  • Quickly identify where workflows fail

This is the primary debugging interface for workflow developers.

Current State

  • Execution data is stored in SQLite
  • No UI to view execution history
  • Users cannot debug failed workflows

Desired State

  • New "Execution History" panel in sidebar
  • List of past executions with status, duration, timestamp
  • Expandable execution detail view
  • Node step list with input/output data
  • Search/filter capabilities
  • Delete/clear history options

Scope

In Scope

  • ExecutionHistoryPanel React component
  • ExecutionList component
  • ExecutionDetail component with node steps
  • Data display for inputs/outputs (JSON viewer)
  • Filter by status, date range
  • Integration with sidebar navigation
  • Proper styling with design tokens

Out of Scope

  • Canvas overlay (CF11-007)
  • Real-time streaming of executions
  • Export/import of execution data

Technical Approach

Component Structure

ExecutionHistoryPanel/
├── index.ts
├── ExecutionHistoryPanel.tsx        # Main panel container
├── ExecutionHistoryPanel.module.scss
├── components/
│   ├── ExecutionList/
│   │   ├── ExecutionList.tsx        # List of executions
│   │   ├── ExecutionList.module.scss
│   │   ├── ExecutionItem.tsx        # Single execution row
│   │   └── ExecutionItem.module.scss
│   ├── ExecutionDetail/
│   │   ├── ExecutionDetail.tsx      # Expanded execution view
│   │   ├── ExecutionDetail.module.scss
│   │   ├── NodeStepList.tsx         # List of node steps
│   │   ├── NodeStepList.module.scss
│   │   ├── NodeStepItem.tsx         # Single step row
│   │   └── NodeStepItem.module.scss
│   └── ExecutionFilters/
│       ├── ExecutionFilters.tsx     # Filter controls
│       └── ExecutionFilters.module.scss
└── hooks/
    ├── useExecutionHistory.ts       # Data fetching hook
    └── useExecutionDetail.ts        # Single execution hook

Main Panel Component

// ExecutionHistoryPanel.tsx
import React, { useState } from 'react';

import { PanelHeader } from '@noodl-core-ui/components/sidebar/PanelHeader';

import { ExecutionDetail } from './components/ExecutionDetail';
import { ExecutionFilters } from './components/ExecutionFilters';
import { ExecutionList } from './components/ExecutionList';
import styles from './ExecutionHistoryPanel.module.scss';
import { useExecutionHistory } from './hooks/useExecutionHistory';

export function ExecutionHistoryPanel() {
  const [selectedExecutionId, setSelectedExecutionId] = useState<string | null>(null);
  const [filters, setFilters] = useState<ExecutionFilters>({
    status: undefined,
    startDate: undefined,
    endDate: undefined
  });

  const { executions, loading, refresh } = useExecutionHistory(filters);

  return (
    <div className={styles.Panel}>
      <PanelHeader title="Execution History" onRefresh={refresh} />

      <ExecutionFilters filters={filters} onChange={setFilters} />

      {selectedExecutionId ? (
        <ExecutionDetail executionId={selectedExecutionId} onBack={() => setSelectedExecutionId(null)} />
      ) : (
        <ExecutionList executions={executions} loading={loading} onSelect={setSelectedExecutionId} />
      )}
    </div>
  );
}

Execution List Item

// ExecutionItem.tsx

import { WorkflowExecution } from '@noodl-viewer-cloud/execution-history';
import React from 'react';

import { Icon } from '@noodl-core-ui/components/common/Icon';

import styles from './ExecutionItem.module.scss';

interface Props {
  execution: WorkflowExecution;
  onSelect: () => void;
}

export function ExecutionItem({ execution, onSelect }: Props) {
  const statusIcon =
    execution.status === 'success' ? 'check-circle' : execution.status === 'error' ? 'x-circle' : 'loader';

  const statusColor =
    execution.status === 'success'
      ? 'var(--theme-color-success)'
      : execution.status === 'error'
      ? 'var(--theme-color-error)'
      : 'var(--theme-color-fg-default)';

  return (
    <div className={styles.Item} onClick={onSelect}>
      <Icon icon={statusIcon} style={{ color: statusColor }} />
      <div className={styles.Info}>
        <span className={styles.Name}>{execution.workflowName}</span>
        <span className={styles.Time}>{formatRelativeTime(execution.startedAt)}</span>
      </div>
      <div className={styles.Meta}>
        <span className={styles.Duration}>{formatDuration(execution.durationMs)}</span>
        <span className={styles.Trigger}>{execution.triggerType}</span>
      </div>
    </div>
  );
}

Execution Detail View

// ExecutionDetail.tsx
import React from 'react';

import { JSONViewer } from '@noodl-core-ui/components/json-editor';

import { useExecutionDetail } from '../../hooks/useExecutionDetail';
import styles from './ExecutionDetail.module.scss';
import { NodeStepList } from './NodeStepList';

interface Props {
  executionId: string;
  onBack: () => void;
  onPinToCanvas?: () => void; // For CF11-007 integration
}

export function ExecutionDetail({ executionId, onBack, onPinToCanvas }: Props) {
  const { execution, loading } = useExecutionDetail(executionId);

  if (loading || !execution) {
    return <div>Loading...</div>;
  }

  return (
    <div className={styles.Detail}>
      <header className={styles.Header}>
        <button onClick={onBack}> Back</button>
        <h3>{execution.workflowName}</h3>
        {onPinToCanvas && <button onClick={onPinToCanvas}>Pin to Canvas</button>}
      </header>

      <section className={styles.Summary}>
        <div className={styles.Status} data-status={execution.status}>
          {execution.status}
        </div>
        <div>Started: {formatTime(execution.startedAt)}</div>
        <div>Duration: {formatDuration(execution.durationMs)}</div>
        <div>Trigger: {execution.triggerType}</div>
      </section>

      {execution.errorMessage && (
        <section className={styles.Error}>
          <h4>Error</h4>
          <pre>{execution.errorMessage}</pre>
          {execution.errorStack && (
            <details>
              <summary>Stack Trace</summary>
              <pre>{execution.errorStack}</pre>
            </details>
          )}
        </section>
      )}

      {execution.triggerData && (
        <section className={styles.TriggerData}>
          <h4>Trigger Data</h4>
          <JSONViewer data={execution.triggerData} />
        </section>
      )}

      <section className={styles.Steps}>
        <h4>Node Execution Steps ({execution.steps.length})</h4>
        <NodeStepList steps={execution.steps} />
      </section>
    </div>
  );
}

Data Fetching Hooks

// useExecutionHistory.ts

import { CloudService } from '@noodl-editor/services/CloudService';
import { WorkflowExecution, ExecutionQuery } from '@noodl-viewer-cloud/execution-history';
import { useState, useEffect, useCallback } from 'react';

export function useExecutionHistory(filters: ExecutionFilters) {
  const [executions, setExecutions] = useState<WorkflowExecution[]>([]);
  const [loading, setLoading] = useState(true);

  const fetch = useCallback(async () => {
    setLoading(true);
    try {
      const query: ExecutionQuery = {
        status: filters.status,
        startedAfter: filters.startDate?.getTime(),
        startedBefore: filters.endDate?.getTime(),
        limit: 100,
        orderBy: 'started_at',
        orderDir: 'desc'
      };
      const result = await CloudService.getExecutionHistory(query);
      setExecutions(result);
    } finally {
      setLoading(false);
    }
  }, [filters]);

  useEffect(() => {
    fetch();
  }, [fetch]);

  return { executions, loading, refresh: fetch };
}

Styling Guidelines

All styles MUST use design tokens:

// ExecutionItem.module.scss
.Item {
  display: flex;
  align-items: center;
  padding: var(--theme-spacing-3);
  background-color: var(--theme-color-bg-2);
  border-bottom: 1px solid var(--theme-color-border-default);
  cursor: pointer;

  &:hover {
    background-color: var(--theme-color-bg-3);
  }
}

.Name {
  color: var(--theme-color-fg-default);
  font-weight: 500;
}

.Time {
  color: var(--theme-color-fg-default-shy);
  font-size: 12px;
}

// Status colors
[data-status='success'] {
  color: var(--theme-color-success);
}

[data-status='error'] {
  color: var(--theme-color-error);
}

Implementation Steps

Step 1: Create Panel Structure (3h)

  1. Create folder structure
  2. Create ExecutionHistoryPanel component
  3. Register panel in sidebar navigation
  4. Basic layout and header

Step 2: Implement Execution List (3h)

  1. Create ExecutionList component
  2. Create ExecutionItem component
  3. Implement useExecutionHistory hook
  4. Add loading/empty states

Step 3: Implement Execution Detail (4h)

  1. Create ExecutionDetail component
  2. Create NodeStepList/NodeStepItem
  3. Implement useExecutionDetail hook
  4. Add JSON viewer for data display
  5. Handle error display

Step 4: Add Filters & Search (2h)

  1. Create ExecutionFilters component
  2. Status filter dropdown
  3. Date range picker
  4. Integration with list

Step 5: Polish & Testing (3h)

  1. Responsive styling
  2. Keyboard navigation
  3. Manual testing
  4. Edge cases

Testing Plan

Manual Testing

  • Panel appears in sidebar
  • Executions load correctly
  • Clicking execution shows detail
  • Back button returns to list
  • Filter by status works
  • Filter by date works
  • Node steps display correctly
  • Input/output data renders
  • Error display works
  • Empty state shows correctly

Automated Testing

  • useExecutionHistory hook tests
  • useExecutionDetail hook tests
  • ExecutionItem renders correctly
  • Filter state management

Success Criteria

  • Panel accessible from sidebar
  • Execution list shows all executions
  • Detail view shows full execution data
  • Node steps show input/output data
  • Filters work correctly
  • All styles use design tokens
  • No hardcoded colors
  • Responsive at different panel widths

Risks & Mitigations

Risk Mitigation
Large execution lists slow Virtual scrolling, pagination
JSON viewer performance Lazy load, collapse by default
Missing CloudService API Coordinate with CF11-005

References