Mind's Eye Platform — Official Technical Documentation
Version: 1.0.0 | Last Updated: December 6, 2025 | Status: MVP Active
An internal operating system for agents, ledgers, workflows, automation, cognitive layers, and orchestration
PROMPT 1 — System Overview (Engine Level, High Altitude)
Executive Summary
The Mind's Eye Platform is a cognitive automation operating system that treats workflows, data streams, and agent behaviors as first-class objects in a unified execution environment. Unlike traditional automation tools that connect APIs through linear pipelines, Mind's Eye implements a dual-engine architecture:
- MORCO (Mind's Eye Workflow Orchestration System) — The deterministic workflow execution runtime
- Hunting Engine — The probabilistic event detection and annotation system
Together, these engines form an Internal OS where Gmail serves as the canonical ledger, Google Workspace acts as the filesystem, and WebDocket provides the task management layer.
System Purpose
Mind's Eye exists to solve a fundamental problem in enterprise automation: the gap between structured workflows and unstructured reality.
Traditional automation tools assume:
- Clean, structured inputs
- Predictable execution paths
- Single-source triggers
- Stateless operations
But real business processes involve:
- Messy, multi-source data streams
- Context-dependent branching
- Long-running state across systems
- Human-in-the-loop decision points
- Post-hoc analysis and learning
Mind's Eye bridges this gap by providing:
- MORCO for structured, repeatable workflows
- Hunting Engine for pattern detection in unstructured streams
- Gmail Ledger for canonical state across all operations
- Cognitive Layers for progressively intelligent automation
Core Vision and Principles
Vision Statement
"Build an automation OS where every event, decision, and state change is logged, traceable, queryable, and learnable — enabling systems that evolve from rule-based automation to cognitive orchestration."
Foundational Principles
1. Ledger-First Architecture
- Gmail is the source of truth for all workflow state
- Every significant event generates a ledger entry
- Machine-readable logs enable future analytics and learning
- Audit trails are automatic, not bolted-on
2. Explicit State Management
- Correlation IDs track complete workflow runs across services
- Run state is persisted at every step
- No hidden state or implicit context
- Replay and debugging are first-class operations
3. Binary Logic for Branching
- All conditional execution uses explicit boolean expressions
- Steps declare:
should_run_expr,on_success,on_failure - No magic — every path is documented
- Deterministic execution, even with external dependencies
4. Service Orchestration, Not Integration
- Connectors abstract service APIs behind consistent interfaces
- Workflows compose services, not endpoints
- Separation of concerns: orchestration logic vs. service implementation
5. Progressive Cognitive Enhancement
- Start with structured workflows (L1-L2)
- Add pattern detection (L3-L4)
- Evolve toward predictive automation (L5)
- The system learns from its own ledger
The Hunting Engine: Why It Exists
The Problem
MORCO handles known workflows — processes you can define upfront with clear triggers, steps, and outcomes. But what about:
- Detecting anomalies in invoice patterns?
- Finding related dockets across disconnected systems?
- Discovering implicit workflows that humans perform manually?
- Learning from past runs to predict future failures?
These require pattern recognition, not just execution.
The Solution: Hunting Engine
The Hunting Engine is a probabilistic event stream processor that:
- Monitors Sources: Gmail, WebDocket, Forms, Docs, external APIs
- Detects Patterns: Uses hunts (detection rules) to find interesting events
- Creates Annotations: Tags events with metadata, relationships, and insights
- Writes to Ledger: Feeds discoveries back into Gmail for MORCO consumption
Example Use Cases:
- Hunt: "Find all invoices submitted by the same vendor within 24 hours"
- Hunt: "Detect when a docket status changes but no action was logged"
- Hunt: "Identify form submissions with suspicious email patterns"
- Hunt: "Discover implicit dependencies between workflows by correlation ID analysis"
Architecture Philosophy
MORCO = Deterministic ("Do this when X happens")
Hunting Engine = Probabilistic ("Tell me when you see X pattern")
Together = Reactive + Proactive Automation
The Hunting Engine creates a cognitive feedback loop:
- MORCO executes workflows → generates ledger events
- Hunting Engine analyzes ledger → detects patterns
- Annotations feed back into MORCO → trigger new workflows
- System becomes progressively smarter
The Workflow OS: MORCO and the Ledger System
MORCO as Runtime
MORCO is the execution kernel of Mind's Eye. It:
- Loads workflow definitions (JSON specifications)
- Receives triggers (Forms, WebDocket, Chatbot, Cron)
- Executes steps sequentially with conditional branching
- Manages state across service boundaries
- Writes execution traces to the Gmail ledger
MORCO is to Mind's Eye what the Linux kernel is to an OS — it schedules work, manages resources, and provides the execution environment.
Gmail as the Canonical Ledger
In traditional systems:
- Logs are ephemeral (files, stdout)
- State is scattered (databases, caches, APIs)
- Audit trails are reconstructed
In Mind's Eye:
- Gmail IS the state
- Every workflow event = one ledger email
- Structured subjects enable instant querying:
[LEDGER] workflow=invoice-intake event=step_completed cid=uuid - Email labels create automatic categorization
- Native search, filters, and retention policies
Why Gmail?
- Durability: Google's infrastructure, built-in redundancy
- Queryability: Powerful search with filters, labels, time ranges
- Accessibility: Any service can read/write via API
- Familiarity: Everyone knows email
- Machine + Human Readable: Structured data in familiar format
- Free: Up to 15GB per account
The Ledger System as Filesystem
Think of Gmail labels as directories:
ledger/
workflows/
invoice-intake/
run-uuid-001/
run-uuid-002/
meeting-notes/
run-uuid-003/
errors/
validations/
analytics/
Each email is an immutable log entry with:
- Subject = metadata (workflow, event, correlation ID)
- Body = full state snapshot (trigger, inputs, outputs, errors)
- Timestamp = built-in
- Thread = related events automatically grouped
Cognitive Layers (L1-L5)
Mind's Eye is designed for progressive enhancement — start simple, add intelligence over time.
Layer 1: Direct Execution (Current MVP)
- Trigger → Workflow → Steps → Services → Ledger
- No conditionals, no branching
- Example: Form submit → Create doc → Send email
Layer 2: Conditional Logic (Current MVP)
- Binary logic:
should_run_exprevaluates to 1 or 0 - Branching:
on_success,on_failure - Example: Validate invoice → if valid, create docket; if invalid, notify submitter
Layer 3: Pattern Detection (Hunting Engine MVP)
- Hunts monitor event streams
- Annotations tag interesting patterns
- Example: Detect duplicate invoices, trigger validation workflow
Layer 4: Context-Aware Orchestration (Roadmap)
- Workflows access historical ledger data
- Decisions based on past runs
- Example: Prioritize dockets based on previous similar cases
Layer 5: Predictive Automation (Future)
- ML models trained on ledger data
- Proactive workflow triggers
- Example: Predict invoice disputes before they occur, pre-emptively assign reviewers
Current Status: L1-L3 implemented in MVP
Why This Architecture Is Different
Standard Automation Tools
Zapier / IFTTT Model:
- Simple trigger-action pairs
- Limited branching
- No state management
- Logs are ephemeral
- No learning or pattern detection
n8n / Integromat Model:
- Visual workflow builder
- More complex logic
- Some state management
- Logs in proprietary format
- Limited cross-workflow analysis
Google Apps Script Model:
- Code-based automation
- Full programmatic control
- Manual state management
- Logs are console output
- No unified execution model
Xano / Backend-as-a-Service Model:
- Database-centric
- API generation
- Custom business logic
- Logs are application-level
- No workflow abstraction
Mind's Eye Differences
| Feature | Traditional Tools | Mind's Eye |
|---|---|---|
| State Management | Implicit, scattered | Explicit, ledger-first |
| Logs | Ephemeral, proprietary | Durable, Gmail-based |
| Traceability | Correlation ID if lucky | Correlation ID mandatory |
| Branching | Visual/nested | Binary logic, explicit |
| Pattern Detection | None | Hunting Engine |
| Learning | None | Cognitive layers |
| Audit Trail | Paid add-on | Built-in |
| Multi-Workflow | Separate | Unified via ledger |
| Replay | Impossible | Ledger + correlation ID |
| Cost | Per execution | Gmail + compute |
Platform Comparison
vs. Zapier
Zapier Strengths:
- 5000+ integrations
- No code required
- Fast to set up
Zapier Limitations:
- Limited to 1 trigger per Zap
- Complex branching requires premium tier
- No unified state across Zaps
- No learning or pattern detection
- Expensive at scale
Mind's Eye Advantage:
- Unlimited complexity in workflows
- Unified state via ledger
- Cross-workflow analysis via Hunting Engine
- Own your data and execution
- Gmail ledger provides free audit trail
vs. n8n
n8n Strengths:
- Self-hosted option
- Visual workflow designer
- More complex logic than Zapier
n8n Limitations:
- Still workflow-centric, not OS-centric
- Logs are in proprietary format
- No pattern detection layer
- State management is implicit
Mind's Eye Advantage:
- Ledger-first architecture
- Dual engine (MORCO + Hunting)
Gmail as queryable state
Designed for cognitive enhancement
vs. Google Apps Script
Apps Script Strengths:
- Full programmatic control
- Native Google Workspace integration
- Free for Google Workspace users
Apps Script Limitations:
- No workflow abstraction
- Manual state management
- Code is scattered across projects
- No unified execution model
- Limited to 6 min execution time
Mind's Eye Advantage:
- Workflow JSON = declarative configuration
- Automatic state management
- Unified orchestration layer
- Ledger provides cross-script visibility
- Can call Apps Script as connector
vs. Xano
Xano Strengths:
- Full backend stack
- Database included
- API generation
Xano Limitations:
- Database-centric, not workflow-centric
- No workflow execution model
- Manual orchestration logic
- No pattern detection
Mind's Eye Advantage:
- Workflow-first design
- Gmail ledger = distributed database
- Built-in orchestration runtime
- Hunting Engine for insights
System Diagram: The Full Stack
┌─────────────────────────────────────────────────────────────┐
│ MIND'S EYE PLATFORM │
│ (Internal OS Layer) │
└─────────────────────────────────────────────────────────────┘
┌──────────────────────┐ ┌──────────────────────────┐
│ MORCO ENGINE │ │ HUNTING ENGINE │
│ (Deterministic) │◄────────┤ (Probabilistic) │
│ │ │ │
│ • Workflow Executor │ │ • Stream Processor │
│ • Binary Logic │ │ • Pattern Matcher │
│ • State Manager │ │ • Annotation Engine │
│ • Connector Layer │ │ • Relationship Detector │
└──────────┬───────────┘ └────────┬─────────────────┘
│ │
└──────────┬───────────────────┘
│
▼
┌──────────────────────┐
│ GMAIL LEDGER │
│ (Canonical State) │
│ │
│ • Structured Logs │
│ • Correlation IDs │
│ • Queryable History │
│ • Auto-Categorized │
└──────────┬───────────┘
│
─────────────┴─────────────
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ TRIGGERS │ │ CONNECTORS │
│ │ │ │
│ • Forms │ │ • Gmail │
│ • WebDocket │ │ • Docs │
│ • Chatbot │ │ • Forms │
│ • Cron │ │ • WebDocket │
│ • Ledger │ │ • External │
└───────────────┘ └──────────────┘
Analogies for Understanding Mind's Eye
Computer Science Analogies
MORCO = Operating System Kernel
- Schedules workflow execution
- Manages resources (API calls, rate limits)
- Provides isolation (correlation IDs)
- Handles errors (try/catch semantics)
Hunting Engine = Intrusion Detection System (IDS)
- Monitors network traffic (event streams)
- Detects patterns (hunts)
- Flags anomalies (annotations)
- Writes to SIEM (ledger)
Gmail Ledger = Write-Ahead Log (WAL)
- Immutable append-only log
- Durable before application state changes
- Enables replay and recovery
- Source of truth for state
Workflows = Programs
- Declarative specifications
- Executed by runtime
- Have inputs, outputs, and side effects
Connectors = System Calls
- Abstract hardware (external APIs)
- Provide consistent interface
- Handle errors and retries
Business Process Analogies
MORCO = Standard Operating Procedure (SOP)
- Documented, repeatable process
- Clear steps and decision points
- Everyone follows the same procedure
Hunting Engine = Quality Assurance (QA)
- Reviews completed work
- Finds patterns and issues
- Reports back to improve processes
Gmail Ledger = Audit Log Book
- Every transaction recorded
- Timestamped and signed
- Can be reviewed later
- Required for compliance
Correlation ID = Case Number
- Tracks a single case across departments
- All documents reference the same ID
- Easy to find all related work
Systems Thinking: Feedback Loops
Mind's Eye implements multiple feedback loops for continuous improvement:
Loop 1: Execution → Logging → Analysis
Workflow Runs → Ledger Events → Manual Review → Workflow Updates
Loop 2: Pattern Detection → Automation
Loop 2: Pattern Detection → Automation
Hunting Engine Detects Pattern → Create New Workflow → More Data → Better Patterns
Loop 3: Failure Learning
Workflow Fails → Error Logged → Hunt Detects Similar Failures → Proactive Prevention
Loop 4: Cross-Workflow Optimization
Multiple Workflows → Ledger Analysis → Find Redundancy → Consolidate → More Efficient
These loops enable emergent intelligence — the system becomes smarter not through explicit programming, but through analysis of its own behavior.
PROMPT 2 — Mind's Eye Engine Architecture (The Core Block)
Internal Object Model
The Mind's Eye platform operates on six core object types that form the foundation of both MORCO and the Hunting Engine.
Object Type 1: Sources
Definition: External systems that generate events or can be queried for data.
Attributes:
-
source_id: Unique identifier (e.g.,gmail-primary,webdocket-prod) -
source_type: Service category (gmail,google_forms,webdocket,google_docs,chatbot) -
connection_config: Authentication and endpoint configuration -
poll_interval: For polling sources (e.g., every 5 minutes) -
webhook_url: For push sources -
is_active: Whether the source is currently monitored
Examples:
{
"source_id": "gmail-primary",
"source_type": "gmail",
"connection_config": {
"account": "[ledger@company.com](mailto:ledger@company.com)",
"labels": ["MORCO/Workflows", "MORCO/Triggers"]
},
"poll_interval": null,
"webhook_url": null,
"is_active": true
}
Object Type 2: Streams
Definition: Continuous flows of events from sources, with optional filtering and transformation.
Attributes:
-
stream_id: Unique identifier -
source_id: Parent source -
filter_expr: Optional filter (e.g., "only emails with subject containing [TRIGGER]") -
transform_rules: Optional transformation logic -
buffer_size: How many events to hold in memory -
backpressure_strategy: What to do when buffer is full
Examples:
{
"stream_id": "invoice-form-submissions",
"source_id": "google-forms-finance",
"filter_expr": "form_id == 'finance_invoice_intake'",
"transform_rules": [
{"field": "amount", "type": "parse_number"},
{"field": "due_date", "type": "parse_date"}
],
"buffer_size": 100,
"backpressure_strategy": "drop_oldest"
}
Object Type 3: Events
Definition: Individual occurrences in a stream, representing something that happened.
Attributes:
-
event_id: Unique identifier (UUID) -
stream_id: Parent stream -
event_type: Classification (e.g.,form_submit,docket_created,email_received) -
timestamp: When the event occurred -
payload: The event data (JSON) -
metadata: Additional context (source IP, user agent, etc.) -
correlation_id: Links this event to related events
Examples:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"stream_id": "invoice-form-submissions",
"event_type": "form_submit",
"timestamp": "2025-12-06T07:30:00Z",
"payload": {
"vendor_name": "Acme Corp",
"invoice_number": "INV-2025-001",
"amount": 5000.00
},
"metadata": {
"form_id": "finance_invoice_intake",
"submitter_email": "[vendor@acme.com](mailto:vendor@acme.com)"
},
"correlation_id": null
}
Object Type 4: Hunts
Definition: Pattern detection rules that analyze event streams to find interesting occurrences.
Attributes:
-
hunt_id: Unique identifier -
hunt_name: Human-readable name -
description: What pattern this hunt detects -
stream_ids: Which streams to monitor -
detection_logic: The pattern matching rule -
window_size: Time window for pattern detection (e.g., "24 hours") -
threshold: How many matches constitute a detection -
action: What to do when pattern is detected
Examples:
{
"hunt_id": "duplicate-invoice-detector",
"hunt_name": "Duplicate Invoice Detection",
"description": "Detects when the same vendor submits invoices with identical invoice numbers within 24 hours",
"stream_ids": ["invoice-form-submissions"],
"detection_logic": {
"group_by": ["payload.vendor_name", "payload.invoice_number"],
"count": ">= 2",
"within": "24h"
},
"window_size": "24h",
"threshold": 2,
"action": {
"type": "create_annotation",
"trigger_workflow": "duplicate-invoice-review"
}
}
Object Type 5: Hunt Runs
Definition: Executions of hunts, tracking what was detected and when.
Attributes:
-
hunt_run_id: Unique identifier -
hunt_id: Parent hunt -
start_time: When the hunt run began -
end_time: When the hunt run completed -
events_analyzed: Count of events processed -
matches_found: Count of pattern matches -
annotations_created: Count of annotations generated -
status:running,completed,failed -
error: If failed, the error details
Examples:
{
"hunt_run_id": "hr-20251206-073000",
"hunt_id": "duplicate-invoice-detector",
"start_time": "2025-12-06T07:30:00Z",
"end_time": "2025-12-06T07:30:05Z",
"events_analyzed": 47,
"matches_found": 1,
"annotations_created": 2,
"status": "completed",
"error": null
}
Object Type 6: Annotations
Definition: Metadata tags applied to events by hunts, enriching them with insights.
Attributes:
-
annotation_id: Unique identifier -
event_id: Which event is being annotated -
hunt_run_id: Which hunt run created this annotation -
annotation_type: Classification (e.g.,duplicate,anomaly,relationship) -
confidence: 0.0 to 1.0, how confident the detection is -
details: Structured data about the annotation -
related_events: Links to other relevant events -
timestamp: When the annotation was created
Examples:
{
"annotation_id": "ann-001",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"hunt_run_id": "hr-20251206-073000",
"annotation_type": "duplicate",
"confidence": 0.95,
"details": {
"reason": "Same vendor, same invoice number",
"duplicate_of": "event-550e8400-e29b-41d4-a716-446655440001",
"time_delta": "3 hours"
},
"related_events": ["550e8400-e29b-41d4-a716-446655440001"],
"timestamp": "2025-12-06T07:30:05Z"
}
Event Flow Through the Engine
The Complete Journey
┌─────────────┐
│ SOURCE │ (Gmail, Forms, WebDocket, etc.)
└──────┬──────┘
│ New event occurs
▼
┌─────────────┐
│ STREAM │ Filter & Transform
└──────┬──────┘
│ Event passes filter
▼
┌─────────────┐
│ EVENT │ Created with UUID, stored
└──────┬──────┘
│
├──────────────────────────┐
│ │
▼ ▼
┌─────────────┐ ┌───────────────┐
│ MORCO │ │ HUNTING │
│ TRIGGER │ │ ENGINE │
│ MATCHER │ │ ANALYZER │
└──────┬──────┘ └───────┬───────┘
│ │
│ Match found │ Pattern detected
▼ ▼
┌─────────────┐ ┌───────────────┐
│ WORKFLOW │ │ HUNT RUN │
│ EXECUTION │ │ │
└──────┬──────┘ └───────┬───────┘
│ │
│ Steps run │ Annotations created
▼ ▼
┌─────────────┐ ┌───────────────┐
│ CONNECTORS │ │ ANNOTATIONS │
└──────┬──────┘ └───────┬───────┘
│ │
│ Actions executed │
▼ ▼
┌──────────────────────────────────┐
│ GMAIL LEDGER │
│ • workflow_triggered │
│ • step_completed │
│ • pattern_detected │
│ • annotation_created │
└──────────────────────────────────┘
Step 1: Event Ingestion
- Source generates event (form submit, webhook, etc.)
- Stream receives and validates event
- Event object created with UUID
Step 2: Trigger Matching
for each workflow in registry:
if workflow.trigger.matches(event):
create_run_context(workflow, event)
queue_for_execution(run_context)
Step 3: Run Context Creation
{
"run_id": "run-550e8400",
"correlation_id": "550e8400-e29b-41d4-a716-446655440000",
"workflow_name": "invoice-intake-and-validation",
"trigger_event": {...},
"step_results": {},
"current_step": null,
"status": "queued",
"created_at": "2025-12-06T07:30:00Z"
}
Step 4: Orchestrator Picks Up Run
- Dequeue from execution queue
- Set
status = "running" - Write initial ledger email:
event=workflow_triggered
Step 5: Step-by-Step Execution
For each step in workflow.steps:
↓
Evaluate binary_logic.should_run_expr
↓
If evaluates to 0 → Skip step, continue
If evaluates to 1 → Execute step
↓
Resolve template variables in step.inputs
↓
Call connector: connector.execute(step.service, step.action, resolved_inputs)
↓
Store result in run_state.step_results[[step.id](http://step.id)]
↓
If step.binary_logic.log_to_ledger == true:
Write ledger email: event=step_completed
↓
If connector returned error:
Handle via step.binary_logic.on_failure
Else:
Continue via step.binary_logic.on_success
Step 6: Completion
- Set
status = "completed"or"failed" - Write final ledger email:
event=workflow_completedorevent=workflow_failed - Update run record in database (if using internal DB)
Detailed Flow: Hunting Engine Path
Step 1: Continuous Stream Analysis
for each hunt in active_hunts:
events = get_events_in_window([hunt.stream](http://hunt.stream)_ids, hunt.window_size)
matches = apply_detection_logic(hunt.detection_logic, events)
if matches.length >= hunt.threshold:
create_hunt_run(hunt, matches)
Step 2: Hunt Run Execution
- Create hunt run record
- Apply annotation logic to matched events
- Generate annotations with confidence scores
Step 3: Annotation Creation
for each match in matches:
annotation = {
event_id: match.event_id,
type: hunt.annotation_type,
confidence: calculate_confidence(match),
details: extract_details(match),
related_events: find_related(match)
}
store_annotation(annotation)
if hunt.action.trigger_workflow:
create_trigger_event_for_morco(annotation)
Step 4: Feedback to MORCO
- If hunt specifies
trigger_workflow - Create synthetic event with
entry_point = "hunting_engine" - Event payload includes annotation details
- MORCO picks up and executes specified workflow
Step 5: Ledger Integration
- Write ledger email:
event=pattern_detected - Include hunt details, matched events, annotations
- This makes pattern detections queryable like workflow runs
Run State Management
The Run State Object
Every workflow execution maintains a run state object that persists throughout the entire workflow.
Structure:
interface RunState {
// Identity
run_id: string; // Unique run identifier
correlation_id: string; // Links related runs
workflow_name: string; // Which workflow is running
// Execution context
trigger: {
event_id: string;
event_type: string;
payload: any;
timestamp: string;
};
// Step results (the key to data flow)
step_results: Record<string, {
outputs: any;
status: 'success' | 'failed';
error?: string;
started_at: string;
completed_at: string;
}>;
// Current execution state
current_step: string | null;
status: 'queued' | 'running' | 'completed' | 'failed';
// Timestamps
created_at: string;
started_at?: string;
completed_at?: string;
// Ledger tracking
ledger_emails_sent: string[]; // Message IDs
// Error handling
error?: {
step_id: string;
message: string;
stack?: string;
};
}
State Persistence Strategy
Option 1: In-Memory Only (Simple, good for MVP)
- Run state lives in memory during execution
- Only persisted via ledger emails
- If orchestrator crashes, can reconstruct from ledger
Option 2: Database Backed (Production)
- Run state written to DB after each step
- Enables:
- Active run monitoring
- Mid-workflow recovery
- Real-time dashboards
- Run history queries
Option 3: Hybrid (Recommended)
- In-memory during execution (fast)
- Write to DB at key checkpoints (durable)
- Always write to ledger (audit trail)
Correlation IDs: The Tracing System
Purpose
Correlation IDs solve the distributed tracing problem: when a workflow spans multiple services, how do you track all related actions?
How They Work
1. ID Generation
- When a workflow run is created, generate a UUID
- This becomes the
correlation_idfor the entire run - All subsequent actions inherit this ID
2. ID Propagation
- Every connector call receives the correlation ID
- Connectors include it in:
- API calls (as header:
X-Correlation-ID) - Ledger emails (in subject and body)
- External system metadata (if supported)
- API calls (as header:
3. Cross-Service Tracing
Form Submit
↓ correlation_id: abc-123
Workflow Triggered
↓ correlation_id: abc-123
Step 1: Create Doc → Doc metadata includes abc-123
↓ correlation_id: abc-123
Step 2: Create Docket → Docket custom field = abc-123
↓ correlation_id: abc-123
Step 3: Send Email → Email subject includes abc-123
↓ correlation_id: abc-123
Ledger Email → Subject: [LEDGER] cid=abc-123
4. Ledger Query
Gmail search: "[LEDGER] cid=abc-123"
→ Returns all ledger emails for this workflow run
→ Complete audit trail, timestamped, with full state
Cross-Workflow Correlation
Sometimes workflows trigger other workflows:
Invoice Intake (cid: abc-123)
↓ Creates docket
Docket Status Change (new cid: def-456)
↓ References original invoice
↓ metadata.parent_correlation_id = abc-123
Hunting Engine can detect these relationships:
{
"hunt_name": "Workflow Chain Detector",
"detection_logic": {
"find": "events where metadata contains parent_correlation_id",
"create_annotation": {
"type": "workflow_chain",
"details": "Links workflows: abc-123 → def-456"
}
}
}
Ledger Email Integration
When Ledger Emails Fire
Each workflow defines which events trigger ledger emails:
{
"gmail_ledger": {
"enabled": true,
"ledger_recipient": "ledger@company.com",
"log_events": [
"workflow_triggered",
"step_completed",
"validation_failed",
"error"
]
}
}
Individual steps can override:
{
"step": {
"id": "step_3",
"binary_logic": {
"log_to_ledger": true // Force log even if not in global list
}
}
}
Ledger Email Format
Subject Template:
[LEDGER] workflow={workflow_name} event={event_type} cid={correlation_id}
Body Template:
WORKFLOW: {workflow_name}
EVENT: {event_type}
CORRELATION_ID: {correlation_id}
RUN_ID: {run_id}
TIMESTAMP: {timestamp}
=== TRIGGER ===
SOURCE: {trigger.source}
TYPE: {trigger.event_type}
PAYLOAD:
{trigger.payload | json_pretty}
=== STEP ===
STEP_ID: {[step.id](http://step.id)}
SERVICE: {step.service}
ACTION: {step.action}
INPUTS:
{step.inputs | json_pretty}
OUTPUTS:
{step.outputs | json_pretty}
=== STATUS ===
STATUS: {status}
ERROR: {error | default: "none"}
=== METADATA ===
RUN_DURATION: {duration_ms}ms
STEPS_COMPLETED: {steps_completed} / {total_steps}
LEDGER_EMAILS_SENT: {ledger_count}
Gmail Label Strategy
Automatic labeling for organization:
MORCO/Ledger (all ledger emails)
MORCO/Workflows/{name} (per workflow)
MORCO/Events/{type} (per event type)
MORCO/Status/Success (successful runs)
MORCO/Status/Failed (failed runs)
MORCO/Hunting (pattern detections)
Step Results Storage
The Step Results Map
Each step's outputs are stored in run_state.step_results[step_id]:
run_state.step_results = {
"step_1": {
"outputs": {
"is_valid": true,
"normalized_payload": {...}
},
"status": "success",
"started_at": "2025-12-06T07:30:01Z",
"completed_at": "2025-12-06T07:30:02Z"
},
"step_2": {
"outputs": {
"doc_id": "1abc123",
"doc_url": "https://docs.google.com/..."
},
"status": "success",
"started_at": "2025-12-06T07:30:02Z",
"completed_at": "2025-12-06T07:30:04Z"
}
}
Template Resolution
Later steps can reference earlier step outputs using template syntax:
In Workflow JSON:
{
"step_3": {
"inputs": {
"doc_url": "step_2.outputs.doc_url",
"validation_result": "step_[1.outputs.is](http://1.outputs.is)_valid"
}
}
}
Resolution at Runtime:
function resolveTemplates(inputs: any, runState: RunState): any {
const resolved = {};
for (const [key, value] of Object.entries(inputs)) {
if (typeof value === 'string' && value.startsWith('step_')) {
// Parse reference: "step_2.outputs.doc_url"
const [stepId, ...path] = value.split('.');
resolved[key] = getNestedValue(runState.step_results, [stepId, ...path]);
} else if (typeof value === 'string' && value.startsWith('trigger.')) {
// Parse reference: "trigger.vendor_name"
const path = value.substring(8); // Remove 'trigger.'
resolved[key] = getNestedValue(runState.trigger.payload, path.split('.'));
} else {
resolved[key] = value;
}
}
return resolved;
}
Binary Logic and Branching
The Binary Logic Block
Every step includes a binary_logic configuration:
{
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_3",
"on_failure": "log error and halt workflow",
"log_to_ledger": true
}
}
Conditional Execution: should_run_expr
This expression evaluates to 1 (run) or 0 (skip).
Simple Examples:
"1" → Always run
"0" → Never run (disabled step)
"step_[1.outputs.is](http://1.outputs.is)_valid" → Run if validation passed
Complex Examples:
"step_1.outputs.amount > 5000" → Run for high-value invoices
"trigger.priority == 'urgent'" → Run only for urgent requests
"step_2.outputs.doc_id != null" → Run if doc was created
"step_[1.outputs.is](http://1.outputs.is)_valid && trigger.amount > 1000" → Boolean AND
"trigger.status == 'completed' || trigger.status == 'approved'" → Boolean OR
Evaluation Engine:
function evaluateShouldRun(expr: string, runState: RunState): boolean {
// Replace template variables
const resolved = resolveTemplates({expr}, runState).expr;
// If it's a simple "1" or "0"
if (resolved === "1" || resolved === "0") {
return resolved === "1";
}
// If it's a boolean value from a previous step
if (typeof resolved === 'boolean') {
return resolved;
}
// If it's a comparison expression, evaluate it
// (In production, use a safe expression evaluator like expr-eval)
return evalSafely(resolved);
}
Branch Handling: on_success and on_failure
Supported Strategies:
-
Continue to next step (default)
{"on_success": "proceed to step_3"} -
Skip to specific step
{"on_success": "jump to step_5"} -
Halt workflow
{"on_failure": "halt workflow"} -
Log and continue
{"on_failure": "log error and continue"} -
Trigger different workflow
{"on_failure": "trigger workflow error-handler-workflow"}
Branching Example
{
"steps": [
{
"id": "step_1",
"service": "google_forms",
"action": "validate_submission",
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_2",
"on_failure": "jump to step_error"
}
},
{
"id": "step_2",
"service": "google_docs",
"action": "create_doc",
"binary_logic": {
"should_run_expr": "step_[1.outputs.is](http://1.outputs.is)_valid",
"on_success": "proceed to step_3"
}
},
{
"id": "step_3",
"service": "gmail",
"action": "send_email",
"binary_logic": {
"should_run_expr": "1"
}
},
{
"id": "step_error",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "[admin@company.com](mailto:admin@company.com)",
"subject": "Validation Failed",
"body": "step_1.outputs.error"
},
"binary_logic": {
"should_run_expr": "!step_[1.outputs.is](http://1.outputs.is)_valid",
"on_success": "halt workflow"
}
}
]
}
Execution Path 1 (Happy Path):
step_1 (validate) → success → step_2 (create doc) → step_3 (send email) → done
Execution Path 2 (Validation Failed):
step_1 (validate) → failure → step_error (notify admin) → halt
Architecture Diagram: Internal Flow
┌─────────────────────────────────────────────────────────────────┐
│ EXTERNAL TRIGGER │
│ (Form Submit, WebDocket Webhook, Chatbot Message, Cron Job) │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌────────────────────┐
│ TRIGGER INGESTION │
│ • Normalize event │
│ • Generate UUID │
│ • Match workflow │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ CREATE RUN CONTEXT │
│ • correlation_id │
│ • run_state │
│ • step_results {} │
└─────────┬──────────┘
│
▼
┌───────────────────────────────────┐
│ WORKFLOW ORCHESTRATOR │
│ (The Execution Loop) │
└───────────────┬───────────────────┘
│
┌─────────▼─────────┐
│ FOR EACH STEP │
└─────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────────┐ ┌──────────┐
│ Evaluate │ │ Resolve │ │ Execute │
│should_run│ → │ Templates │ → │Connector │
└──────────┘ └─────────────┘ └────┬─────┘
│
▼
┌─────────────────┐
│ CONNECTOR LAYER │
│ • Gmail │
│ • Docs │
│ • Forms │
│ • WebDocket │
└────────┬────────┘
│
┌───────────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌───────────────┐ ┌────────────────┐
│ Store Result │ │ Log to │ │ Handle Error │
│ in step_ │ │ Ledger │ │ (on_failure) │
│ results[id] │ │ (if enabled) │ │ │
└──────┬───────┘ └───────────────┘ └────────────────┘
│
▼
┌─────────────┐
│ on_success │
│ on_failure │
│ handlers │
└──────┬──────┘
│
▼
┌─────────────┐
│ Next Step │ ─── Loop continues
└─────────────┘
│
▼
┌─────────────┐
│ Workflow │
│ Complete │
└──────┬──────┘
│
▼
┌────────────────────┐
│ GMAIL LEDGER │
│ • Complete trace │
│ • All step data │
│ • Correlation ID │
│ • Queryable │
└────────────────────┘
Component Deep Dive
1. Trigger Ingestion Layer
Responsibilities:
- Accept events from external sources
- Normalize to standard format
- Route to appropriate workflow(s)
Implementation Pattern:
class TriggerIngestion {
async handleWebhook(req: Request): Promise<void> {
// Extract event data
const rawEvent = req.body;
// Normalize
const event: Event = {
event_id: generateUUID(),
event_type: detectEventType(rawEvent),
timestamp: new Date().toISOString(),
payload: normalizePayload(rawEvent),
metadata: extractMetadata(req)
};
// Match workflows
const workflows = this.registry.findMatchingWorkflows(event);
// Create run contexts
for (const workflow of workflows) {
const runContext = this.createRunContext(workflow, event);
await this.orchestrator.queueRun(runContext);
}
}
}
2. Workflow Registry
Responsibilities:
- Load workflow definitions
- Validate JSON schema
- Index for fast lookup
Implementation Pattern:
class WorkflowRegistry {
private workflows: Map<string, Workflow> = new Map();
async loadFromDirectory(path: string): Promise<void> {
const files = await readdir(path);
for (const file of files) {
if (file.endsWith('.json')) {
const workflow = await this.loadWorkflow(join(path, file));
this.workflows.set(workflow.workflow_name, workflow);
}
}
}
findMatchingWorkflows(event: Event): Workflow[] {
return Array.from(this.workflows.values())
.filter(w => this.matchesTrigger(w.trigger, event));
}
private matchesTrigger(trigger: Trigger, event: Event): boolean {
// Implement trigger matching logic
return trigger.source === event.metadata.source &&
trigger.type === event.event_type &&
this.evaluateCondition(trigger.condition, event);
}
}
3. Orchestrator Core
Responsibilities:
- Execute workflow steps in order
- Manage run state
- Handle branching logic
Implementation Pattern:
class Orchestrator {
async executeWorkflow(runContext: RunContext): Promise<void> {
const { workflow, run_state } = runContext;
// Initial ledger entry
await this.ledger.logEvent('workflow_triggered', run_state);
try {
for (const step of workflow.steps) {
// Check if should run
const shouldRun = this.evaluateShouldRun(
step.binary_logic.should_run_expr,
run_state
);
if (!shouldRun) {
continue;
}
// Execute step
run_state.current_step = [step.id](http://step.id);
const result = await this.executeStep(step, run_state);
// Store result
run_state.step_results[[step.id](http://step.id)] = result;
// Log if configured
if (step.binary_logic.log_to_ledger) {
await this.ledger.logEvent('step_completed', run_state, step);
}
// Handle result
if (result.status === 'failed') {
await this.handleFailure(step, run_state);
return; // May halt or continue based on on_failure
}
}
// Success
run_state.status = 'completed';
await this.ledger.logEvent('workflow_completed', run_state);
} catch (error) {
run_state.status = 'failed';
run_state.error = error;
await this.ledger.logEvent('workflow_failed', run_state);
throw error;
}
}
}
4. Connector Layer
Responsibilities:
- Abstract external APIs
- Handle authentication
- Retry logic
- Error mapping
Implementation Pattern:
interface ServiceConnector {
execute(action: string, inputs: any, context: RunContext): Promise<any>;
}
class GmailConnector implements ServiceConnector {
async execute(action: string, inputs: any, context: RunContext): Promise<any> {
switch (action) {
case 'send_email':
return await this.sendEmail(inputs, context);
case 'create_draft':
return await this.createDraft(inputs, context);
default:
throw new Error(`Unknown action: ${action}`);
}
}
private async sendEmail(inputs: any, context: RunContext): Promise<any> {
const { to, subject, body } = inputs;
// Add correlation ID to headers
const headers = {
'X-Correlation-ID': context.correlation_id
};
// Call Gmail API with retry
const result = await this.retryWithBackoff(() =>
this.gmailClient.users.messages.send({
userId: 'me',
requestBody: {
raw: this.encodeEmail({ to, subject, body, headers })
}
})
);
return {
message_id: [result.data.id](http://result.data.id)
};
}
}
5. Ledger System
Responsibilities:
- Format ledger emails
- Send to Gmail
- Apply labels
- Track message IDs
Implementation Pattern:
class LedgerSystem {
async logEvent(
event_type: string,
run_state: RunState,
step?: Step
): Promise<void> {
const workflow = run_state.workflow;
// Check if should log this event
if (_ledger.log_events.includes(event_type)) {
return;
}
// Format subject
const subject = this.formatSubject(
[workflow.gmail](http://workflow.gmail)_ledger.subject_template,
{ workflow, event_type, run_state }
);
// Format body
const body = this.formatBody(
[workflow.gmail](http://workflow.gmail)_ledger.body_template,
{ workflow, event_type, run_state, step }
);
// Send email
const result = await this.gmailConnector.execute('send_email', {
to: [workflow.gmail](http://workflow.gmail)_ledger.ledger_recipient,
subject,
body
}, run_state);
// Track message ID
run_state.ledger_emails_sent.push(result.message_id);
// Apply labels
await this.applyLabels(result.message_id, workflow, event_type);
}
}
PROMPT 3 — The Ledger System (Gmail OS Explanation)
Philosophy: Logs as Email
Why Email for Logging?
Traditional logging approaches have fundamental limitations:
File-based logs:
- Require filesystem access
- Rotation and retention are manual
- No built-in search
- Siloed per server
- Lost when servers are destroyed
Database logs:
- Require DB schema
- Query performance degrades over time
- Backup and retention require separate systems
- Access requires DB credentials
Logging services (Datadog, Splunk, etc.):
- Expensive at scale
- Proprietary formats
- Vendor lock-in
- Complex query languages
Gmail as a logging system provides:
- Durability: Google's infrastructure, automatic backup
- Search: Powerful, fast, familiar search interface
- Organization: Labels act as hierarchical directories
- Access Control: Gmail's native permissions
- Retention: Configurable per label
- Cost: Free up to 15GB per account
- Accessibility: Any service can read/write via API
- Human + Machine: Both readable in same format
- Threading: Related events automatically grouped
- Time-based queries: Built-in date filters
The Paradigm Shift
Traditional: Logs are a side effect of execution
Mind's Eye: Logs ARE the execution record
In Mind's Eye:
- Every significant state change → ledger entry
- The ledger is queryable as a database
- The ledger enables replay and audit
- The ledger feeds the Hunting Engine
- The ledger is the source of truth
Email threading creates natural grouping:
Thread: Invoice INV-001 Processing
├─ [LEDGER] workflow=invoice-intake event=workflow_triggered
├─ [LEDGER] workflow=invoice-intake event=step_completed (validation)
├─ [LEDGER] workflow=invoice-intake event=step_completed (doc creation)
├─ [LEDGER] workflow=invoice-intake event=step_completed (docket creation)
└─ [LEDGER] workflow=invoice-intake event=workflow_completed
Ledger Event Structure
Subject Format (Machine-Readable)
[LEDGER] workflow={workflow_name} event={event_type} cid={correlation_id}
Components:
-
[LEDGER]: Prefix for all ledger emails (enables quick filtering) -
workflow={name}: Which workflow generated this event -
event={type}: What happened (workflow_triggered, step_completed, error, etc.) -
cid={uuid}: Correlation ID linking all events in this workflow run
Examples:
[LEDGER] workflow=invoice-intake-and-validation event=workflow_triggered cid=550e8400-e29b-41d4-a716-446655440000
[LEDGER] workflow=invoice-intake-and-validation event=step_completed cid=550e8400-e29b-41d4-a716-446655440000
[LEDGER] workflow=duplicate-invoice-review event=validation_failed cid=7f2b0c50-f3e1-4b99-8a47-5d6c8b9e3a12
[LEDGER] workflow=meeting-notes-workflow event=error cid=9c4d1e60-a4f2-5c88-9b58-6e7d9c0f4b23
Body Format (Structured Template)
========================================
MIND'S EYE WORKFLOW LEDGER ENTRY
========================================
WORKFLOW: {workflow_name}
EVENT: {event_type}
CORRELATION_ID: {correlation_id}
RUN_ID: {run_id}
TIMESTAMP: {iso_timestamp}
========================================
TRIGGER INFORMATION
========================================
SOURCE: {trigger.source}
TYPE: {trigger.event_type}
TIMESTAMP: {trigger.timestamp}
PAYLOAD:
{trigger.payload | json_pretty_print}
========================================
STEP INFORMATION
========================================
STEP_ID: {[step.id](http://step.id)}
SERVICE: {step.service}
ACTION: {step.action}
STATUS: {step_status}
INPUTS:
{step.inputs | json_pretty_print}
RESOLVED INPUTS (after template substitution):
{resolved_inputs | json_pretty_print}
OUTPUTS:
{step.outputs | json_pretty_print}
EXECUTION TIME: {step_duration_ms}ms
========================================
WORKFLOW STATE
========================================
CURRENT_STEP: {current_step_number} of {total_steps}
STATUS: {workflow_status}
STEPS_COMPLETED: {completed_steps}
STEPS_REMAINING: {remaining_steps}
STEP RESULTS SUMMARY:
{step_results_summary}
========================================
ERROR INFORMATION
========================================
ERROR: {error_message | default: "none"}
STACK TRACE:
{error_stack | default: "N/A"}
RECOVERY ACTION: {on_failure_action}
========================================
LEDGER METADATA
========================================
LEDGER_EMAIL_COUNT: {total_ledger_emails_for_this_run}
WORKFLOW_START_TIME: {workflow_start_timestamp}
ELAPSED_TIME: {elapsed_ms}ms
CORRELATION_ID: {correlation_id}
QUERY THIS RUN: gmail search "cid={correlation_id}"
========================================
END LEDGER ENTRY
========================================
Correlation IDs: Complete Traceability
How Correlation IDs Work
1. Generation
const correlation_id = crypto.randomUUID();
// Example: "550e8400-e29b-41d4-a716-446655440000"
2. Propagation
Every action in the workflow receives the correlation ID:
// In orchestrator
run_state.correlation_id = correlation_id;
// Passed to connectors
await connector.execute(action, inputs, {
correlation_id: run_state.correlation_id
});
// Included in all ledger emails
subject: `[LEDGER] ... cid=${run_state.correlation_id}`
// Added to external system calls
headers: {
'X-Correlation-ID': run_state.correlation_id
}
3. Cross-Service Tracking
Google Form Submit
↓
MORCO Trigger (generates cid: abc-123)
↓
Step 1: Validate
→ Ledger Email: cid=abc-123
↓
Step 2: Create Google Doc
→ Doc metadata: correlation_id=abc-123
→ Ledger Email: cid=abc-123
↓
Step 3: Create WebDocket
→ Docket custom field: correlation_id=abc-123
→ Ledger Email: cid=abc-123
↓
Step 4: Send Gmail
→ Email header: X-Correlation-ID=abc-123
→ Ledger Email: cid=abc-123
4. Complete Audit Trail Query
Gmail search: "cid=abc-123"
Results:
- 5 ledger emails
- Full trace from trigger to completion
- All inputs, outputs, and state at each step
- Timestamps showing duration
- Any errors that occurred
Multi-Workflow Correlation
When workflows trigger other workflows:
{
"correlation_id": "def-456",
"parent_correlation_id": "abc-123",
"workflow_chain": ["invoice-intake", "duplicate-review"]
}
Queries:
// Find parent workflow
cid=abc-123
// Find child workflows
parent_cid=abc-123
// Find entire chain
cid=abc-123 OR parent_cid=abc-123
Ledger Event Types
Core Event Types
workflow_created
- When: Workflow definition is first registered
- Payload: Complete workflow JSON
- Use: Track workflow versions
2. workflow_triggered
- When: Workflow execution starts
- Payload: Trigger event, correlation ID, initial state
- Use: Track workflow activations
3. step_completed
- When: A step successfully executes
- Payload: Step ID, inputs, outputs, duration
- Use: Detailed execution trace
4. step_skipped
-
When: A step's
should_run_exprevaluates to 0 - Payload: Step ID, reason (expr result)
- Use: Understand branching paths
5. validation_failed
- When: Input validation fails
- Payload: Failed fields, validation rules, errors
- Use: Track data quality issues
6. error
- When: Any error occurs (network, API, logic)
- Payload: Error message, stack trace, step context
- Use: Debugging, alerting
7. workflow_completed
- When: Workflow successfully finishes
- Payload: Final state, all outputs, total duration
- Use: Success tracking, metrics
8. workflow_failed
- When: Workflow terminates with error
- Payload: Error details, partial state, recovery options
- Use: Failure analysis
Hunting Engine Event Types
9. pattern_detected
- When: A hunt finds a matching pattern
- Payload: Hunt details, matched events, confidence
- Use: Track discovered patterns
10. annotation_created
- When: A hunt annotates an event
- Payload: Annotation details, related events
- Use: Enrichment tracking
11. hunt_run_completed
- When: A hunt finishes analyzing a time window
- Payload: Events analyzed, patterns found, duration
- Use: Hunt performance metrics
Ledger Email Lifecycle
Phase 1: Event Generation
Orchestrator executes step
↓
Step completes successfully
↓
Check: step.binary_logic.log_to_ledger?
↓ (yes)
Check: [workflow.gmail](http://workflow.gmail)_ledger.log_events includes "step_completed"?
↓ (yes)
Generate ledger event
Phase 2: Email Formatting
const subject = formatSubject({
workflow_name: run_state.workflow_name,
event_type: 'step_completed',
correlation_id: run_state.correlation_id
});
const body = formatBody({
workflow: run_state.workflow,
event_type: 'step_completed',
trigger: run_state.trigger,
step: current_step,
step_results: run_state.step_results,
error: null
});
Phase 3: Email Sending
await gmailConnector.execute('send_email', {
to: [workflow.gmail](http://workflow.gmail)_ledger.ledger_recipient,
subject: subject,
body: body,
headers: {
'X-Correlation-ID': run_state.correlation_id,
'X-Workflow': run_state.workflow_name,
'X-Event-Type': 'step_completed'
}
});
Phase 4: Label Application
const labels = [
'MORCO/Ledger',
`MORCO/Workflows/${workflow_name}`,
`MORCO/Events/${event_type}`,
`MORCO/Status/${workflow_status}`
];
await gmailConnector.applyLabels(message_id, labels);
Phase 5: Archival & Retention
Gmail's automatic features:
- Stored durably in Google's infrastructure
- Searchable via Gmail search
- Can be archived (removed from inbox but kept)
- Retention policies via label settings
- Can be exported via Google Takeout
Optional: Secondary Archive
// Periodically export to cold storage
const ledger_emails = await fetchLedgerEmails({
older_than: '90 days',
label: 'MORCO/Ledger'
});
await exportToS3(ledger_emails);
await applyLabel(ledger_emails, 'MORCO/Archived');
Querying the Ledger
Gmail Search Patterns
By Correlation ID:
subject:"cid=550e8400-e29b-41d4-a716-446655440000"
By Workflow:
subject:"workflow=invoice-intake-and-validation"
By Event Type:
subject:"event=step_completed"
By Time Range:
subject:"[LEDGER]" after:2025/12/01 before:2025/12/31
Errors Only:
subject:"event=error" OR subject:"event=workflow_failed"
Specific Workflow + Errors:
subject:"workflow=invoice-intake" (subject:"event=error" OR subject:"event=validation_failed")
By Label:
label:morco-workflows-invoice-intake
Complex Query:
subject:"[LEDGER]"
subject:"workflow=invoice-intake"
after:2025/12/01
-subject:"event=workflow_completed"
(All invoice-intake runs in December that didn't complete)
Programmatic Queries
class LedgerQueryService {
async getWorkflowRun(correlation_id: string): Promise<LedgerEmail[]> {
const query = `subject:"cid=${correlation_id}" label:morco-ledger`;
return await this.gmailClient.searchMessages(query);
}
async getRecentErrors(hours: number = 24): Promise<LedgerEmail[]> {
const after = new Date([Date.now](http://Date.now)() - hours * 60 * 60 * 1000);
const query = `
subject:"[LEDGER]"
(subject:"event=error" OR subject:"event=workflow_failed")
after:${this.formatDate(after)}
`;
return await this.gmailClient.searchMessages(query);
}
async getWorkflowMetrics(
workflow_name: string,
start: Date,
end: Date
): Promise<WorkflowMetrics> {
const query = `
subject:"workflow=${workflow_name}"
subject:"event=workflow_completed"
after:${this.formatDate(start)}
before:${this.formatDate(end)}
`;
const emails = await this.gmailClient.searchMessages(query);
return this.calculateMetrics(emails);
}
}
Analytics from Ledger Data
Parsing Ledger Emails
class LedgerParser {
parseEmail(email: GmailMessage): LedgerEntry {
const subject = email.subject;
const body = email.body;
// Parse subject
const workflow_name = this.extractField(subject, 'workflow=');
const event_type = this.extractField(subject, 'event=');
const correlation_id = this.extractField(subject, 'cid=');
// Parse body sections
const trigger = this.extractSection(body, 'TRIGGER INFORMATION');
const step = this.extractSection(body,
'STEP INFORMATION');
const status = this.extractSection(body, 'STATUS');
const metadata = this.extractSection(body, 'METADATA');
return {
correlation_id,
workflow_name,
event_type,
timestamp: email.timestamp,
trigger,
step,
status,
metadata
};
}
private extractField(text: string, prefix: string): string {
const regex = new RegExp(`${prefix}([^\\s]+)`);
const match = text.match(regex);
return match ? match[1] : '';
}
private extractSection(text: string, sectionName: string): any {
const startMarker = `=== ${sectionName} ===`;
const endMarker = '===';
const startIdx = text.indexOf(startMarker);
if (startIdx === -1) return null;
const contentStart = startIdx + startMarker.length;
const nextSectionIdx = text.indexOf(endMarker, contentStart);
const sectionText = text.substring(contentStart, nextSectionIdx).trim();
// Try to parse as JSON if it looks like structured data
try {
return JSON.parse(sectionText);
} catch {
return sectionText;
}
}
}
Aggregating Metrics
class LedgerAnalytics {
async getWorkflowMetrics(
workflow_name: string,
timeRange: { start: Date; end: Date }
): Promise<WorkflowMetrics> {
// Fetch all completed runs
const completedRuns = await this.ledgerQuery.getCompletedRuns(
workflow_name,
timeRange
);
// Fetch all failed runs
const failedRuns = await this.ledgerQuery.getFailedRuns(
workflow_name,
timeRange
);
// Parse and aggregate
const metrics = {
total_runs: completedRuns.length + failedRuns.length,
successful_runs: completedRuns.length,
failed_runs: failedRuns.length,
success_rate: completedRuns.length / (completedRuns.length + failedRuns.length),
avg_duration: this.calculateAvgDuration(completedRuns),
p50_duration: this.calculatePercentile(completedRuns, 0.5),
p95_duration: this.calculatePercentile(completedRuns, 0.95),
p99_duration: this.calculatePercentile(completedRuns, 0.99),
error_breakdown: this.categorizeErrors(failedRuns),
step_performance: this.analyzeStepPerformance(completedRuns)
};
return metrics;
}
async getStepErrorRate(
workflow_name: string,
step_id: string,
days: number = 7
): Promise<number> {
const query = `
subject:"workflow=${workflow_name}"
subject:"event=error"
body:"STEP_ID: ${step_id}"
after:${this.formatDate(daysAgo(days))}
`;
const errors = await this.gmailClient.searchMessages(query);
const totalRuns = await this.getTotalRunsForWorkflow(workflow_name, days);
return errors.length / totalRuns;
}
}
Visualization from Ledger
// Generate timeline visualization
const timeline = await ledgerAnalytics.getWorkflowTimeline(
'invoice-intake-and-validation',
{ start: startDate, end: endDate }
);
// Output:
// 2025-12-01: ████████████ 12 runs (11 success, 1 fail)
// 2025-12-02: ██████████████████ 18 runs (18 success, 0 fail)
// 2025-12-03: ████████ 8 runs (7 success, 1 fail)
When Ledger Emails Fire: Decision Matrix
| Event Type | When It Fires | Always Logged? | Configurable? |
|---|---|---|---|
workflow_created |
Workflow registered in system | Yes | No |
workflow_triggered |
Workflow execution starts | If in log_events
|
Yes |
step_completed |
Step finishes successfully | If in log_events AND step.binary_logic.log_to_ledger
|
Yes (per step) |
step_skipped |
should_run_expr = 0 |
If in log_events
|
Yes |
validation_failed |
Input validation fails | If in log_events
|
Yes |
error |
Any error occurs | Always | No (critical) |
workflow_completed |
Workflow finishes successfully | If in log_events
|
Yes |
workflow_failed |
Workflow fails | Always | No (critical) |
Best Practice: Always log workflow_triggered, step_completed, and workflow_completed for full traceability.
PROMPT 4 — Workflow Execution Model (The MORCO Runtime)
MORCO Runtime Overview
The MORCO Execution Runtime is the deterministic execution engine that takes workflow JSON specifications and executes them against real services. It's the heart of the Mind's Eye platform.
Core Responsibilities:
- Receive triggers from external sources
- Load workflow definitions from the registry
- Execute steps sequentially with conditional branching
- Resolve template variables using run state
- Call service connectors to perform actions
- Manage state across the entire workflow run
- Write ledger entries at key checkpoints
- Handle failures according to defined strategies
Step-by-Step Execution Flow
Phase 1: Trigger Reception
External Event (Form submit, Webhook, etc.)
↓
Ingestion Layer receives event
↓
Normalize to standard Event object
↓
Search Registry for matching workflows
↓
For each match: Create RunContext
Code:
async handleTrigger(rawEvent: any): Promise<void> {
// Normalize
const event = this.normalizeEvent(rawEvent);
// Find matching workflows
const workflows = this.registry.findMatchingWorkflows(event);
// Create run contexts
for (const workflow of workflows) {
const runContext = {
run_id: generateUUID(),
correlation_id: generateUUID(),
workflow: workflow,
trigger_event: event,
run_state: this.initializeRunState(workflow, event)
};
// Queue for execution
await this.executionQueue.enqueue(runContext);
}
}
Phase 2: Run State Initialization
initializeRunState(workflow: Workflow, event: Event): RunState {
return {
run_id: generateUUID(),
correlation_id: generateUUID(),
workflow_name: workflow.workflow_name,
trigger: {
event_id: event.event_id,
event_type: event.event_type,
payload: event.payload,
timestamp: event.timestamp
},
step_results: {},
current_step: null,
status: 'queued',
created_at: new Date().toISOString(),
ledger_emails_sent: [],
metadata: {
workflow_version: workflow.version || '1.0.0',
entry_point: workflow.entry_point
}
};
}
Phase 3: Orchestration Loop
This is the core execution algorithm:
async executeWorkflow(runContext: RunContext): Promise<void> {
const { workflow, run_state } = runContext;
// Update status
run_state.status = 'running';
run_state.started_at = new Date().toISOString();
// Log workflow start
await this.ledger.logEvent('workflow_triggered', run_state);
try {
// Execute each step
for (let i = 0; i < workflow.steps.length; i++) {
const step = workflow.steps[i];
run_state.current_step = [step.id](http://step.id);
// === STEP 1: Evaluate Should Run ===
const shouldRun = this.evaluateShouldRun(
step.binary_logic.should_run_expr,
run_state
);
if (!shouldRun) {
// Log skip
if ([workflow.gmail](http://workflow.gmail)_ledger.log_events.includes('step_skipped')) {
await this.ledger.logEvent('step_skipped', run_state, step);
}
continue;
}
// === STEP 2: Resolve Template Variables ===
const resolvedInputs = this.resolveTemplates(
step.inputs,
run_state
);
// === STEP 3: Execute Connector ===
const startTime = [Date.now](http://Date.now)();
let stepResult: StepResult;
try {
const connector = this.connectorFactory.getConnector(step.service);
const outputs = await connector.execute(
step.action,
resolvedInputs,
runContext
);
stepResult = {
outputs,
status: 'success',
started_at: new Date(startTime).toISOString(),
completed_at: new Date().toISOString(),
duration_ms: [Date.now](http://Date.now)() - startTime
};
} catch (error) {
stepResult = {
outputs: null,
status: 'failed',
error: error.message,
stack: error.stack,
started_at: new Date(startTime).toISOString(),
completed_at: new Date().toISOString(),
duration_ms: [Date.now](http://Date.now)() - startTime
};
}
// === STEP 4: Store Result ===
run_state.step_results[[step.id](http://step.id)] = stepResult;
// === STEP 5: Log if Configured ===
if (step.binary_logic.log_to_ledger) {
await this.ledger.logEvent('step_completed', run_state, step);
}
// === STEP 6: Handle Result ===
if (stepResult.status === 'failed') {
await this.handleStepFailure(step, run_state);
// Check on_failure strategy
if (step.binary_logic.on_failure === 'halt workflow') {
throw new Error(`Step ${[step.id](http://step.id)} failed: ${stepResult.error}`);
} else if (step.binary_logic.on_failure.startsWith('jump to')) {
const targetStep = step.binary_logic.on_failure.match(/jump to (\\w+)/)[1];
i = workflow.steps.findIndex(s => [s.id](http://s.id) === targetStep) - 1;
continue;
}
// else: continue to next step
} else {
// Success path
if (step.binary_logic.on_success.startsWith('jump to')) {
const targetStep = step.binary_logic.on_success.match(/jump to (\\w+)/)[1];
i = workflow.steps.findIndex(s => [s.id](http://s.id) === targetStep) - 1;
}
// else: continue to next step (default)
}
}
// Workflow completed successfully
run_state.status = 'completed';
run_state.completed_at = new Date().toISOString();
await this.ledger.logEvent('workflow_completed', run_state);
} catch (error) {
// Workflow failed
run_state.status = 'failed';
run_state.completed_at = new Date().toISOString();
run_state.error = {
step_id: run_state.current_step,
message: error.message,
stack: error.stack
};
await this.ledger.logEvent('workflow_failed', run_state);
throw error;
}
}
Binary Logic: should_run_expr Deep Dive
The Expression Language
Mind's Eye uses a safe subset of JavaScript expressions for should_run_expr. This allows powerful conditional logic while preventing code injection.
Supported Operators:
-
Comparison:
==,!=,>,<,>=,<= -
Logical:
&&,||,! -
Arithmetic:
+,-,*,/,% -
Literals:
true,false, numbers, strings (single/double quotes) - Variable access: Dot notation for nested paths
Supported Variables:
-
trigger.*: Access trigger payload fields -
step_N.outputs.*: Access outputs from previous steps -
step_N.status: Check if step succeeded or failed
Evaluation Engine
class BinaryLogicEvaluator {
evaluateShouldRun(expr: string, runState: RunState): boolean {
// Handle simple cases
if (expr === '1' || expr === 'true') return true;
if (expr === '0' || expr === 'false') return false;
// Resolve all variable references
const resolved = this.resolveVariables(expr, runState);
// If resolution resulted in a boolean, return it
if (typeof resolved === 'boolean') return resolved;
// Otherwise, safely evaluate the expression
return this.safeEval(resolved);
}
private resolveVariables(expr: string, runState: RunState): any {
// Replace trigger references
expr = expr.replace(/trigger\\.([a-zA-Z0-9_.\\[\\]]+)/g, (match, path) => {
const value = this.getNestedValue(runState.trigger.payload, path);
return JSON.stringify(value);
});
// Replace step references
expr = expr.replace(/step_([a-zA-Z0-9_]+)\\.outputs\\.([a-zA-Z0-9_.\\[\\]]+)/g,
(match, stepId, path) => {
const stepResult = runState.step_results[`step_${stepId}`];
if (!stepResult) return 'null';
const value = this.getNestedValue(stepResult.outputs, path);
return JSON.stringify(value);
});
// Replace step status references
expr = expr.replace(/step_([a-zA-Z0-9_]+)\\.status/g, (match, stepId) => {
const stepResult = runState.step_results[`step_${stepId}`];
return stepResult ? `"${stepResult.status}"` : '"unknown"';
});
return expr;
}
private safeEval(expr: string): boolean {
// Use a safe expression evaluator (like expr-eval library)
// or implement a simple recursive descent parser
const parser = new ExpressionParser();
const result = parser.parse(expr).evaluate();
return Boolean(result);
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => {
if (current === null || current === undefined) return null;
return current[key];
}, obj);
}
}
Examples with Evaluation
Example 1: Simple Boolean
{
"should_run_expr": "[trigger.is](http://trigger.is)_urgent"
}
// Evaluation:
trigger.payload = { is_urgent: true, ... }
resolveVariables("[trigger.is](http://trigger.is)_urgent", runState)
→ true
return true // Step runs
Example 2: Comparison
{
"should_run_expr": "trigger.amount > 5000"
}
// Evaluation:
trigger.payload = { amount: 7500, ... }
resolveVariables("trigger.amount > 5000", runState)
→ "7500 > 5000"
safeEval("7500 > 5000")
→ true
return true // Step runs
Example 3: Dependent on Previous Step
{
"should_run_expr": "step_[1.outputs.is](http://1.outputs.is)_valid && trigger.amount > 1000"
}
// Evaluation:
step_results = {
step_1: { outputs: { is_valid: true }, status: 'success' }
}
trigger.payload = { amount: 2000, ... }
resolveVariables("step_[1.outputs.is](http://1.outputs.is)_valid && trigger.amount > 1000", runState)
→ "true && 2000 > 1000"
safeEval("true && 2000 > 1000")
→ true
return true // Step runs
Example 4: String Comparison
{
"should_run_expr": "trigger.status == 'approved' || trigger.status == 'completed'"
}
// Evaluation:
trigger.payload = { status: 'approved', ... }
resolveVariables("trigger.status == 'approved' || trigger.status == 'completed'", runState)
→ "\"approved\" == 'approved' || \"approved\" == 'completed'"
safeEval("\"approved\" == 'approved' || \"approved\" == 'completed'")
→ true
return true // Step runs
Template Resolution: Dynamic Input Binding
The Template Syntax
Templates use dotted path notation to reference values from the run state:
Syntax:
-
trigger.field_name: Access trigger payload -
step_N.outputs.field_name: Access previous step outputs -
step_N.status: Access step execution status
No templating engine needed — simple string replacement and path resolution.
Resolution Algorithm
class TemplateResolver {
resolveTemplates(inputs: any, runState: RunState): any {
if (typeof inputs === 'string') {
return this.resolveString(inputs, runState);
} else if (Array.isArray(inputs)) {
return [inputs.map](http://inputs.map)(item => this.resolveTemplates(item, runState));
} else if (typeof inputs === 'object' && inputs !== null) {
const resolved = {};
for (const [key, value] of Object.entries(inputs)) {
resolved[key] = this.resolveTemplates(value, runState);
}
return resolved;
} else {
return inputs;
}
}
private resolveString(template: string, runState: RunState): any {
// Check if the entire string is a template reference
if (this.isTemplateReference(template)) {
return this.resolveReference(template, runState);
}
// Otherwise, perform string interpolation
return template.replace(/\\{\\{([^}]+)\\}\\}/g, (match, path) => {
const value = this.resolveReference(path.trim(), runState);
return String(value);
});
}
private isTemplateReference(str: string): boolean {
return /^(trigger|step_\\w+)\\.[a-zA-Z0-9_.\\[\\]]+$/.test(str);
}
private resolveReference(path: string, runState: RunState): any {
if (path.startsWith('trigger.')) {
const fieldPath = path.substring(8); // Remove 'trigger.'
return this.getNestedValue(runState.trigger.payload, fieldPath);
} else if (path.startsWith('step_')) {
const match = path.match(/^step_([a-zA-Z0-9_]+)\\.(.+)$/);
if (!match) return null;
const [, stepId, fieldPath] = match;
const stepResult = runState.step_results[`step_${stepId}`];
if (!stepResult) return null;
return this.getNestedValue(stepResult, fieldPath);
}
return null;
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => {
if (current === null || current === undefined) return null;
// Handle array indices: field[0]
const arrayMatch = key.match(/^([^\\[]+)\\[(\\d+)\\]$/);
if (arrayMatch) {
const [, arrayKey, index] = arrayMatch;
return current[arrayKey] ? current[arrayKey][parseInt(index)] : null;
}
return current[key];
}, obj);
}
}
Template Resolution Examples
Example 1: Simple Field Reference
{
"step_3": {
"inputs": {
"recipient": "trigger.submitter_email",
"subject": "Invoice Received"
}
}
}
// Before resolution:
inputs = {
recipient: "trigger.submitter_email",
subject: "Invoice Received"
}
// After resolution:
runState.trigger.payload = { submitter_email: "[vendor@acme.com](mailto:vendor@acme.com)", ... }
resolvedInputs = {
recipient: "[vendor@acme.com](mailto:vendor@acme.com)",
subject: "Invoice Received"
}
Example 2: Nested Object Reference
{
"step_4": {
"inputs": {
"doc_url": "step_2.outputs.doc_url",
"docket_id": "step_3.outputs.docket_id"
}
}
}
// Before resolution:
inputs = {
doc_url: "step_2.outputs.doc_url",
docket_id: "step_3.outputs.docket_id"
}
// After resolution:
runState.step_results = {
step_2: { outputs: { doc_url: "https://docs.google.com/..." } },
step_3: { outputs: { docket_id: "DOCK-001" } }
}
resolvedInputs = {
doc_url: "https://docs.google.com/...",
docket_id: "DOCK-001"
}
Example 3: String Interpolation
{
"step_5": {
"inputs": {
"subject": "Invoice trigger.invoice_number - step_1.outputs.validation_status",
"body": "Invoice from trigger.vendor_name has been step_1.outputs.validation_status"
}
}
}
// Before resolution:
inputs = {
subject: "Invoice trigger.invoice_number - step_1.outputs.validation_status",
body: "Invoice from trigger.vendor_name has been step_1.outputs.validation_status"
}
// After resolution:
runState.trigger.payload = { invoice_number: "INV-001", vendor_name: "Acme Corp" }
runState.step_results = {
step_1: { outputs: { validation_status: "approved" } }
}
resolvedInputs = {
subject: "Invoice INV-001 - approved",
body: "Invoice from Acme Corp has been approved"
}
Connector Execution
Connector Interface
All connectors implement this interface:
interface ServiceConnector {
/**
* Execute an action on this service
* @param action The action to perform (e.g., 'send_email', 'create_doc')
* @param inputs The resolved inputs for this action
* @param context The full run context for correlation tracking
* @returns The outputs from the action
*/
execute(action: string, inputs: any, context: RunContext): Promise<any>;
}
Connector Responsibilities
- Action Routing: Map action string to internal method
- Authentication: Handle OAuth, API keys, service accounts
- Input Validation: Ensure required fields are present
- API Calls: Make actual HTTP requests to external services
- Retry Logic: Retry failed requests with exponential backoff
- Rate Limiting: Respect API rate limits
- Error Handling: Map service errors to standardized format
- Output Mapping: Return consistent output structure
- Correlation Tracking: Include correlation ID in requests
Example: Gmail Connector
class GmailConnector implements ServiceConnector {
constructor(
private gmailClient: GmailClient,
private retryConfig: RetryConfig
) {}
async execute(action: string, inputs: any, context: RunContext): Promise<any> {
// Route to action handler
switch (action) {
case 'send_email':
return await this.sendEmail(inputs, context);
case 'create_draft':
return await this.createDraft(inputs, context);
case 'add_label':
return await this.addLabel(inputs, context);
default:
throw new Error(`Unknown Gmail action: ${action}`);
}
}
private async sendEmail(inputs: any, context: RunContext): Promise<any> {
// Validate inputs
this.validateRequired(inputs, ['to', 'subject', 'body']);
// Prepare email
const email = {
to: [inputs.to](http://inputs.to),
from: inputs.from || this.config.defaultFrom,
subject: inputs.subject,
body: inputs.body,
cc: [inputs.cc](http://inputs.cc),
bcc: inputs.bcc,
attachments: inputs.attachments
};
// Add correlation ID header
const headers = {
'X-Correlation-ID': context.correlation_id,
'X-Workflow': [context.run](http://context.run)_state.workflow_name,
'X-Run-ID': [context.run](http://context.run)_id,
...(inputs.headers || {})
};
// Send with retry
const result = await this.retryWithBackoff(async () => {
return await this.gmailClient.users.messages.send({
userId: 'me',
requestBody: {
raw: this.encodeEmail({ ...email, headers })
}
});
});
// Return outputs
return {
message_id: [result.data.id](http://result.data.id),
thread_id: [result.data](http://result.data).threadId,
sent_at: new Date().toISOString()
};
}
private async retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Don't retry on client errors (4xx)
if (error.code >= 400 && error.code < 500) {
throw error;
}
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
await this.sleep(delay);
}
}
}
throw lastError;
}
private validateRequired(inputs: any, required: string[]): void {
const missing = required.filter(field => !(field in inputs));
if (missing.length > 0) {
throw new Error(`Missing required fields: ${missing.join(', ')}`);
}
}
private encodeEmail(email: any): string {
// Implement RFC 2822 email encoding
// ... encoding logic ...
return Buffer.from(encoded).toString('base64url');
}
}
Failure Handling Strategies
on_failure Options
1. halt workflow (Default for critical steps)
{
"on_failure": "halt workflow"
}
- Stops execution immediately
- Marks workflow as
failed - Writes
workflow_failedledger entry - Error details captured in run state
2. log error and continue
{
"on_failure": "log error and continue"
}
- Logs the error to ledger
- Continues to next step
- Subsequent steps can check
step_N.status == 'failed'
3. jump to <step_id>
{
"on_failure": "jump to step_error_handler"
}
- Skips remaining steps
- Jumps directly to specified error handling step
- Error details available in run state
4. trigger workflow <workflow_name>
{
"on_failure": "trigger workflow error-notification-workflow"
}
- Creates a new workflow run
- Passes error details as trigger payload
- Original workflow continues or halts based on secondary config
Error Context Propagation
When a step fails, the error context is stored:
stepResult = {
outputs: null,
status: 'failed',
error: {
message: "Gmail API error: Invalid recipient",
code: "INVALID_RECIPIENT",
details: {
recipient: "invalid-email",
api_response: {...}
},
stack: "Error: Gmail API error\\n at GmailConnector.sendEmail..."
}
}
Subsequent steps can access this:
{
"step_error_handler": {
"inputs": {
"error_message": "step_3.error.message",
"failed_step": "step_3",
"original_inputs": "step_3.inputs"
}
}
}
Data Flow Between Steps
The Step Results Chain
Step 1: Validate
↓ outputs: { is_valid: true, normalized_data: {...} }
Step 2: Create Doc (uses Step 1 outputs)
↓ inputs: { data: "step_1.outputs.normalized_data" }
↓ outputs: { doc_id: "abc123", doc_url: "https://..." }
Step 3: Create Docket (uses Step 2 outputs)
↓ inputs: { doc_url: "step_2.outputs.doc_url" }
↓ outputs: { docket_id: "DOCK-001", docket_url: "https://..." }
Step 4: Send Email (uses outputs from Steps 2 & 3)
↓ inputs: {
body: "Doc: step_2.outputs.doc_url, Docket: step_3.outputs.docket_url"
}
↓ outputs: { message_id: "msg123" }
Data Transformation Pipeline
Sometimes you need to transform data between steps:
Option 1: Connector Handles Transformation
{
"step_2": {
"service": "google_docs",
"action": "create_doc_from_template",
"inputs": {
"template_id": "template123",
"replacements": {
"vendor": "trigger.vendor_name",
"amount": "trigger.amount",
"date": "trigger.due_date"
}
}
}
}
Option 2: Use Formula/Expression Step (Future Enhancement)
{
"step_1_5": {
"service": "transform",
"action": "evaluate",
"inputs": {
"expression": "step_1.outputs.amount * 1.15",
"output_field": "amount_with_tax"
},
"outputs": {
"amount_with_tax": "number"
}
}
}
Complete Execution Diagram with Data Flow
┌────────────────────────────────────────────────────────────────────┐
│ WORKFLOW EXECUTION │
└────────────────────────────────────────────────────────────────────┘
TRIGGER EVENT
{ vendor_name: "Acme", invoice_number: "INV-001", amount: 5000 }
↓
┌──────────────────────────────────────────────────────────────────┐
│ INITIALIZE RUN STATE │
│ • run_id: uuid │
│ • correlation_id: uuid │
│ • trigger: { ... } │
│ • step_results: {} │
│ • status: 'running' │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ STEP 1: VALIDATE INVOICE │
│ should_run_expr: "1" → evaluates to TRUE │
│ inputs (resolved): │
│ • submission: { vendor_name: "Acme", ... } │
│ ──────────────────────────────────────────────────────────────── │
│ CONNECTOR: google_forms.validate_submission() │
│ ──────────────────────────────────────────────────────────────── │
│ outputs: │
│ • is_valid: true │
│ • normalized_payload: { ... } │
│ ──────────────────────────────────────────────────────────────── │
│ STORE: step_results['step_1'] = { outputs: {...}, status: 'success' } │
│ LOG: ledger email "step_completed" │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ STEP 2: CREATE GOOGLE DOC │
│ should_run_expr: "step_[1.outputs.is](http://1.outputs.is)_valid" → TRUE │
│ inputs (before resolution): │
│ • data: "step_1.outputs.normalized_payload" │
│ inputs (after resolution): │
│ • data: { vendor_name: "Acme", ... } │
│ ──────────────────────────────────────────────────────────────── │
│ CONNECTOR: google_docs.create_doc_from_template() │
│ ──────────────────────────────────────────────────────────────── │
│ outputs: │
│ • doc_id: "1abc123def" │
│ • doc_url: "https://docs.google.com/document/d/1abc123def" │
│ ──────────────────────────────────────────────────────────────── │
│ STORE: step_results['step_2'] = { outputs: {...}, status: 'success' } │
│ LOG: ledger email "step_completed" │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ STEP 3: CREATE WEBDOCKET │
│ should_run_expr: "1" → TRUE │
│ inputs (before resolution): │
│ • title: "Invoice: trigger.invoice_number" │
│ • doc_url: "step_2.outputs.doc_url" │
│ inputs (after resolution): │
│ • title: "Invoice: INV-001" │
│ • doc_url: "https://docs.google.com/document/d/1abc123def" │
│ ──────────────────────────────────────────────────────────────── │
│ CONNECTOR: webdocket.create_docket() │
│ ──────────────────────────────────────────────────────────────── │
│ outputs: │
│ • docket_id: "DOCK-001" │
│ • docket_url: "https://webdocket.com/docket/DOCK-001" │
│ ──────────────────────────────────────────────────────────────── │
│ STORE: step_results['step_3'] = { outputs: {...}, status: 'success' } │
│ LOG: ledger email "step_completed" │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ STEP 4: SEND NOTIFICATION EMAIL │
│ should_run_expr: "1" → TRUE │
│ inputs (before resolution): │
│ • to: "[finance@company.com](mailto:finance@company.com)" │
│ • subject: "Invoice trigger.invoice_number Processed" │
│ • body: "Doc: step_2.outputs.doc_url\\nDocket: step_3.outputs.docket_url" │
│ inputs (after resolution): │
│ • to: "[finance@company.com](mailto:finance@company.com)" │
│ • subject: "Invoice INV-001 Processed" │
│ • body: "Doc: https://docs.google.com/...\\nDocket: https://webdocket.com/..." │
│ ──────────────────────────────────────────────────────────────── │
│ CONNECTOR: gmail.send_email() │
│ ──────────────────────────────────────────────────────────────── │
│ outputs: │
│ • message_id: "msg789xyz" │
│ ──────────────────────────────────────────────────────────────── │
│ STORE: step_results['step_4'] = { outputs: {...}, status: 'success' } │
│ LOG: ledger email "step_completed" │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ WORKFLOW COMPLETE │
│ • status: 'completed' │
│ • completed_at: timestamp │
│ • all steps succeeded │
│ LOG: ledger email "workflow_completed" │
└──────────────────────────────────────────────────────────────────┘
↓
GMAIL LEDGER
5 emails sent with correlation_id
Queryable for complete audit trail
PROMPT 5 — Workflow JSON Specification (Official Schema)
Schema Overview
This is the canonical specification for Mind's Eye MORCO workflow definitions. All workflows must conform to this schema.
Schema Version: 1.0.0
Format: JSON
Encoding: UTF-8
Top-Level Workflow Object
interface Workflow {
// Required fields
workflow_name: string;
description: string;
entry_point: 'google_form' | 'webdocket' | 'chatbot' | 'cron' | 'hunting_engine';
trigger: Trigger;
steps: Step[];
// Optional fields
gmail_ledger?: GmailLedger;
version?: string;
author?: string;
tags?: string[];
metadata?: Record<string, any>;
}
Field Specifications
workflow_name (required)
Type: string
Format: Lowercase, kebab-case
Pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
Max Length: 100 characters
Description: Unique identifier for the workflow. Must be stable across versions.
Valid Examples:
invoice-intake-and-validationmeeting-notes-workflowduplicate-invoice-review
Invalid Examples:
-
Invoice Intake(contains spaces, not lowercase) -
invoice_intake(uses underscores, should be hyphens) -
workflow-123-ABC(contains uppercase)
description (required)
Type: string
Max Length: 500 characters
Description: Human-readable explanation of what the workflow does, its trigger conditions, and expected outcomes. Should include any assumptions.
Example:
{
"description": "Triggered when a vendor submits an invoice through the finance invoice form. Validates required fields, creates a structured Google Doc invoice record, creates a WebDocket entry for tracking, notifies the finance team, and logs all key events to the Gmail ledger. Assumes a dedicated invoice intake form and a shared finance notification email."
}
entry_point (required)
Type: enum
Allowed Values:
-
google_form: Triggered by Google Form submission -
webdocket: Triggered by WebDocket event (created, updated, status changed) -
chatbot: Triggered by conversational AI interaction -
cron: Triggered on a schedule -
hunting_engine: Triggered by pattern detection from Hunting Engine
Description: Specifies how this workflow is triggered.
trigger (required)
Type: object
interface Trigger {
type: string;
source: string;
condition: string;
input_schema: Record<string, string>;
}
Fields:
trigger.type
The specific event type that triggers this workflow.
For google_form entry point:
on_form_submit
For webdocket entry point:
on_docket_createdon_docket_updatedon_status_changed
For chatbot entry point:
on_messageon_intent
For cron entry point:
on_schedule
For hunting_engine entry point:
on_pattern_detected
trigger.source
The source system generating the event.
Allowed Values:
google_formwebdocketgmailchatbotcronhunting_engine
trigger.condition
A boolean expression that must evaluate to true for the workflow to execute.
Examples:
form_id == 'finance_invoice_intake'new_status == 'completed'intent == 'create_meeting_notes' AND has_meeting_details == true-
schedule == '0 9 * * 1'(cron expression: Every Monday at 9am)
trigger.input_schema
Defines the expected structure of the trigger payload.
Format: Map of field names to type descriptions
Example:
{
"input_schema": {
"vendor_name": "string",
"vendor_email": "string",
"invoice_number": "string",
"amount": "number",
"due_date": "string (ISO 8601 date)",
"attachment_url": "string (URL)"
}
}
steps (required)
Type: array
Min Items: 1
interface Step {
id: string;
service: string;
action: string;
inputs: Record<string, any>;
outputs?: Record<string, string>;
binary_logic: BinaryLogic;
description?: string;
}
step.id (required)
Type: string
Format: step_N where N is a number or descriptive identifier
Pattern: ^step_[a-zA-Z0-9_]+$
Must be unique within the workflow.
Examples:
step_1step_validatestep_error_handler
step.service (required)
Type: enum
Allowed Values:
gmailgoogle_docsgoogle_formswebdocket-
google_sheets(future) -
slack(future)
step.action (required)
Type: string
The action to perform on the service. See "Allowed Actions per Service" section below.
step.inputs (required)
Type: object
Inputs for the action. Structure depends on the service and action. Can contain template references.
step.outputs (optional)
Type: object
Describes the expected output structure. Used for documentation and validation.
Example:
{
"outputs": {
"doc_id": "string",
"doc_url": "string (URL)"
}
}
step.binary_logic (required)
interface BinaryLogic {
should_run_expr: string;
on_success?: string;
on_failure?: string;
log_to_ledger?: boolean;
}
binary_logic.should_run_expr (required)
Type: string
Expression that evaluates to 1/true (run) or 0/false (skip).
Examples:
-
"1"— Always run -
"0"— Never run (disabled) -
"[trigger.is](http://trigger.is)_urgent"— Run if trigger field is truthy -
"step_[1.outputs.is](http://1.outputs.is)_valid"— Run if previous step output is valid -
"trigger.amount > 5000"— Run if amount exceeds threshold -
"step_[1.outputs.is](http://1.outputs.is)_valid && trigger.priority == 'high'"— Complex condition
binary_logic.on_success (optional, default: proceed to next step)
Type: string
Allowed Values:
-
"proceed to step_N"— Continue to specific step -
"jump to step_N"— Skip to specific step -
"halt workflow"— Stop execution (success) -
"trigger workflow <name>"— Start another workflow
binary_logic.on_failure (optional, default: halt workflow)
Type: string
Allowed Values:
-
"halt workflow"— Stop execution (failure) -
"log error and continue"— Continue despite error -
"jump to step_N"— Skip to error handler -
"trigger workflow <name>"— Start error handling workflow
binary_logic.log_to_ledger (optional, default: false)
Type: boolean
If true, forces a ledger entry for this step regardless of global gmail_ledger.log_events configuration.
gmail_ledger (optional)
interface GmailLedger {
enabled: boolean;
ledger_recipient: string;
subject_template?: string;
body_template?: string;
log_events?: string[];
labels?: string[];
}
gmail_ledger.enabled (required if gmail_ledger provided)
Type: boolean
Default: true
Whether to write ledger emails for this workflow.
gmail_ledger.ledger_recipient (required if enabled)
Type: string (email address)
Email address to receive ledger entries.
Examples:
gmail_ledger.subject_template (optional)
Type: string
Custom subject line template. If not provided, uses default format.
Default: [LEDGER] workflow={workflow_name} event={event_type} cid={correlation_id}
gmail_ledger.body_template (optional)
Type: string
Custom body template. If not provided, uses default structured format.
gmail_ledger.log_events (optional)
Type: array
Default: [\"workflow_triggered\", \"step_completed\", \"workflow_completed\", \"error\"]
Allowed Values:
workflow_createdworkflow_triggeredstep_completedstep_skippedvalidation_failederrorworkflow_completedworkflow_failed
gmail_ledger.labels (optional)
Type: array
Gmail labels to apply to ledger emails.
Example:
{
\"labels\": [
\"MORCO/Ledger\",
\"MORCO/Workflows/Invoice-Intake\",
\"Finance\"
]
}
Allowed Service Types and Actions
Service: gmail
Actions:
1. send_email
Required Inputs:
-
to:string(email address) -
subject:string -
body:string
Optional Inputs:
-
from:string(defaults to configured sender) -
cc:stringorarray -
bcc:stringorarray -
attachments:array(file URLs or IDs) -
headers:object(custom headers)
Outputs:
-
message_id:string -
thread_id:string -
sent_at:string(ISO 8601 timestamp)
2. create_draft
Required Inputs:
-
to:string -
subject:string -
body:string
Outputs:
-
draft_id:string -
message_id:string
3. add_label
Required Inputs:
-
message_id:string -
label:string
Outputs:
-
success:boolean
Service: google_docs
Actions:
1. create_doc_from_template
Required Inputs:
-
template_doc_id:string -
content_blocks:array
Optional Inputs:
-
title:string -
folder_id:string(Drive folder)
Outputs:
-
doc_id:string -
doc_url:string
2. append_section
Required Inputs:
-
target_doc_id:string -
content:string
Optional Inputs:
-
position:number(default: end of document)
Outputs:
-
doc_id:string -
doc_url:string
3. overwrite_content
Required Inputs:
-
target_doc_id:string -
content:string
Outputs:
-
doc_id:string -
doc_url:string
Service: google_forms
Actions:
1. validate_submission
Required Inputs:
-
submission:object(form data) -
required_fields:array
Optional Inputs:
-
numeric_fields:array -
email_fields:array -
date_fields:array
Outputs:
-
is_valid:boolean -
validation_errors:array -
normalized_payload:object
2. get_response
Required Inputs:
-
form_id:string -
response_id:string
Outputs:
-
response:object
3. update_response
Required Inputs:
-
form_id:string -
response_id:string -
updates:object
Outputs:
-
success:boolean
Service: webdocket
Actions:
1. create_docket
Required Inputs:
-
title:string -
description:string
Optional Inputs:
-
status:string -
priority:string -
assignee_email:string -
due_date:string(ISO 8601) -
custom_fields:object
Outputs:
-
docket_id:string -
docket_url:string -
created_at:string
2. update_docket
Required Inputs:
-
docket_id:string -
updates:object
Outputs:
-
docket_id:string -
docket_url:string -
updated_at:string
3. get_docket_details
Required Inputs:
-
docket_id:string
Outputs:
-
docket:object(full docket data)
4. change_status
Required Inputs:
-
docket_id:string -
new_status:string
Optional Inputs:
-
notes:string
Outputs:
-
docket_id:string -
old_status:string -
new_status:string -
changed_at:string
Template Variable Rules
Reference Syntax
Trigger Data:
trigger.field_name
trigger.nested.field_name
trigger.array_field[0]
Previous Step Outputs:
step_N.outputs.field_name
step_N.outputs.nested.field_name
step_N.status
Step Status:
step_N.status → "success" | "failed" | "skipped"
String Interpolation
Use variable syntax for embedding values in strings:
{
\"subject\": \"Invoice trigger.invoice_number from trigger.vendor_name\"
}
Direct Reference
Use direct reference when the entire value should be replaced:
{
\"doc_url\": \"step_2.outputs.doc_url\"
}
Allowed in:
-
step.inputs(all fields) -
binary_logic.should_run_expr(boolean expressions) -
gmail_ledger.subject_template(string interpolation) -
gmail_ledger.body_template(string interpolation)
Not Allowed in:
workflow_namedescriptionstep.idstep.servicestep.actionbinary_logic.on_successbinary_logic.on_failure
Validation Rules
All workflows must pass these validation checks before execution:
Schema Validation
- Required Fields: All required fields must be present
- Type Checking: All fields must match their specified types
- Enum Validation: Enum fields must use allowed values only
- Format Validation: String formats (email, URL, ISO dates) must be valid
Semantic Validation
-
Unique Step IDs: No duplicate
step.idvalues -
Valid Step References: All
step_Nreferences must point to existing steps - Forward References Only: Steps can only reference previous steps (step_1 can't reference step_2)
- Template Variables: All template references must be valid
- Service/Action Pairs: Action must be supported by the specified service
- Required Inputs: All required inputs for the action must be provided
Logical Validation
- Reachable Steps: All steps must be reachable from workflow start
-
No Circular Jumps:
jump tologic must not create infinite loops -
Binary Logic:
should_run_exprmust be a valid boolean expression -
on_success/on_failure: Target steps must exist if using
jump toortrigger workflow
Complete Workflow Example
{
\"workflow_name\": \"invoice-intake-and-validation\",
\"description\": \"Triggered when a vendor submits an invoice through the finance invoice form. Validates required fields, creates a structured Google Doc invoice record, creates a WebDocket entry for tracking, notifies the finance team, and logs all key events to the Gmail ledger.\",
\"version\": \"1.0.0\",
\"author\": \"Finance Team\",
\"tags\": [\"finance\", \"invoices\", \"intake\"],
\"entry_point\": \"google_form\",
\"trigger\": {
\"type\": \"on_form_submit\",
\"source\": \"google_form\",
\"condition\": \"form_id == 'finance_invoice_intake'\",
\"input_schema\": {
\"vendor_name\": \"string\",
\"vendor_email\": \"string\",
\"invoice_number\": \"string\",
\"amount\": \"number\",
\"due_date\": \"string (ISO 8601 date)\",
\"attachment_url\": \"string (URL)\"
}
},
\"steps\": [
{
\"id\": \"step_1\",
\"description\": \"Validate invoice submission data\",
\"service\": \"google_forms\",
\"action\": \"validate_submission\",
\"inputs\": {
\"submission\": \"trigger\",
\"required_fields\": [
\"vendor_name\",
\"vendor_email\",
\"invoice_number\",
\"amount\",
\"due_date\"
],
\"numeric_fields\": [\"amount\"],
\"email_fields\": [\"vendor_email\"]
},
\"outputs\": {
\"is_valid\": \"boolean\",
\"validation_errors\": \"array\",
\"normalized_payload\": \"object\"
},
\"binary_logic\": {
\"should_run_expr\": \"1\",
\"on_success\": \"proceed to step_2\",
\"on_failure\": \"jump to step_error\",
\"log_to_ledger\": true
}
},
{
\"id\": \"step_2\",
\"description\": \"Create Google Doc invoice record from template\",
\"service\": \"google_docs\",
\"action\": \"create_doc_from_template\",
\"inputs\": {
\"template_doc_id\": \"1ABC123DEF456\",
\"content_blocks\": [
{
\"type\": \"heading\",
\"value\": \"Invoice: trigger.invoice_number\"
},
{
\"type\": \"paragraph\",
\"value\": \"Vendor: trigger.vendor_name\"
},
{
\"type\": \"paragraph\",
\"value\": \"Amount: $trigger.amount\"
},
{
\"type\": \"paragraph\",
\"value\": \"Due Date: trigger.due_date\"
}
]
},
\"outputs\": {
\"doc_id\": \"string\",
\"doc_url\": \"string\"
},
\"binary_logic\": {
\"should_run_expr\": \"step_
[1.outputs.is](http://1.outputs.is)_valid\",
\"on_success\": \"proceed to step_3\",
\"on_failure\": \"halt workflow\",
\"log_to_ledger\": true
}
},
{
\"id\": \"step_3\",
\"description\": \"Create WebDocket tracking entry\",
\"service\": \"webdocket\",
\"action\": \"create_docket\",
\"inputs\": {
\"title\": \"Invoice: trigger.invoice_number\",
\"description\": \"Invoice from trigger.vendor_name for $trigger.amount. Due: trigger.due_date. Doc: step_2.outputs.doc_url\",
\"status\": \"pending_review\",
\"priority\": \"normal\",
\"assignee_email\": \"[finance-team@company.com](mailto:finance-team@company.com)\",
\"custom_fields\": {
\"invoice_number\": \"trigger.invoice_number\",
\"vendor_name\": \"trigger.vendor_name\",
\"amount\": \"trigger.amount\",
\"doc_url\": \"step_2.outputs.doc_url\"
}
},
\"outputs\": {
\"docket_id\": \"string\",
\"docket_url\": \"string\"
},
\"binary_logic\": {
\"should_run_expr\": \"1\",
\"on_success\": \"proceed to step_4\",
\"on_failure\": \"log error and continue\",
\"log_to_ledger\": true
}
},
{
\"id\": \"step_4\",
\"description\": \"Send notification email to finance team\",
\"service\": \"gmail\",
\"action\": \"send_email\",
\"inputs\": {
\"to\": \"[finance-team@company.com](mailto:finance-team@company.com)\",
\"subject\": \"New Invoice: trigger.invoice_number from trigger.vendor_name\",
\"body\": \"A new invoice has been received:\\n\\nVendor: trigger.vendor_name\\nInvoice Number: trigger.invoice_number\\nAmount: $trigger.amount\\nDue Date: trigger.due_date\\n\\nDocument: step_2.outputs.doc_url\\nDocket: step_3.outputs.docket_url\"
},
\"outputs\": {
\"message_id\": \"string\"
},
\"binary_logic\": {
\"should_run_expr\": \"1\",
\"on_failure\": \"log error and continue\",
\"log_to_ledger\": true
}
},
{
\"id\": \"step_error\",
\"description\": \"Handle validation failure\",
\"service\": \"gmail\",
\"action\": \"send_email\",
\"inputs\": {
\"to\": \"trigger.vendor_email\",
\"cc\": \"[finance-team@company.com](mailto:finance-team@company.com)\",
\"subject\": \"Invoice Submission Error: trigger.invoice_number\",
\"body\": \"Your invoice submission has validation errors:\\n\\nstep_1.outputs.validation_errors\\n\\nPlease correct these issues and resubmit.\"
},
\"binary_logic\": {
\"should_run_expr\": \"!step_[1.outputs.is](http://1.outputs.is)_valid\",
\"on_success\": \"halt workflow\",
\"log_to_ledger\": true
}
}
],
\"gmail_ledger\": {
\"enabled\": true,
\"ledger_recipient\": \"[ledger+finance@company.com](mailto:ledger+finance@company.com)\",
\"log_events\": [
\"workflow_triggered\",
\"step_completed\",
\"validation_failed\",
\"error\",
\"workflow_completed\",
\"workflow_failed\"
],
\"labels\": [
\"MORCO/Ledger\",
\"MORCO/Workflows/Invoice-Intake\",
\"Finance/Invoices\"
]
}
}
PROMPT 6 — Example Workflows (3 Full Examples)
This section provides three complete, production-ready workflow examples demonstrating different entry points and use cases.
Example 1: Invoice Intake & Validation
Use Case
Scenario: A vendor submits an invoice through a Google Form. The workflow validates the submission, creates a structured document, creates a tracking docket, and notifies the finance team.
Entry Point: google_form
Complexity: Medium (4 steps + error handler)
Services Used: Google Forms, Google Docs, WebDocket, Gmail
Visual Flow
┌─────────────────────────────────────────────────────────────────┐
│ INVOICE INTAKE WORKFLOW │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────────┐
│ GOOGLE FORM SUBMIT │
│ • Vendor Name │
│ • Invoice Number │
│ • Amount │
│ • Due Date │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ STEP 1: VALIDATE │
│ • Required fields │
│ • Numeric formats │
│ • Email validation │
└──────────┬───────────┘
│
┌──────────────┼──────────────┐
│ Valid? │ │
▼ ▼ │
YES NO │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ STEP_ERROR: │ │
│ │ Send rejection │ │
│ │ email to vendor │ │
│ └──────────────────────┘ │
│ │ │
│ ▼ │
│ HALT WORKFLOW │
│ │
▼ │
┌──────────────────────┐ │
│ STEP 2: CREATE DOC │ │
│ • Use template │ │
│ • Fill invoice data │ │
└──────────┬───────────┘ │
│ │
▼ │
┌──────────────────────┐ │
│ STEP 3: CREATE │ │
│ WEBDOCKET │ │
│ • Link to doc │ │
│ • Assign to team │ │
└──────────┬───────────┘ │
│ │
▼ │
┌──────────────────────┐ │
│ STEP 4: NOTIFY │ │
│ FINANCE TEAM │ │
│ • Doc link │ │
│ • Docket link │ │
└──────────┬───────────┘ │
│ │
▼ │
COMPLETED │
│
┌────────────────────────────────────────────┴──────────────────┐
│ GMAIL LEDGER: 5-6 emails tracking entire workflow execution │
└───────────────────────────────────────────────────────────────┘
Complete JSON
{
"workflow_name": "invoice-intake-and-validation",
"description": "Validates vendor invoice submissions, creates documentation, tracks in WebDocket, and notifies finance team. Handles both successful and failed validations with appropriate routing.",
"version": "1.0.0",
"author": "Finance Automation Team",
"tags": ["finance", "invoices", "validation", "intake"],
"entry_point": "google_form",
"trigger": {
"type": "on_form_submit",
"source": "google_form",
"condition": "form_id == 'finance_invoice_intake'",
"input_schema": {
"vendor_name": "string",
"vendor_email": "string (email)",
"invoice_number": "string",
"amount": "number",
"due_date": "string (ISO 8601 date)",
"attachment_url": "string (URL)",
"notes": "string (optional)"
}
},
"steps": [
{
"id": "step_1",
"description": "Validate invoice submission data for completeness and correctness",
"service": "google_forms",
"action": "validate_submission",
"inputs": {
"submission": "trigger",
"required_fields": [
"vendor_name",
"vendor_email",
"invoice_number",
"amount",
"due_date"
],
"numeric_fields": ["amount"],
"email_fields": ["vendor_email"]
},
"outputs": {
"is_valid": "boolean",
"validation_errors": "array<string>",
"normalized_payload": "object"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_2",
"on_failure": "jump to step_error",
"log_to_ledger": true
}
},
{
"id": "step_2",
"description": "Create structured Google Doc from template with invoice details",
"service": "google_docs",
"action": "create_doc_from_template",
"inputs": {
"template_doc_id": "1ABC123DEF456GHI789JKL",
"title": "Invoice: trigger.invoice_number - trigger.vendor_name",
"content_blocks": [
{
"type": "heading",
"value": "Invoice Record: trigger.invoice_number"
},
{
"type": "paragraph",
"value": "Submission Date: trigger.submission_timestamp"
},
{
"type": "heading",
"value": "Vendor Information"
},
{
"type": "paragraph",
"value": "Name: trigger.vendor_name"
},
{
"type": "paragraph",
"value": "Email: trigger.vendor_email"
},
{
"type": "heading",
"value": "Invoice Details"
},
{
"type": "paragraph",
"value": "Invoice Number: trigger.invoice_number"
},
{
"type": "paragraph",
"value": "Amount: $trigger.amount"
},
{
"type": "paragraph",
"value": "Due Date: trigger.due_date"
},
{
"type": "paragraph",
"value": "Attachment: trigger.attachment_url"
},
{
"type": "heading",
"value": "Notes"
},
{
"type": "paragraph",
"value": "trigger.notes"
}
]
},
"outputs": {
"doc_id": "string",
"doc_url": "string"
},
"binary_logic": {
"should_run_expr": "step_[1.outputs.is](http://1.outputs.is)_valid",
"on_success": "proceed to step_3",
"on_failure": "halt workflow",
"log_to_ledger": true
}
},
{
"id": "step_3",
"description": "Create WebDocket for finance team review and tracking",
"service": "webdocket",
"action": "create_docket",
"inputs": {
"title": "Invoice Review: trigger.invoice_number",
"description": "Vendor: trigger.vendor_name\\nAmount: $trigger.amount\\nDue: trigger.due_date\\n\\nDocument: step_2.outputs.doc_url\\n\\nSubmitted: trigger.submission_timestamp",
"status": "pending_review",
"priority": "normal",
"assignee_email": "[finance-team@company.com](mailto:finance-team@company.com)",
"due_date": "trigger.due_date",
"custom_fields": {
"invoice_number": "trigger.invoice_number",
"vendor_name": "trigger.vendor_name",
"amount": "trigger.amount",
"vendor_email": "trigger.vendor_email",
"doc_url": "step_2.outputs.doc_url",
"correlation_id": "run_state.correlation_id"
}
},
"outputs": {
"docket_id": "string",
"docket_url": "string",
"created_at": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_4",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_4",
"description": "Send notification to finance team with all relevant links",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "[finance-team@company.com](mailto:finance-team@company.com)",
"subject": "New Invoice Submission: trigger.invoice_number - trigger.vendor_name",
"body": "A new invoice has been received and validated.\\n\\n=== VENDOR ===\\nName: trigger.vendor_name\\nEmail: trigger.vendor_email\\n\\n=== INVOICE ===\\nInvoice Number: trigger.invoice_number\\nAmount: $trigger.amount\\nDue Date: trigger.due_date\\n\\n=== DOCUMENTS ===\\nInvoice Document: step_2.outputs.doc_url\\nTracking Docket: step_3.outputs.docket_url\\nOriginal Attachment: trigger.attachment_url\\n\\n=== NEXT STEPS ===\\nPlease review the invoice in the docket and take appropriate action.\\n\\nCorrelation ID: run_state.correlation_id"
},
"outputs": {
"message_id": "string",
"sent_at": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_error",
"description": "Handle validation failure by notifying vendor of errors",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "trigger.vendor_email",
"cc": "[finance-team@company.com](mailto:finance-team@company.com)",
"subject": "Invoice Submission Error: trigger.invoice_number",
"body": "Hello trigger.vendor_name,\\n\\nYour invoice submission (trigger.invoice_number) could not be processed due to the following validation errors:\\n\\nstep_1.outputs.validation_errors\\n\\nPlease correct these issues and resubmit the invoice through the same form.\\n\\nIf you have questions, please contact [finance-team@company.com](mailto:finance-team@company.com).\\n\\nThank you,\\nFinance Automation System"
},
"outputs": {
"message_id": "string"
},
"binary_logic": {
"should_run_expr": "!step_[1.outputs.is](http://1.outputs.is)_valid",
"on_success": "halt workflow",
"on_failure": "halt workflow",
"log_to_ledger": true
}
}
],
"gmail_ledger": {
"enabled": true,
"ledger_recipient": "[ledger+finance@company.com](mailto:ledger+finance@company.com)",
"log_events": [
"workflow_triggered",
"step_completed",
"validation_failed",
"error",
"workflow_completed",
"workflow_failed"
],
"labels": [
"MORCO/Ledger",
"MORCO/Workflows/Invoice-Intake",
"MORCO/Department/Finance"
]
}
}
Logic Explanation
Trigger Phase:
- Workflow activates when the finance invoice intake form (
form_id == 'finance_invoice_intake') is submitted - Captures vendor information, invoice details, and optional notes
Validation Logic (Step 1):
- Checks all required fields are present
- Validates
amountis numeric - Validates
vendor_emailis a valid email format - If validation fails: jumps to
step_error, bypassing all processing steps - If validation succeeds: continues to
step_2
Document Creation (Step 2):
- Only runs if
step_[1.outputs.is](http://1.outputs.is)_valid == true(explicit conditional) - Creates a Google Doc from a predefined template
- Populates template with structured invoice data
- Output:
doc_idanddoc_urlfor reference in subsequent steps
Docket Creation (Step 3):
- Creates a WebDocket task for finance team tracking
- Links to the Google Doc created in Step 2
- Includes correlation ID for cross-system tracing
- Uses
log error and continuestrategy - if WebDocket fails, workflow continues - Priority:
normal(could be made dynamic based on amount)
Team Notification (Step 4):
- Sends comprehensive email to finance team
- Includes all relevant links (doc, docket, attachment)
- Provides correlation ID for ledger queries
- Uses
log error and continue- even if email fails, workflow is marked complete
Error Handling (step_error):
- Only runs if
!step_[1.outputs.is](http://1.outputs.is)_valid(validation failed) - Sends rejection email to vendor with specific error details
- CCs finance team for visibility
- Halts workflow after sending (no further processing needed)
Ledger Integration:
- Logs 5-6 events per successful run
- Logs 2-3 events per failed validation
- All events queryable by correlation ID
- Enables complete audit trail
Example 2: Chatbot Request → Case Creation
Use Case
Scenario: A user interacts with a chatbot to request support. The chatbot extracts structured data from the conversation, then triggers this workflow to create a formal case, generate documentation, and send confirmations.
Entry Point: chatbot
Complexity: Medium (4 steps)
Services Used: WebDocket, Google Docs, Gmail
Visual Flow
┌─────────────────────────────────────────────────────────────────┐
│ CHATBOT CASE CREATION WORKFLOW │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────┐
│ CHATBOT CONVERSATION │
│ User: "I need help with..." │
│ Bot: Extracts structured data │
│ Intent: create_support_case │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ WORKFLOW TRIGGERED │
│ • User name & email │
│ • Issue description │
│ • Priority │
│ • Conversation transcript │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 1: CREATE WEBDOCKET CASE │
│ • Title from issue summary │
│ • Description with transcript │
│ • Auto-assign to support team │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 2: GENERATE CASE DOC │
│ • Formal case documentation │
│ • Full conversation log │
│ • Action items extracted │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 3: UPDATE DOCKET │
│ • Add doc link to docket │
│ • Add correlation ID │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 4: SEND CONFIRMATION │
│ • Email to user │
│ • Case number & links │
│ • Expected response time │
└──────────────┬───────────────────────┘
│
▼
COMPLETED
┌───────────────────────────────────────────────────────────────┐
│ GMAIL LEDGER: Complete workflow trace with all case details │
└───────────────────────────────────────────────────────────────┘
Complete JSON
{
"workflow_name": "chatbot-support-case-creation",
"description": "Triggered when chatbot detects a support request intent. Creates a WebDocket case, generates formal documentation, and sends confirmation to the user. Designed for conversational support intake with full transcript preservation.",
"version": "1.0.0",
"author": "Support Automation Team",
"tags": ["support", "chatbot", "cases", "customer-service"],
"entry_point": "chatbot",
"trigger": {
"type": "on_intent",
"source": "chatbot",
"condition": "intent == 'create_support_case' AND has_required_data == true",
"input_schema": {
"user_name": "string",
"user_email": "string (email)",
"issue_summary": "string (max 200 chars)",
"issue_description": "string",
"priority": "string (low|normal|high|urgent)",
"conversation_id": "string",
"conversation_transcript": "string",
"extracted_action_items": "array<string> (optional)",
"request_timestamp": "string (ISO 8601)"
}
},
"steps": [
{
"id": "step_1",
"description": "Create WebDocket support case from chatbot interaction",
"service": "webdocket",
"action": "create_docket",
"inputs": {
"title": "Support Case: trigger.issue_summary",
"description": "User: trigger.user_name (trigger.user_email)\\n\\nIssue:\\ntrigger.issue_description\\n\\nPriority: trigger.priority\\nConversation ID: trigger.conversation_id\\n\\nRequested: trigger.request_timestamp",
"status": "new",
"priority": "trigger.priority",
"assignee_email": "[support-team@company.com](mailto:support-team@company.com)",
"custom_fields": {
"source": "chatbot",
"user_name": "trigger.user_name",
"user_email": "trigger.user_email",
"conversation_id": "trigger.conversation_id",
"created_via": "chatbot-automation"
}
},
"outputs": {
"docket_id": "string",
"docket_url": "string",
"case_number": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_2",
"on_failure": "halt workflow",
"log_to_ledger": true
}
},
{
"id": "step_2",
"description": "Generate formal case documentation with full conversation history",
"service": "google_docs",
"action": "create_doc_from_template",
"inputs": {
"template_doc_id": "1SUPPORT987CASE654TEMPLATE321",
"title": "Case trigger.issue_summary - trigger.user_name",
"content_blocks": [
{
"type": "heading",
"value": "Support Case: step_[1.outputs.case](http://1.outputs.case)_number"
},
{
"type": "paragraph",
"value": "Created: trigger.request_timestamp"
},
{
"type": "paragraph",
"value": "Source: Chatbot (Conversation ID: trigger.conversation_id)"
},
{
"type": "heading",
"value": "User Information"
},
{
"type": "paragraph",
"value": "Name: trigger.user_name"
},
{
"type": "paragraph",
"value": "Email: trigger.user_email"
},
{
"type": "heading",
"value": "Issue Summary"
},
{
"type": "paragraph",
"value": "trigger.issue_summary"
},
{
"type": "heading",
"value": "Detailed Description"
},
{
"type": "paragraph",
"value": "trigger.issue_description"
},
{
"type": "heading",
"value": "Priority"
},
{
"type": "paragraph",
"value": "trigger.priority"
},
{
"type": "heading",
"value": "Action Items"
},
{
"type": "paragraph",
"value": "trigger.extracted_action_items"
},
{
"type": "heading",
"value": "Full Conversation Transcript"
},
{
"type": "paragraph",
"value": "trigger.conversation_transcript"
},
{
"type": "heading",
"value": "Tracking"
},
{
"type": "paragraph",
"value": "Docket: step_1.outputs.docket_url"
},
{
"type": "paragraph",
"value": "Correlation ID: run_state.correlation_id"
}
]
},
"outputs": {
"doc_id": "string",
"doc_url": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_3",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_3",
"description": "Update WebDocket with link to case documentation",
"service": "webdocket",
"action": "update_docket",
"inputs": {
"docket_id": "step_1.outputs.docket_id",
"updates": {
"custom_fields": {
"case_doc_url": "step_2.outputs.doc_url",
"correlation_id": "run_state.correlation_id",
"doc_created_at": "step_2.outputs.created_at"
}
}
},
"outputs": {
"updated_at": "string"
},
"binary_logic": {
"should_run_expr": "step_2.outputs.doc_id != null",
"on_success": "proceed to step_4",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_4",
"description": "Send confirmation email to user with case details",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "trigger.user_email",
"subject": "Your Support Case Has Been Created - step_[1.outputs.case](http://1.outputs.case)_number",
"body": "Hello trigger.user_name,\\n\\nThank you for contacting support. Your case has been created and assigned to our team.\\n\\n=== CASE DETAILS ===\\nCase Number: step_[1.outputs.case](http://1.outputs.case)_number\\nPriority: trigger.priority\\nStatus: New\\n\\n=== WHAT YOU REPORTED ===\\ntrigger.issue_summary\\n\\n=== WHAT HAPPENS NEXT ===\\nOur support team will review your case and respond within:\\n- Urgent: 2 hours\\n- High: 4 hours\\n- Normal: 1 business day\\n- Low: 2 business days\\n\\n=== CASE TRACKING ===\\nYou can track your case here: step_1.outputs.docket_url\\nCase Documentation: step_2.outputs.doc_url\\n\\nCase Reference: step_[1.outputs.case](http://1.outputs.case)_number\\n\\nIf you have additional information, please reply to this email.\\n\\nBest regards,\\nSupport Team"
},
"outputs": {
"message_id": "string",
"sent_at": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed",
"on_failure": "log error and continue",
"log_to_ledger": true
}
}
],
"gmail_ledger": {
"enabled": true,
"ledger_recipient": "[ledger+support@company.com](mailto:ledger+support@company.com)",
"log_events": [
"workflow_triggered",
"step_completed",
"error",
"workflow_completed",
"workflow_failed"
],
"labels": [
"MORCO/Ledger",
"MORCO/Workflows/Support-Case-Creation",
"MORCO/Department/Support",
"MORCO/Source/Chatbot"
]
}
}
Logic Explanation
Trigger Phase:
- Workflow activates when chatbot detects
intent == 'create_support_case' - Chatbot must extract and provide all required structured data
- Conditional:
has_required_data == trueensures data quality before triggering
Case Creation (Step 1):
- Creates WebDocket immediately to capture the case
- Uses chatbot-extracted priority level directly
- Auto-assigns to
support-team@company.com - Stores conversation ID for traceability
- Critical step: failure halts workflow (support case MUST be created)
Documentation Generation (Step 2):
- Creates comprehensive Google Doc with all case details
- Preserves full conversation transcript (important for context)
- Includes extracted action items if chatbot identified any
- Embeds docket link for cross-reference
- Non-critical: uses
log error and continue- case exists even if doc fails
Docket Enhancement (Step 3):
- Conditional:
step_2.outputs.doc_id != null(only if doc was created) - Updates the WebDocket with doc URL
- Adds correlation ID for distributed tracing
- Allows failure - docket is already functional without doc link
User Confirmation (Step 4):
- Sends professional confirmation email
- Includes case number for future reference
- Sets expectations for response time based on priority
- Provides tracking links
- Non-critical: case is created even if email fails
Workflow Characteristics:
- Resilience: Only Step 1 is critical; others gracefully degrade
- User Experience: Fast initial response (case created immediately)
- Traceability: Conversation ID + correlation ID = full audit trail
- Automation: No human intervention required from chatbot to case creation
Example 3: WebDocket Status Change Handler
Use Case
Scenario: When a WebDocket status changes to "completed", automatically update documentation, notify stakeholders, archive records, and log the completion for audit purposes.
Entry Point: webdocket
Complexity: Medium (5 steps with conditional logic)
Services Used: WebDocket, Google Docs, Gmail
Visual Flow
┌─────────────────────────────────────────────────────────────────┐
│ WEBDOCKET STATUS CHANGE HANDLER WORKFLOW │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────┐
│ WEBDOCKET STATUS CHANGED │
│ • Old Status: in_progress │
│ • New Status: completed │
│ • Completed By: [user@company.com](mailto:user@company.com) │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ TRIGGER CONDITION CHECK │
│ new_status == 'completed' │
└──────────────┬───────────────────────┘
│ Match
▼
┌──────────────────────────────────────┐
│ STEP 1: GET DOCKET DETAILS │
│ • Fetch full docket data │
│ • Get assignee │
│ • Get related doc URLs │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 2: UPDATE CASE DOC │
│ • Append completion section │
│ • Add timestamp & user │
│ • Include resolution notes │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 3: NOTIFY ASSIGNEE │
│ Should run: assignee exists? │
│ • Send completion confirmation │
│ • Include final status │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 4: NOTIFY CUSTOMER │
│ Should run: customer email exists? │
│ • Send closure notification │
│ • Request feedback │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ STEP 5: CREATE ARCHIVE RECORD │
│ • Generate completion summary doc │
│ • Link all related docs │
│ • Mark for long-term storage │
└──────────────┬───────────────────────┘
│
▼
COMPLETED
┌───────────────────────────────────────────────────────────────┐
│ GMAIL LEDGER: Full completion workflow with all updates │
└───────────────────────────────────────────────────────────────┘
Complete JSON
{
"workflow_name": "webdocket-completion-handler",
"description": "Triggered when a WebDocket status changes to 'completed'. Updates documentation, notifies relevant parties (assignee and customer if applicable), and creates archival records. Demonstrates conditional notification logic based on available data.",
"version": "1.0.0",
"author": "Operations Automation Team",
"tags": ["webdocket", "completion", "notifications", "archival"],
"entry_point": "webdocket",
"trigger": {
"type": "on_status_changed",
"source": "webdocket",
"condition": "new_status == 'completed'",
"input_schema": {
"docket_id": "string",
"title": "string",
"old_status": "string",
"new_status": "string (must be 'completed')",
"completed_by": "string (email)",
"completion_notes": "string",
"completion_timestamp": "string (ISO 8601)",
"assignee_email": "string (optional)",
"metadata": "object (optional, may contain customer_email, case_doc_url)"
}
},
"steps": [
{
"id": "step_1",
"description": "Fetch complete docket details including all custom fields",
"service": "webdocket",
"action": "get_docket_details",
"inputs": {
"docket_id": "trigger.docket_id"
},
"outputs": {
"docket": "object (full docket data)",
"assignee_email": "string",
"customer_email": "string",
"case_doc_url": "string",
"original_requester": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed to step_2",
"on_failure": "halt workflow",
"log_to_ledger": true
}
},
{
"id": "step_2",
"description": "Update case documentation with completion details",
"service": "google_docs",
"action": "append_section",
"inputs": {
"target_doc_id": "step_[1.outputs.case](http://1.outputs.case)_doc_url",
"content": "\\n\\n=== CASE COMPLETION ===\\n\\nCompleted By: trigger.completed_by\\nCompletion Date: trigger.completion_timestamp\\nPrevious Status: trigger.old_status\\nFinal Status: [trigger.new](http://trigger.new)_status\\n\\n=== RESOLUTION NOTES ===\\n\\ntrigger.completion_notes\\n\\n=== AUDIT TRAIL ===\\n\\nCorrelation ID: run_state.correlation_id\\nWorkflow: webdocket-completion-handler\\nProcessed: run_state.timestamp"
},
"outputs": {
"doc_id": "string",
"updated_at": "string"
},
"binary_logic": {
"should_run_expr": "step_[1.outputs.case](http://1.outputs.case)_doc_url != null",
"on_success": "proceed to step_3",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_3",
"description": "Notify assignee of docket completion",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "step_1.outputs.assignee_email",
"subject": "Docket Completed: trigger.title",
"body": "Hello,\\n\\nThe docket you were assigned has been marked as completed.\\n\\n=== DOCKET DETAILS ===\\nTitle: trigger.title\\nDocket ID: trigger.docket_id\\nCompleted By: trigger.completed_by\\nCompletion Time: trigger.completion_timestamp\\n\\n=== RESOLUTION ===\\ntrigger.completion_notes\\n\\n=== DOCUMENTATION ===\\nCase Document: step_[1.outputs.case](http://1.outputs.case)_doc_url\\nDocket URL: step_1.outputs.docket_url\\n\\nThank you for your work on this case.\\n\\nAutomated notification from MORCO"
},
"outputs": {
"message_id": "string"
},
"binary_logic": {
"should_run_expr": "step_1.outputs.assignee_email != null",
"on_success": "proceed to step_4",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_4",
"description": "Notify customer if customer email is available in docket metadata",
"service": "gmail",
"action": "send_email",
"inputs": {
"to": "step_1.outputs.customer_email",
"subject": "Your Request Has Been Completed - trigger.title",
"body": "Hello,\\n\\nWe're writing to let you know that your request has been completed.\\n\\n=== REQUEST SUMMARY ===\\ntrigger.title\\n\\nCompleted: trigger.completion_timestamp\\n\\n=== RESOLUTION ===\\ntrigger.completion_notes\\n\\n=== YOUR FEEDBACK ===\\nWe value your feedback. Please let us know how we did by replying to this email.\\n\\nIf you have any questions about this resolution, please don't hesitate to reach out.\\n\\nThank you,\\nOperations Team\\n\\n---\\nReference: trigger.docket_id"
},
"outputs": {
"message_id": "string",
"sent_to": "string"
},
"binary_logic": {
"should_run_expr": "step_1.outputs.customer_email != null && step_1.outputs.customer_email != ''",
"on_success": "proceed to step_5",
"on_failure": "log error and continue",
"log_to_ledger": true
}
},
{
"id": "step_5",
"description": "Create archive record summarizing the completed docket",
"service": "google_docs",
"action": "create_doc_from_template",
"inputs": {
"template_doc_id": "1ARCHIVE987TEMPLATE654COMPLETE321",
"title": "ARCHIVED - trigger.title - trigger.completion_timestamp",
"folder_id": "archive_folder_id",
"content_blocks": [
{
"type": "heading",
"value": "Completion Archive: trigger.docket_id"
},
{
"type": "paragraph",
"value": "Original Title: trigger.title"
},
{
"type": "paragraph",
"value": "Completed: trigger.completion_timestamp"
},
{
"type": "paragraph",
"value": "Completed By: trigger.completed_by"
},
{
"type": "heading",
"value": "Status History"
},
{
"type": "paragraph",
"value": "Initial Status: (from docket history)"
},
{
"type": "paragraph",
"value": "Final Status: [trigger.new](http://trigger.new)_status"
},
{
"type": "paragraph",
"value": "Previous Status: trigger.old_status"
},
{
"type": "heading",
"value": "Resolution"
},
{
"type": "paragraph",
"value": "trigger.completion_notes"
},
{
"type": "heading",
"value": "Related Documents"
},
{
"type": "paragraph",
"value": "Case Documentation: step_[1.outputs.case](http://1.outputs.case)_doc_url"
},
{
"type": "paragraph",
"value": "Original Docket: step_1.outputs.docket_url"
},
{
"type": "heading",
"value": "Notifications Sent"
},
{
"type": "paragraph",
"value": "Assignee: step_1.outputs.assignee_email - step_3.outputs.message_id"
},
{
"type": "paragraph",
"value": "Customer: step_1.outputs.customer_email - step_4.outputs.message_id"
},
{
"type": "heading",
"value": "Audit Trail"
},
{
"type": "paragraph",
"value": "Workflow: webdocket-completion-handler"
},
{
"type": "paragraph",
"value": "Correlation ID: run_state.correlation_id"
},
{
"type": "paragraph",
"value": "Archived: run_state.timestamp"
}
]
},
"outputs": {
"archive_doc_id": "string",
"archive_doc_url": "string"
},
"binary_logic": {
"should_run_expr": "1",
"on_success": "proceed",
"on_failure": "log error and continue",
"log_to_ledger": true
}
}
},
"gmail_ledger": {
"enabled": true,
"ledger_recipient": "[ledger+operations@company.com](mailto:ledger+operations@company.com)",
"log_events": [
"workflow_triggered",
"step_completed",
"step_skipped",
"error",
"workflow_completed",
"workflow_failed"
],
"labels": [
"MORCO/Ledger",
"MORCO/Workflows/Docket-Completion",
"MORCO/Department/Operations",
"MORCO/Trigger/WebDocket"
]
}
}
Logic Explanation
Trigger Phase:
- Workflow only activates when
new_status == 'completed'(ignores other status changes) - Captures who completed the docket and when
- Includes completion notes for context
Data Gathering (Step 1):
- Critical first step: fetches complete docket details
- Needed because trigger payload may not include all metadata
- Retrieves assignee email, customer email (if exists), and doc URLs
- Failure here halts workflow - can't proceed without data
Documentation Update (Step 2):
-
Conditional:
step_[1.outputs.case](http://1.outputs.case)_doc_url != null - Only runs if a case doc exists (not all dockets have docs)
- Appends completion section with timestamp, completer, and notes
- Non-critical: uses
log error and continue
Assignee Notification (Step 3):
-
Conditional:
step_1.outputs.assignee_email != null - Only sends if docket had an assignee
- Professional notification with full context
- Includes all relevant links
- Non-critical: docket is completed regardless of email success
Customer Notification (Step 4):
-
Conditional:
step_1.outputs.customer_email != null && != '' - Two-part check: field exists AND is not empty string
- Customer-facing language (different tone than assignee email)
- Requests feedback
- Non-critical: completion valid even without customer notification
Archival Record (Step 5):
- Always runs (unconditional)
- Creates comprehensive archive document
- Consolidates all completion information in one place
- Includes references to all emails sent (via message IDs)
- Saved to designated archive folder
- Non-critical: original docket and case doc still exist if this fails
Advanced Features Demonstrated:
-
Multi-Condition Logic: Step 4 shows AND logic in
should_run_expr - Graceful Degradation: Each notification step can fail independently
- Data Enrichment: Step 1 enriches trigger data with full docket details
- Audit Completeness: Archive doc includes message IDs for sent emails
- Conditional Notifications: Different stakeholders get different messages
Ledger Benefits:
- Logs both
step_completedandstep_skippedevents - Can query ledger to see which notifications were actually sent
- Correlation ID traces the entire completion process
- Enables metrics: average time from creation to completion
Key Patterns Across Examples
Pattern 1: Progressive Robustness
Strategy: Critical steps use on_failure: halt workflow, while nice-to-have steps use log error and continue.
Example 1: Validation must succeed; notifications can fail
Example 2: Case creation must succeed; email can fail
Example 3: Data fetch must succeed; notifications can fail
Pattern 2: Conditional Execution
All examples use should_run_expr to handle optional data:
- Example 1: Skip doc creation if validation fails
- Example 2: Skip docket update if doc creation failed
- Example 3: Skip customer notification if no customer email
Pattern 3: Data Flow Chains
Each example builds a data pipeline:
Trigger Data
→ Step 1 validates/enriches
→ Step 2 uses Step 1 outputs
→ Step 3 uses Step 1 + Step 2 outputs
→ etc.
Pattern 4: Correlation for Traceability
All examples:
- Generate correlation ID at start
- Include it in docket custom fields
- Reference it in emails
- Write it to documents
- Log it in ledger
Pattern 5: User-Facing vs Internal Operations
Notice the tone difference:
- Internal (Step 3 in Example 3): Technical, includes system details
- User-Facing (Step 4 in Examples 1, 2, 3): Friendly, hides complexity



Top comments (0)