feat: hybrid workflow storage - DB + filesystem#29
Conversation
Workflows created via Web UI are now stored in the database instead of the filesystem. Filesystem-based workflows (bundled defaults + repo YAML) continue working as-is. Discovery merges all sources: bundled < global < repo < DB (DB highest priority). Changes: - Add remote_agent_workflow_definitions table (SQLite + PostgreSQL) - Add workflow-definitions.ts CRUD operations (upsert/get/list/delete) - Extend WorkflowSource with 'db' value - Extend discoverWorkflowsWithConfig with getDbWorkflows callback - PUT /api/workflows/:name now saves to DB (source: 'db') - DELETE /api/workflows/:name now deletes from DB - Add POST /api/workflows/import (YAML -> parse -> validate -> DB) - Add GET /api/workflows/:name/export (returns YAML text) - GET /api/workflows/:name checks DB first (highest priority) - CLI workflow list includes DB workflows via getDbWorkflows callback - Update Zod schemas for import/export endpoints - Update tests for DB-backed storage behavior Fixes #5 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🔍 Comprehensive PR Review — #29Reviewed by: 5 specialized agents (code-review, error-handling, test-coverage, comment-quality, docs-impact) SummaryThe hybrid workflow storage architecture is well-designed: the DB CRUD layer correctly uses the Verdict:
🔴 Critical Issues1. DELETE route missing try-catch — unhandled DB exception📍 Every other mutating route in this file (PUT, import POST) wraps the DB call in try-catch with View fixtry {
const deleted = await workflowDefinitionsDb.deleteWorkflowDefinition(name);
if (!deleted) return apiError(c, 404, `Workflow '${name}' not found in database`);
return c.json({ deleted: true, name });
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
getLog().error({ err, name }, 'workflow.definition_delete_failed');
return apiError(c, 500, 'Failed to delete workflow');
}2. DB CRUD layer has zero unit tests📍 All 4 exported DB functions have no tests. Every other DB module in the codebase has corresponding tests using the Fix: Create 3. CLI
|
| Issue | Location | Suggestion |
|---|---|---|
Export route OpenAPI schema declares text/plain but sends text/yaml |
api.ts:205-210 |
Change route definition to 'text/yaml' to match actual Content-Type |
| Misleading route registration comment on export route | api.ts:2244 |
Remove — path segment counts differ, no Hono conflict possible |
| DB merge priority undocumented | workflow-discovery.ts:303 |
Add comment explaining why DB wins (API-managed overrides repo-managed YAML) |
| SQLite schema not validated in sqlite.test.ts | sqlite.ts:76-97 |
Add column/constraint test for new table to catch DDL drift |
| PUT mock call assertion missing | api.workflows.test.ts |
Assert mockUpsertWorkflowDefinition called with correct args |
| Stale test comment on DELETE 404 test | api.workflows.test.ts:430 |
Comment says "real unlink" but DELETE is now DB-backed |
cli.md discovery description incomplete |
cli.md:91 |
Add DB-stored workflows to the source list |
cli-internals.md function signature stale |
cli-internals.md:95,340 |
Add options? third argument to discoverWorkflowsWithConfig |
Missing JSDoc on listWorkflowDefinitions codebaseId behavior |
workflow-definitions.ts |
Document that omitting codebaseId returns ALL records |
✅ What's Good
- DB CRUD layer is correct: Dialect abstraction, ON CONFLICT upsert with
created_atpreservation,rowCount > 0boolean check in delete — all right. - SQLite/PostgreSQL DDL parity: Inline SQLite schema exactly mirrors
022_workflow_definitions.sql, including partial index oncodebase_id WHERE codebase_id IS NOT NULL. - Discovery merge ordering is correct: DB highest priority via Map-based deduplication — simple and effective.
- Import route error handling is exemplary: Parse failure (400) and DB failure (500) paths correctly handled with logging.
- File-level JSDoc in
workflow-definitions.tsproactively clarifies the distinction from workflow runs — exactly the question every reader will have. - Bundled workflow protection in DELETE: Correctly guards before hitting the DB.
- Test refactor is an improvement: Old brittle tmpdir-based tests replaced with clean mock-based tests.
- Deviation 1 (for-loop over .filter): The TypeScript readonly narrowing constraint is real; the imperative loop is the right call.
- Import/export as separate concerns: Clean API design — raw YAML in/out distinct from JSON-centric PUT/GET.
📋 Suggested Follow-up Issues
| Issue Title | Priority |
|---|---|
| "Add error logging to DB read functions in workflow-definitions.ts" | P2 |
| "Surface DB discovery failures in WorkflowLoadResult.errors" | P2 |
| "Add test coverage for discoverWorkflowsWithConfig DB-merge logic" | P2 |
| "Update docs-web API reference for hybrid workflow storage" | P2 |
Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: ~/.archon/workspaces/coleam00/Archon/artifacts/runs/ce4945a5f0f97fe1e326aba0694f0cfc/review/
- Add try-catch to DELETE /api/workflows/:name handler to match project error handling pattern - Add error logging to upsertWorkflowDefinition (with rows[0] null guard), getWorkflowDefinition, and listWorkflowDefinitions - Remove no-op normalizeRecord function (YAGNI violation) - Add log.warn to CLI getDbWorkflows parse-fail branch (was silently dropping corrupt records) - Upgrade DB lookup catch blocks from warn to error level for unexpected infrastructure failures - Fix OpenAPI schema for export route: text/plain -> text/yaml to match actual Content-Type header - Remove incorrect export route registration order comment (3-segment path cannot conflict with 2-segment) - Update WorkflowSource JSDoc to mention 'db' variant - Expand DB merge priority comment to explain why DB wins over filesystem - Surface DB discovery failure in WorkflowLoadResult.errors array (not just logged) - Add workflow-definitions.test.ts with full DB CRUD unit test coverage - Add tests for POST /api/workflows/import, GET /api/workflows/:name/export, GET /:name DB branch, DELETE DB error path - Add workflow-definitions.test.ts to @archon/core test batch - Update CLAUDE.md: 8 Tables -> 9 Tables, add workflow_definitions entry, update API endpoint descriptions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Report: Addressing Code Review FindingsCommit: CRITICAL Fixes (3/3)1. DELETE route missing try-catch ( 2. DB CRUD layer unit tests (new 3. CLI silent parse-fail drop ( HIGH Fixes (4/4)4. 5. Import endpoint tests: 4 tests (valid → 200, invalid YAML → 400, empty → 400, DB failure → 500). 6. Export endpoint tests: 3 tests (DB hit → 200 YAML, not found → 404, DB null → filesystem → 200). 7. Read function error logging: try-catch with MEDIUM Fixes (All addressed)
LOW Fixes
Docs UpdatedCLAUDE.md: "8 Tables" → "9 Tables", Deferred: docs-web Total new tests: 26 (13 in new |
Combines workflow_definitions, users, project_members, node_states, test_results tables. Renumbers migrations to avoid collision: 022_workflow_definitions, 023_multi_user_auth, 024_node_states. Updates CLAUDE.md to reflect 13 tables. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
remote_agent_workflow_definitionsDB table; PUT/DELETE API routes now save to DB instead of filesystem; discovery merges both sources (bundled < global < repo-specific < DB).~/.archon/.archon/workflows/, repo-specific.archon/workflows/) continue to work exactly as before. TheIWorkflowStoreinterface (for workflow runs) is untouched.UX Journey
Before
After
Architecture Diagram
Before
After
Connection inventory:
api.tsworkflow-definitions.tsworkflow-discovery.tsgetDbWorkflowscallbackworkflow-discovery.tsWorkflowSource'db'to unionapi.tssqlite.tsworkflow_definitionstableLabel Snapshot
risk: lowsize: Mcore,workflows,server,clicore:db,workflows:discovery,server:apiChange Metadata
featuremultiLinked Issue
Validation Evidence (required)
bun run type-checkbun run lintbun run format:checkbun run testbun run validateoutput from workflow runce4945a5f0f97fe1e326aba0694f0cfcbun --filter @archon/web generate:types(requires running server; types regenerate automatically on nextbun run dev)Security Impact (required)
Compatibility / Migration
sqlite.ts(new table created on first start)migrations/022_workflow_definitions.sqlbefore deployingHuman Verification (required)
bun run validatesuite (type-check, lint, format, tests) passed clean in worktree environmentlistWorkflowDefinitionsaccepts optionalcodebaseIdfilterSide Effects / Blast Radius (required)
workflow list(showsdbsource tag)getDbWorkflowscallback surfaces as a discovery error (same handling as malformed YAML)Rollback Plan (required)
a2cf61daand redeploy; filesystem workflow discovery is unchanged so all existing YAML workflows remain accessibleremote_agent_workflow_definitionstable absent from DBRisks and Mitigations
migrations/022_workflow_definitions.sqlbefore upgrading