Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8ef1c41
feat(db): add workflow tables (schema v9)
JayantDevkar Mar 1, 2026
05f5da3
feat(models): add workflow Pydantic models and response schemas
JayantDevkar Mar 1, 2026
718c9da
feat(engine): add workflow execution engine with template resolution
JayantDevkar Mar 1, 2026
80d122d
feat(api): add workflow CRUD and execution endpoints
JayantDevkar Mar 1, 2026
0f661d6
chore(frontend): add @xyflow/svelte and @dagrejs/dagre
JayantDevkar Mar 1, 2026
e9c1c44
feat(frontend): add workflow TypeScript types
JayantDevkar Mar 1, 2026
7c5b9d1
feat(frontend): add workflow list page
JayantDevkar Mar 1, 2026
bd80e42
feat(frontend): add Svelte Flow workflow editor components
JayantDevkar Mar 1, 2026
b6d74f2
feat(frontend): add workflow editor pages (new + edit)
JayantDevkar Mar 1, 2026
ada750f
feat(frontend): add workflow execution view with live polling
JayantDevkar Mar 1, 2026
3cb93ed
feat(frontend): add Workflows to navigation header
JayantDevkar Mar 1, 2026
a5b633f
test: add workflow CRUD integration test
JayantDevkar Mar 1, 2026
3a2cab0
fix: address code review findings for workflow builder
JayantDevkar Mar 2, 2026
104a282
feat: normalize workflow database into separate workflow.db
JayantDevkar Mar 2, 2026
ddf7e79
feat(api): add label field to workflow steps (schema v2)
JayantDevkar Mar 2, 2026
eb841b6
feat(frontend): add toast notification system
JayantDevkar Mar 2, 2026
75c2d52
feat(frontend): enhance workflow editor with 7 UX improvements
JayantDevkar Mar 2, 2026
1a91501
feat(frontend): add run input form, delete confirmation, replace alerts
JayantDevkar Mar 2, 2026
03ff19d
chore: ignore .worktrees/ directory
JayantDevkar Mar 2, 2026
77cc2da
fix: address code review findings for workflow builder safety and cor…
JayantDevkar Mar 2, 2026
0a28665
fix(frontend): convert toast store from $state rune to writable store
JayantDevkar Mar 4, 2026
2148028
Create package-lock.json
JayantDevkar Mar 5, 2026
8036839
fix(frontend): rename toast.ts → toast.svelte.ts for $state rune comp…
the-non-expert Mar 7, 2026
f070557
feat: Replace timeline event keyboard navigation with Cmd+K search fo…
the-non-expert Mar 6, 2026
43532ea
fix(live-sessions): stop reconciler from ending STOPPED sessions
JayantDevkar Mar 8, 2026
966c608
Merge branch 'feat/workflow-builder' of https://github.com/JayantDevk…
JayantDevkar Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Git worktrees
.worktrees/

# OMC state
.omc/

Expand Down
5 changes: 5 additions & 0 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ def sqlite_db_path(self) -> Path:
"""Get the SQLite metadata database path."""
return self.karma_base / "metadata.db"

@property
def workflow_db_path(self) -> Path:
"""Get the SQLite workflow database path."""
return self.karma_base / "workflow.db"


# Global settings instance
settings = Settings()
Expand Down
40 changes: 39 additions & 1 deletion api/db/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

logger = logging.getLogger(__name__)

SCHEMA_VERSION = 10
SCHEMA_VERSION = 11

SCHEMA_SQL = """
-- Schema versioning
Expand Down Expand Up @@ -214,6 +214,8 @@
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_projects_slug ON projects(slug);

-- Workflow tables moved to workflow.db (schema version 10)
"""


Expand Down Expand Up @@ -426,6 +428,42 @@ def ensure_schema(conn: sqlite3.Connection) -> None:
# Nudge mtime to force re-index of all sessions
conn.execute("UPDATE sessions SET jsonl_mtime = jsonl_mtime - 1")

if current_version < 11:
logger.info("Migrating → v11: dropping workflow tables (moved to workflow.db)")
from config import settings

# Only drop if workflow.db migration was confirmed successful
should_drop = False
wf_path = settings.workflow_db_path
if wf_path.exists():
try:
import sqlite3 as _sqlite3

wf_check = _sqlite3.connect(str(wf_path), timeout=5.0)
migrated = wf_check.execute(
"SELECT 1 FROM wf_migration_log WHERE source = 'metadata_db'"
).fetchone()
wf_check.close()
should_drop = migrated is not None
except Exception:
logger.warning(
"Could not verify workflow.db migration; "
"keeping workflow tables in metadata.db"
)

if should_drop:
conn.executescript("""
DROP TABLE IF EXISTS workflow_run_steps;
DROP TABLE IF EXISTS workflow_runs;
DROP TABLE IF EXISTS workflows;
""")
logger.info("Dropped workflow tables from metadata.db")
else:
logger.info(
"Workflow migration not confirmed; "
"keeping workflow tables in metadata.db until next restart"
)

# Record version
conn.execute(
"INSERT OR REPLACE INTO schema_version (version) VALUES (?)",
Expand Down
98 changes: 98 additions & 0 deletions api/db/workflow_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
SQLite connection management for workflow.db.

Mirrors the reader/writer pattern from connection.py but for the
separate workflow database. Single writer connection, per-request readers.
"""

import logging
import sqlite3
import threading
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

# Writer singleton state
_wf_writer: Optional[sqlite3.Connection] = None
_wf_writer_lock = threading.Lock()


def get_workflow_db_path() -> Path:
"""Get the workflow database file path."""
from config import settings

return settings.workflow_db_path


def get_wf_writer() -> sqlite3.Connection:
"""
Get or create the singleton writer connection for workflow.db.

Initializes schema and runs migration from metadata.db on first call.
"""
global _wf_writer

if _wf_writer is not None:
return _wf_writer

with _wf_writer_lock:
if _wf_writer is not None:
return _wf_writer

db_path = get_workflow_db_path()
db_path.parent.mkdir(parents=True, exist_ok=True)

logger.info("Opening workflow DB writer connection at %s", db_path)

conn = sqlite3.connect(
str(db_path),
check_same_thread=False,
timeout=10.0,
)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA synchronous=NORMAL")
conn.execute("PRAGMA foreign_keys=ON")
conn.execute("PRAGMA busy_timeout=5000")

from .workflow_schema import ensure_workflow_schema, migrate_from_metadata_db

ensure_workflow_schema(conn)
try:
migrate_from_metadata_db(conn)
except Exception:
logger.warning("Workflow migration from metadata.db failed; skipping", exc_info=True)

_wf_writer = conn
logger.info("Workflow DB writer connection ready")
return _wf_writer


def create_wf_read_conn() -> sqlite3.Connection:
"""
Create a new read-only connection for workflow.db.

Caller is responsible for closing the connection.
"""
db_path = get_workflow_db_path()

conn = sqlite3.connect(
f"file:{db_path}?mode=ro",
uri=True,
timeout=5.0,
)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA busy_timeout=5000")
return conn


def close_wf_db() -> None:
"""Close the singleton writer. Called during app shutdown."""
global _wf_writer

with _wf_writer_lock:
if _wf_writer is not None:
logger.info("Closing workflow DB writer connection")
_wf_writer.close()
_wf_writer = None
Loading
Loading