π― Workflow Studio: N8N Killer Architecture - Complete Vision
Mission: Transform Workflow Studio into the most powerful, intuitive workflow automation platform on Earth by implementing advanced content picking, data mapping, and real-time validation systems.
ποΈ Architectural Foundationβ
Existing Infrastructure (Already Built)β
-
ThorAPI Code Generation System
- OpenAPI spec β Spring Boot + TypeScript clients + JPA models
- Generated models:
ExecModule,IntegrationAccount,Workflow,Task,WorkflowState,OasOpenAPISpec - All CRUD operations + QBE filtering + pagination
-
ExecModule System
- 20+ modules (Email, REST, Stripe, AWS, Social, etc.)
IntegrationAccountrelationship: Each ExecModule can have associated IntegrationAccount for auth- Module config stored as JSON in
moduleDatafield - Metadata: className, moduleType, moduleOrder, status
-
WorkflowState
- Key-value store for workflow runtime data
- Stored per workflow:
List<WorkflowState> - Usage: Pass data between tasks/modules
- Example:
WorkflowState(name="imageUrl", stateValue="https://...")
-
OpenAPI Spec System
OasOpenAPISpecmodel with structured data- Paths, operations, schemas, components
- Used by ThorAPIController for code generation
- Fail-safe backup spec available
π¨ New Architecture: The Big Pictureβ
Core Concept: Workflow as API Contract + Data Flow Graphβ
Every workflow should:
- Define its API surface (what data sources/endpoints it uses)
- Track IntegrationAccounts (credentials for each service)
- Enable content/URL picking (select from previous module outputs)
- Support field-level data mapping (GraphQL-style field selection with Gridheim transforms)
- Provide real-time validation (sanity check after every action)
π¦ Component Architectureβ
1. Content Picker Systemβ
Purpose: Select content from:
- Workflow-defined data sources (from OpenAPI spec)
- Previous ExecModule outputs
- IntegrationAccount-scoped resources (templates, lists, etc.)
Architecture:
// New Component: ContentPicker
interface ContentPickerProps {
/** Workflow context for filtering available sources */
workflow: Workflow;
/** Type of content to pick (template, list, url, file, etc.) */
contentType: ContentType;
/** Optional filter by integration account type */
accountType?: string; // "mailchimp", "stripe", "aws-s3", etc.
/** Callback when content selected */
onSelect: (content: PickedContent) => void;
/** Enable multi-select */
multiSelect?: boolean;
}
interface PickedContent {
/** Source type */
source: "workflow-api" | "module-output" | "integration-resource";
/** Reference path for runtime resolution */
path: string; // e.g., "{{ImageGen.output.url}}" or "spec://BlogPostApi/posts"
/** Metadata for display */
label: string;
description?: string;
preview?: string | object;
}
// ContentPicker renders:
// 1. "From Previous Modules" tab β shows upstream module outputs (existing VariablePicker)
// 2. "From API Sources" tab β lists workflow's OpenAPI spec endpoints
// 3. "From Integration" tab β resources from specific IntegrationAccount
Integration with Existing System:
- Extends
VariablePickercomponent (already built) - Uses
workflow.specs(OpenAPI spec reference) for API sources - Queries
IntegrationAccountServicefor integration resources
2. URL Picker Systemβ
Purpose: Select URLs from:
- Previous module outputs (e.g.,
{{ImageGen.url}}) - Workflow API endpoints (e.g.,
GET /api/templates/{id}/download) - Static URLs with validation
Architecture:
// New Component: URLPicker
interface URLPickerProps {
/** Current field value */
value: string;
/** Available upstream modules */
availableModules: ExecModule[];
/** Workflow API spec for endpoint selection */
workflowSpec?: OasOpenAPISpec;
/** Callback on URL selected */
onChange: (url: string) => void;
}
// URLPicker renders:
// 1. Variable picker button (π―) β opens VariablePicker for module outputs
// 2. API endpoint picker (π) β opens ContentPicker filtered to URL-returning endpoints
// 3. Manual input with real-time validation
Smart Detection:
- Detect URL fields in ExecModule config automatically
- Show picker button next to any field containing "url", "uri", "endpoint", "webhook"
- Validate URL format and reachability (optional ping check)
3. IntegrationAccount Scopingβ
Current State:
ExecModulehasintegrationAccountproperty (ManyToOne relationship)IntegrationAccounthasexecModule(inverse relationship)- Can query:
findIntegrationAccountByExecModule(execModule)
Enhancement:
// New: Workflow-level IntegrationAccount registry
interface WorkflowIntegrationRegistry {
workflow: Workflow;
integrationAccounts: IntegrationAccount[];
/** Get accounts filtered by type */
getAccountsByType(type: string): IntegrationAccount[];
/** Get all accounts used by workflow tasks */
getAllUsedAccounts(): IntegrationAccount[];
}
// Service method (Java backend)
@Service
public class WorkflowIntegrationRegistryService {
/**
* Get all IntegrationAccounts referenced by workflow's ExecModules
*/
public List<IntegrationAccount> getWorkflowIntegrationAccounts(UUID workflowId) {
Workflow workflow = workflowRepo.findById(workflowId).orElseThrow();
Set<IntegrationAccount> accounts = new HashSet<>();
for (Task task : workflow.getTasks()) {
for (ExecModule module : task.getModules()) {
if (module.getIntegrationAccount() != null) {
accounts.add(module.getIntegrationAccount());
}
}
}
return new ArrayList<>(accounts);
}
/**
* Get all API/data sources defined in workflow's OpenAPI spec
*/
public List<ApiSource> getWorkflowApiSources(UUID workflowId) {
Workflow workflow = workflowRepo.findById(workflowId).orElseThrow();
if (workflow.getSpecs() == null || workflow.getSpecs().isEmpty()) {
return Collections.emptyList();
}
// Parse first spec (main API definition)
OasOpenAPISpec spec = workflow.getSpecs().get(0);
List<ApiSource> sources = new ArrayList<>();
for (OasPath path : spec.getPaths()) {
sources.add(new ApiSource(
path.getPath(),
path.getSummary(),
path.getOperations()
));
}
return sources;
}
}
UI Integration:
- New toolbar button: "π Accounts & APIs" β shows WorkflowIntegrationRegistry modal
- Lists all IntegrationAccounts used by workflow
- Lists all API endpoints defined in workflow.specs
- Quick actions: "Add Account", "Edit Account", "Test Connection"
4. Data Mapper Componentβ
Purpose: Visual field-to-field mapping between ExecModules with Gridheim transformations
Architecture:
// New Component: DataMapper
interface DataMapperProps {
/** Source module (left side) */
sourceModule: ExecModule;
/** Target module (right side) */
targetModule: ExecModule;
/** Current mapping configuration */
mapping: FieldMapping[];
/** Callback when mapping changes */
onChange: (mapping: FieldMapping[]) => void;
}
interface FieldMapping {
/** Source field path (e.g., "response.data.items[0].title") */
sourcePath: string;
/** Target field path (e.g., "config.emailSubject") */
targetPath: string;
/** Optional transformation (Gridheim Rune calc) */
transform?: GridheimRuneCalc;
}
interface GridheimRuneCalc {
/** Rune expression (e.g., "UPPER(A1)", "CONCAT(A1, ' - ', A2)") */
expression: string;
/** Preview of transformed value */
preview?: string;
}
// DataMapper UI Layout:
βββββββββββββββββββββββ¬βββββββββββββββ¬ββββββββββββββββββββββ
β SOURCE MODULE β TRANSFORM β TARGET MODULE β
β (REST API Response) β β (Email Module) β
βββββββββββββββββββββββΌβββββββββββββββΌββββββββββββββββββββββ€
β response.data.title β β UPPER() β β config.emailSubject β
β response.data.body β β (none) β β config.emailBody β
β response.status β β =IF() β β config.priority β
βββββββββββββββββββββββ΄βββββββββββββββ΄ββββββββββββββββββββββ
Features:
- Drag-and-drop field connections (left source β right target)
- Middle column shows transformation formula (Gridheim Rune calc)
- Live preview of transformed values (using sample data)
- Type validation (warn if string mapped to number field)
- Auto-suggestions for common transforms:
UPPER(),LOWER(),TRIM()CONCAT(),SPLIT(),REPLACE()IF(),SWITCH(),VLOOKUP()- Date/time formatting:
DATEFORMAT(),NOW()
Integration with Gridheim:
- Reuse Gridheim's Rune calc engine for transformations
- Expose common Excel-like functions
- Support cell references:
A1= source field,B1= transformed value
5. Real-Time Validation Systemβ
Purpose: Sanity check workflow after every action (like compilation)
Architecture:
// New Service: WorkflowValidationService
interface WorkflowValidationResult {
valid: boolean;
errors: ValidationError[];
warnings: ValidationWarning[];
suggestions: ValidationSuggestion[];
}
interface ValidationError {
severity: "error" | "warning" | "info";
taskId?: UUID;
moduleId?: UUID;
field?: string;
message: string;
quickFix?: QuickFix;
}
interface QuickFix {
label: string;
action: () => void;
}
// Validation Rules (Backend):
@Service
public class WorkflowValidationService {
/**
* Validate workflow and return actionable errors
*/
public WorkflowValidationResult validate(Workflow workflow) {
List<ValidationError> errors = new ArrayList<>();
// Rule 1: Missing IntegrationAccount
for (Task task : workflow.getTasks()) {
for (ExecModule module : task.getModules()) {
if (requiresIntegrationAccount(module) && module.getIntegrationAccount() == null) {
errors.add(new ValidationError(
"error",
task.getId(),
module.getId(),
"integrationAccount",
"Module '" + module.getName() + "' requires an IntegrationAccount",
createQuickFix("Select account", () -> showAccountPicker(module))
));
}
}
}
// Rule 2: Invalid variable references
for (Task task : workflow.getTasks()) {
for (ExecModule module : task.getModules()) {
String moduleData = module.getModuleData();
List<String> refs = extractVariableReferences(moduleData);
for (String ref : refs) {
if (!isValidReference(ref, workflow)) {
errors.add(new ValidationError(
"error",
task.getId(),
module.getId(),
"moduleData",
"Invalid variable reference: " + ref,
createQuickFix("Pick variable", () -> openVariablePicker(module))
));
}
}
}
}
// Rule 3: Missing required fields
// Rule 4: Type mismatches
// Rule 5: Unreachable tasks
// Rule 6: Circular dependencies
// Rule 7: Missing API endpoints in spec
return new WorkflowValidationResult(
errors.isEmpty(),
errors.stream().filter(e -> e.severity == "error").toList(),
errors.stream().filter(e -> e.severity == "warning").toList(),
generateSuggestions(workflow)
);
}
}
UI Integration:
- Validation panel at bottom of Workflow Studio (like VS Code Problems panel)
- Auto-runs after every workflow change (debounced 500ms)
- Shows errors, warnings, and suggestions
- Click error β jumps to offending task/module + highlights field
- Quick Fix button β applies auto-fix
Visual Indicators:
- β Red badge on task with errors
- β οΈ Yellow badge on task with warnings
- β Green checkmark on valid task
- Status bar: "β No errors" or "β 3 errors, 2 warnings"
π Data Flow Architectureβ
Runtime Execution Flowβ
1. Workflow starts
β
2. Load WorkflowState (initial data)
β
3. For each Task:
a. Resolve variable references ({{moduleName.output.field}})
b. Apply Gridheim transformations (DataMapper rules)
c. Execute ExecModule with resolved config
d. Store output in WorkflowState
β
4. Pass data to next Task/Module
β
5. Repeat until all Tasks complete
Key Components:
-
Variable Resolution Engine (Backend)
@Service
public class WorkflowVariableResolver {
/**
* Resolve {{variable}} references in moduleData
*/
public String resolveVariables(String moduleData, Map<String, Object> workflowState) {
// Pattern: {{moduleName.outputField}}
Pattern pattern = Pattern.compile("\\{\\{([^}]+)\\}\\}");
Matcher matcher = pattern.matcher(moduleData);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String varPath = matcher.group(1);
Object value = resolveVariablePath(varPath, workflowState);
matcher.appendReplacement(result, String.valueOf(value));
}
matcher.appendTail(result);
return result.toString();
}
} -
Data Transform Engine (Gridheim Integration)
@Service
public class WorkflowTransformService {
@Autowired
private GridheimRuneCalcEngine runeEngine;
/**
* Apply Gridheim Rune calc transformations
*/
public Object transform(Object sourceValue, String runeExpression) {
// Create temporary cell with source value
Cell cell = new Cell();
cell.setValue(sourceValue);
// Execute Rune calc
RuneResult result = runeEngine.evaluate(runeExpression, cell);
return result.getValue();
}
} -
WorkflowState Manager (State Persistence)
@Service
public class WorkflowStateManager {
/**
* Store module output in WorkflowState
*/
public void storeModuleOutput(Workflow workflow, ExecModule module, Map<String, Object> output) {
String moduleName = module.getName() != null ? module.getName() : module.getClassName();
// Flatten output object into WorkflowState entries
for (Map.Entry<String, Object> entry : output.entrySet()) {
String stateName = moduleName + "." + entry.getKey();
String stateValue = JSON.stringify(entry.getValue());
WorkflowState state = new WorkflowState()
.name(stateName)
.stateValue(stateValue)
.workflowId(workflow.getId());
workflowStateRepo.save(state);
}
}
}
π― Implementation Roadmapβ
Phase 1: Foundation (Week 1)β
1.1 Backend Servicesβ
Create core services:
// WorkflowIntegrationRegistryService.java
@Service
public class WorkflowIntegrationRegistryService {
public List<IntegrationAccount> getWorkflowIntegrationAccounts(UUID workflowId) { }
public List<ApiSource> getWorkflowApiSources(UUID workflowId) { }
public IntegrationAccount selectIntegrationAccount(UUID moduleId, UUID accountId) { }
}
// WorkflowValidationService.java
@Service
public class WorkflowValidationService {
public WorkflowValidationResult validate(Workflow workflow) { }
public List<ValidationError> validateModule(ExecModule module) { }
public List<String> extractVariableReferences(String json) { }
public boolean isValidReference(String ref, Workflow workflow) { }
}
// WorkflowVariableResolver.java
@Service
public class WorkflowVariableResolver {
public String resolveVariables(String moduleData, Map<String, Object> state) { }
public Object resolveVariablePath(String path, Map<String, Object> state) { }
}
// WorkflowStateManager.java
@Service
public class WorkflowStateManager {
public void storeModuleOutput(Workflow workflow, ExecModule module, Map<String, Object> output) { }
public Map<String, Object> getWorkflowState(UUID workflowId) { }
}
1.2 REST API Endpointsβ
Add validation and registry endpoints:
@RestController
@RequestMapping("/v1/workflow")
public class WorkflowStudioController {
@PostMapping("/{id}/validate")
public ResponseEntity<WorkflowValidationResult> validateWorkflow(@PathVariable UUID id) {
Workflow workflow = workflowService.findById(id).orElseThrow();
WorkflowValidationResult result = validationService.validate(workflow);
return ResponseEntity.ok(result);
}
@GetMapping("/{id}/integrations")
public ResponseEntity<List<IntegrationAccount>> getWorkflowIntegrations(@PathVariable UUID id) {
List<IntegrationAccount> accounts = registryService.getWorkflowIntegrationAccounts(id);
return ResponseEntity.ok(accounts);
}
@GetMapping("/{id}/api-sources")
public ResponseEntity<List<ApiSource>> getWorkflowApiSources(@PathVariable UUID id) {
List<ApiSource> sources = registryService.getWorkflowApiSources(id);
return ResponseEntity.ok(sources);
}
}
Phase 2: Content Picker System (Week 2)β
2.1 ContentPicker Componentβ
Extend VariablePicker with API/Integration tabs:
// ContentPicker.tsx
export const ContentPicker: React.FC<ContentPickerProps> = ({
workflow,
contentType,
accountType,
onSelect,
multiSelect = false,
}) => {
const [activeTab, setActiveTab] = useState<
"modules" | "apis" | "integrations"
>("modules");
// Fetch data sources
const { data: moduleOutputs } = useGetModuleOutputsQuery(workflow.id);
const { data: apiSources } = useGetWorkflowApiSourcesQuery(workflow.id);
const { data: integrationAccounts } = useGetWorkflowIntegrationsQuery(
workflow.id
);
return (
<Modal show={show} onClose={onCancel}>
<Tabs activeTab={activeTab} onChange={setActiveTab}>
<Tab name="modules" label="π¦ From Modules">
<VariablePicker
availableModules={moduleOutputs}
onSelect={onSelect}
/>
</Tab>
<Tab name="apis" label="π From APIs">
<ApiSourceList
sources={apiSources}
contentType={contentType}
onSelect={onSelect}
/>
</Tab>
<Tab name="integrations" label="π From Integrations">
<IntegrationResourceList
accounts={integrationAccounts}
accountType={accountType}
contentType={contentType}
onSelect={onSelect}
/>
</Tab>
</Tabs>
</Modal>
);
};
2.2 URL Picker Integrationβ
Add URL picker to FormFieldRenderer:
// Update FormFieldRenderer.tsx
const renderURLField = (fieldName: string, value: string) => {
return (
<div style={{ display: "flex", gap: "8px" }}>
<input
type="url"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="https://example.com/resource"
/>
{/* Variable picker button */}
<button onClick={() => openVariablePicker()}>π―</button>
{/* API endpoint picker button */}
<button onClick={() => openContentPicker("url")}>π</button>
{/* URL validator */}
<ValidationBadge url={value} />
</div>
);
};
Phase 3: Data Mapper (Week 3)β
3.1 DataMapper Componentβ
Build visual field mapper:
// DataMapper.tsx
export const DataMapper: React.FC<DataMapperProps> = ({
sourceModule,
targetModule,
mapping,
onChange,
}) => {
const [connections, setConnections] = useState<Connection[]>([]);
// Detect source fields from module output schema
const sourceFields = useMemo(
() => extractFieldsFromSchema(sourceModule.outputSchema),
[sourceModule]
);
// Detect target fields from module config schema
const targetFields = useMemo(
() => extractFieldsFromSchema(targetModule.configSchema),
[targetModule]
);
return (
<div className="data-mapper">
<div className="mapper-grid">
{/* Left: Source fields */}
<div className="source-panel">
<h3>{sourceModule.name || "Source"}</h3>
{sourceFields.map((field) => (
<FieldNode
key={field.path}
field={field}
onDragStart={() => handleDragStart(field)}
/>
))}
</div>
{/* Middle: Transformations */}
<div className="transform-panel">
{mapping.map((map, idx) => (
<TransformEditor
key={idx}
mapping={map}
onUpdate={(newMap) => updateMapping(idx, newMap)}
/>
))}
</div>
{/* Right: Target fields */}
<div className="target-panel">
<h3>{targetModule.name || "Target"}</h3>
{targetFields.map((field) => (
<FieldNode
key={field.path}
field={field}
onDrop={(sourceField) => createMapping(sourceField, field)}
/>
))}
</div>
</div>
{/* Visual connections (SVG lines) */}
<ConnectionLines connections={connections} />
</div>
);
};
3.2 Gridheim Rune Calc Integrationβ
Add transformation formula editor:
// TransformEditor.tsx
const TransformEditor: React.FC<{ mapping: FieldMapping }> = ({ mapping }) => {
const [formula, setFormula] = useState(mapping.transform?.expression || "");
const [preview, setPreview] = useState<string>("");
// Live preview of transformation
useEffect(() => {
if (formula && mapping.sourceValue) {
const result = evaluateGridheimFormula(formula, mapping.sourceValue);
setPreview(result);
}
}, [formula, mapping.sourceValue]);
return (
<div className="transform-editor">
<input
value={formula}
onChange={(e) => setFormula(e.target.value)}
placeholder="=UPPER(A1)"
/>
<div className="preview">
Preview: <code>{preview}</code>
</div>
<FunctionPicker onSelect={(fn) => setFormula(fn)} />
</div>
);
};
Phase 4: Real-Time Validation (Week 4)β
4.1 Validation Panel Componentβ
Build VS Code-style problems panel:
// ValidationPanel.tsx
export const ValidationPanel: React.FC<{ workflow: Workflow }> = ({
workflow,
}) => {
const [validationResult, setValidationResult] =
useState<WorkflowValidationResult | null>(null);
// Auto-validate on workflow changes (debounced)
useEffect(() => {
const timer = setTimeout(() => {
validateWorkflow(workflow.id).then(setValidationResult);
}, 500);
return () => clearTimeout(timer);
}, [workflow]);
if (!validationResult) {
return <div>Validating...</div>;
}
return (
<div className="validation-panel">
<div className="panel-header">
{validationResult.valid ? (
<span>β
No errors</span>
) : (
<span>
β {validationResult.errors.length} errors, β οΈ{" "}
{validationResult.warnings.length} warnings
</span>
)}
</div>
<div className="error-list">
{validationResult.errors.map((error, idx) => (
<ErrorItem
key={idx}
error={error}
onJumpTo={() => jumpToError(error)}
onQuickFix={() => error.quickFix?.action()}
/>
))}
</div>
</div>
);
};
4.2 Visual Error Indicatorsβ
Add badges to TaskNode:
// Update WorkflowCanvas.tsx TaskNode rendering
const TaskNode = ({ data }) => {
const { errors, warnings } = useTaskValidation(data.task.id);
return (
<div className="task-node">
<div className="task-header">
{data.task.name}
{/* Validation badges */}
{errors > 0 && <span className="badge error">β {errors}</span>}
{warnings > 0 && <span className="badge warning">β οΈ {warnings}</span>}
{errors === 0 && warnings === 0 && (
<span className="badge success">β
</span>
)}
</div>
{/* Rest of task rendering... */}
</div>
);
};
π Data Model Extensionsβ
New Models (Add to OpenAPI Spec)β
# api-source.yaml
ApiSource:
type: object
properties:
path:
type: string
description: API endpoint path (e.g., "/api/templates")
method:
type: string
enum: [GET, POST, PUT, DELETE, PATCH]
summary:
type: string
responseType:
type: string
description: Response content type
outputSchema:
type: object
description: JSON schema of response
# field-mapping.yaml
FieldMapping:
type: object
properties:
sourceModule:
$ref: "#/components/schemas/ExecModule"
targetModule:
$ref: "#/components/schemas/ExecModule"
sourcePath:
type: string
example: "response.data.title"
targetPath:
type: string
example: "config.emailSubject"
transform:
$ref: "#/components/schemas/GridheimRuneCalc"
# gridheim-rune-calc.yaml
GridheimRuneCalc:
type: object
properties:
expression:
type: string
description: Rune formula (e.g., "=UPPER(A1)")
preview:
type: string
description: Preview of transformed value
# workflow-validation-result.yaml
WorkflowValidationResult:
type: object
properties:
valid:
type: boolean
errors:
type: array
items:
$ref: "#/components/schemas/ValidationError"
warnings:
type: array
items:
$ref: "#/components/schemas/ValidationError"
suggestions:
type: array
items:
$ref: "#/components/schemas/ValidationSuggestion"
ValidationError:
type: object
properties:
severity:
type: string
enum: [error, warning, info]
taskId:
type: string
format: uuid
moduleId:
type: string
format: uuid
field:
type: string
message:
type: string
quickFix:
$ref: "#/components/schemas/QuickFix"
π¨ UI/UX Enhancementsβ
Workflow Studio Toolbarβ
Add new buttons:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β πΎ Save βΆοΈ Run βΈοΈ Pause π Refresh π Accounts & APIs β
β β
β π― Variables π URLs π Data Mapper β
Validate β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Task Context Menuβ
Right-click on task:
ββββββββββββββββββββββββββββββ
β βοΈ Configure β
β π§© Add Module β
β π― Pick Variables β
β π Map Data β
β π Select URLs β
β π Set Integration Account β
β β
Validate Task β
β ποΈ Delete Task β
ββββββββββββββββββββββββββββββ
π₯ Success Criteriaβ
-
β Content Picker
- Can select from module outputs, API endpoints, integration resources
- Multi-source support (modules, APIs, integrations)
- Type filtering (templates, lists, URLs, files)
-
β URL Picker
- Smart detection of URL fields
- Variable picker integration (π―)
- API endpoint picker integration (π)
- Real-time URL validation
-
β IntegrationAccount Management
- Workflow-level account registry
- Auto-detect accounts used by modules
- Quick account selection/switching
- Type-based filtering (QBE)
-
β Data Mapper
- Visual field-to-field connections (drag-and-drop)
- Gridheim Rune calc transformations
- Live preview of transformed values
- Auto-suggestions for common transforms
-
β Real-Time Validation
- Auto-validate after every workflow change
- Problems panel (errors, warnings, suggestions)
- Visual error indicators on canvas
- Quick Fix actions
-
β OpenAPI Spec Integration
- Workflow.specs defines available data sources
- API endpoint picker uses spec metadata
- Schema-based field detection
π Documentation Requirementsβ
-
Architecture Decision Record (ADR)
- Why content picker vs manual entry
- Why Gridheim for transformations
- Why real-time validation vs on-save
-
Developer Guide
- How to add new ContentPicker sources
- How to create custom validation rules
- How to extend DataMapper transforms
-
User Guide
- How to use Content Picker
- How to map data between modules
- How to fix validation errors
π Next Stepsβ
Phase 1 (Backend Foundation):
- Create
WorkflowIntegrationRegistryService - Create
WorkflowValidationService - Create
WorkflowVariableResolver - Add REST API endpoints
Phase 2 (Content Picker):
- Create
ContentPickercomponent - Extend
VariablePickerwith tabs - Build
ApiSourceListcomponent - Build
IntegrationResourceListcomponent
Phase 3 (Data Mapper):
- Create
DataMappercomponent - Build
FieldNodecomponent - Build
TransformEditorcomponent - Integrate Gridheim Rune calc engine
Phase 4 (Validation):
- Create
ValidationPanelcomponent - Add visual error badges to TaskNode
- Implement auto-validation system
- Add Quick Fix actions
This architecture transforms ValkyrAI into the most powerful workflow automation platform on Earth - a true N8N killer! π―π₯