ADR-074: Workflow Tool Architecture (Wf Prefix Convention)

Status: Proposed Date: 2025-12-30 Related: ADR-051 (Application Generator), ADR-054 (AI Tool Architecture), ADR-055 (Tool Ecosystem Review)


Context and Problem Statement

The iDempiere Hub has evolved into a multi-layered tool ecosystem with three AI integration patterns (MCP, LangChain4j, Chat API). As documented in ADR-054 and ADR-055, we have:

  1. Individual Tools (Layer 2) - Single operations like createTable(), generate2Pack()
  2. Implicit Workflows - ADR-051 Application Generator is a workflow but not formalized

Problem: There's no clear architectural distinction between:

This leads to:


Decision Drivers


Decision

Introduce a four-layer tool architecture with explicit "Wf" prefix for workflow tools.

Layer Architecture

┌─────────────────────────────────────────────────────────────────────┐
│  Layer 3: WORKFLOWS (Wf prefix)                                     │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  Orchestration of multiple tools for business-level operations      │
│                                                                     │
│  Examples:                                                          │
│  • WfDeployTable()      - createTable → createWindow → 2Pack → packIn│
│  • WfDeployApplication()- Full app generation (ADR-051)             │
│  • WfMigrateSchema()    - Generate migration → apply → verify       │
│                                                                     │
│  Characteristics:                                                   │
│  • Orchestrates 2+ Layer 2 tools                                    │
│  • Business-level abstraction                                       │
│  • May be stateful (track progress)                                 │
│  • Human-readable goal descriptions                                 │
├─────────────────────────────────────────────────────────────────────┤
│  Layer 2: TOOLS (no prefix)                                         │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  Individual operations exposed via MCP/LangChain4j                  │
│                                                                     │
│  Examples:                                                          │
│  • createTable()        - Create AD table + columns                 │
│  • createWindow()       - Create AD window + tabs + fields          │
│  • generate2Pack()      - Generate 2Pack ZIP                        │
│  • packin2Pack()        - Deploy via REST API (NEW)                 │
│  • generateMigration()  - Generate SQL migration script             │
│                                                                     │
│  Characteristics:                                                   │
│  • Single responsibility                                            │
│  • Stateless                                                        │
│  • Direct service invocation                                        │
├─────────────────────────────────────────────────────────────────────┤
│  Layer 1: SERVICES (business logic)                                 │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  Business logic used by both tools and workflows                    │
│                                                                     │
│  Examples:                                                          │
│  • TableService         - Table CRUD operations                     │
│  • WindowService        - Window CRUD operations                    │
│  • PackOutService       - 2Pack generation logic                    │
│  • MigrationService     - Migration script generation               │
│                                                                     │
│  Characteristics:                                                   │
│  • No AI/MCP awareness                                              │
│  • Reusable by CLI, API, tools, workflows                           │
│  • Transaction-safe                                                 │
├─────────────────────────────────────────────────────────────────────┤
│  Layer 0: DATA                                                      │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  • PostgreSQL (Application Dictionary)                              │
│  • iDempiere REST API                                               │
│  • File System (2Pack, migrations)                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Naming Convention

Layer Prefix Example Class Example Method
Workflow Wf WfDeployToolLogic wfDeployTable()
Tool (none) TableToolLogic createTable()
Service (none) TableService create()

MCP Tool Naming

// Layer 2: Individual Tools (no prefix)
@Tool(description = "Create a table in Application Dictionary")
ToolResponse createTable(...);

@Tool(description = "Generate 2Pack ZIP file")
ToolResponse generate2Pack(...);

@Tool(description = "Deploy 2Pack via REST API PackIn")
ToolResponse packin2Pack(...);

// Layer 3: Workflows (Wf prefix)
@Tool(description = "Deploy complete table: AD entry + window + 2Pack + PackIn")
ToolResponse wfDeployTable(...);

@Tool(description = "Deploy complete application from requirements")
ToolResponse wfDeployApplication(...);

Workflow Implementation Pattern

Class Structure

src/main/java/org/idempiere/cli/
├── ai/shared/                         # Shared logic (Layer 1-3)
│   ├── ToolResult.java                # Unified result
│   ├── ToolGuide.java                 # AI guidance
│   │
│   ├── TableToolLogic.java            # Layer 2: Table tools
│   ├── WindowToolLogic.java           # Layer 2: Window tools
│   ├── PackOutToolLogic.java          # Layer 2: 2Pack tools
│   ├── MigrationToolLogic.java        # Layer 2: Migration tools
│   │
│   └── workflow/                      # Layer 3: Workflows
│       ├── WfDeployToolLogic.java     # Deployment workflows
│       ├── WfMigrateToolLogic.java    # Migration workflows
│       └── WfApplicationToolLogic.java# Application workflows (ADR-051)
│
├── mcp/tools/                         # MCP Server wrappers
│   ├── McpTableTools.java             # Layer 2 exposure
│   ├── McpWindowTools.java            # Layer 2 exposure
│   ├── McpPackOutTools.java           # Layer 2 exposure
│   └── McpWfTools.java                # Layer 3 exposure (Wf* methods)
│
└── services/                          # Layer 1: Business logic
    ├── TableService.java
    ├── WindowService.java
    └── PackOutService.java

Workflow Logic Example

@ApplicationScoped
public class WfDeployToolLogic {

    @Inject TableToolLogic tableLogic;
    @Inject WindowToolLogic windowLogic;
    @Inject PackOutToolLogic packOutLogic;
    @Inject PackInToolLogic packInLogic;  // NEW: REST API deployment

    /**
     * Workflow: Deploy a complete table with window and 2Pack.
     *
     * Steps:
     * 1. Create table in Application Dictionary
     * 2. Create window for the table
     * 3. Generate 2Pack ZIP
     * 4. Deploy via REST API PackIn
     *
     * @param tableName  table name (e.g., "XX_MyTable")
     * @param columns    column definitions
     * @param windowName optional window name
     * @param deploy     if true, deploy to iDempiere via REST API
     * @return workflow result with all step outcomes
     */
    public ToolResult wfDeployTable(String tableName, String columns,
                                     String windowName, boolean deploy) {

        WorkflowContext ctx = new WorkflowContext("wfDeployTable");

        try {
            // Step 1: Create table
            ctx.startStep("createTable");
            ToolResult tableResult = tableLogic.createTable(
                tableName, null, columns, "U", "3", false);
            ctx.completeStep(tableResult);

            if (!tableResult.isSuccess()) {
                return ctx.failed("Table creation failed: " + tableResult.getMessage());
            }

            // Step 2: Create window
            ctx.startStep("createWindow");
            String winName = windowName != null ? windowName : tableName;
            ToolResult windowResult = windowLogic.createWindow(
                tableName, winName, "M", "U", false);
            ctx.completeStep(windowResult);

            if (!windowResult.isSuccess()) {
                return ctx.failed("Window creation failed: " + windowResult.getMessage());
            }

            // Step 3: Generate 2Pack
            ctx.startStep("generate2Pack");
            ToolResult packResult = packOutLogic.generate2Pack(
                "com.cloudempiere." + tableName.toLowerCase(),
                "1.0.0",
                "Auto-generated package for " + tableName,
                tableName,
                winName,
                null, null, null, null);
            ctx.completeStep(packResult);

            if (!packResult.isSuccess()) {
                return ctx.failed("2Pack generation failed: " + packResult.getMessage());
            }

            // Step 4: Deploy via PackIn (optional)
            if (deploy) {
                ctx.startStep("packin2Pack");
                String zipPath = (String) packResult.getData().get("packOut.outputPath");
                ToolResult packInResult = packInLogic.packin2Pack(zipPath);
                ctx.completeStep(packInResult);

                if (!packInResult.isSuccess()) {
                    return ctx.partialSuccess(
                        "Table and 2Pack created, but deployment failed: " + packInResult.getMessage(),
                        ctx.getStepResults());
                }
            }

            return ctx.success(String.format(
                "Deployed table %s with window %s%s",
                tableName,
                winName,
                deploy ? " (imported via PackIn)" : " (2Pack ready for import)"));

        } catch (Exception e) {
            return ctx.failed("Workflow error: " + e.getMessage());
        }
    }
}

MCP Workflow Exposure

@ApplicationScoped
public class McpWfTools {

    @Inject WfDeployToolLogic deployLogic;
    @Inject WfMigrateToolLogic migrateLogic;

    @Tool(description = """
        WORKFLOW: Deploy a complete table with window and 2Pack.
        This is a multi-step workflow that:
        1. Creates the table in Application Dictionary
        2. Creates a window with tabs and fields
        3. Generates a 2Pack ZIP file
        4. Optionally deploys via REST API PackIn
        Use this instead of individual tools when you want the full deployment cycle.
        """)
    ToolResponse wfDeployTable(
            @ToolArg(description = "Table name (e.g., 'XX_MyTable')") String tableName,
            @ToolArg(description = "Column definitions (e.g., 'S#Name,Q#Qty')") String columns,
            @ToolArg(description = "Optional window name") String windowName,
            @ToolArg(description = "If true, deploy to iDempiere via REST API") Boolean deploy) {

        ToolResult result = deployLogic.wfDeployTable(
            tableName, columns, windowName,
            deploy != null ? deploy : false);
        return toToolResponse(result);
    }

    @Tool(description = """
        WORKFLOW: Deploy complete application from requirements.
        This is a comprehensive workflow that:
        1. Analyzes requirements (AI-assisted)
        2. Designs database schema
        3. Creates all tables and columns
        4. Creates windows for each table
        5. Generates business logic (processes, callouts)
        6. Generates complete plugin structure
        7. Generates 2Pack and documentation
        See ADR-051 for full details.
        """)
    ToolResponse wfDeployApplication(
            @ToolArg(description = "Application name") String name,
            @ToolArg(description = "Requirements in markdown or YAML") String requirements,
            @ToolArg(description = "Entity type prefix (e.g., 'CE_WMS')") String entityType) {

        // Delegate to ADR-051 Application Generator
        // ...
    }
}

Workflow vs Tool Decision Matrix

Scenario Use Tool Use Workflow
Create single table createTable() -
Create table with window + deploy - wfDeployTable()
Generate 2Pack only generate2Pack() -
Full application from requirements - wfDeployApplication()
Generate single migration script generateMigration() -
Generate + apply + verify migration - wfMigrateSchema()

AI Agent Guidance

Workflows should include AI guidance in their tool descriptions:

@Tool(description = """
    WORKFLOW: Deploy a complete table to iDempiere.

    WHEN TO USE:
    - User wants to create a "complete" or "full" table
    - User mentions "deploy" or "production-ready"
    - User asks to create table "with window" or "with UI"

    WHEN NOT TO USE:
    - User only wants AD_Table entry (use createTable)
    - User only wants 2Pack file (use generate2Pack)
    - User is debugging/testing (use individual tools)

    STEPS PERFORMED:
    1. createTable() - AD_Table + AD_Column entries
    2. createWindow() - AD_Window + AD_Tab + AD_Field entries
    3. generate2Pack() - Export to portable ZIP
    4. packin2Pack() - Import to target iDempiere (if deploy=true)
    """)
ToolResponse wfDeployTable(...);

Required New Tools

Before implementing workflows, these Layer 2 tools are needed:

1. packin2Pack() - REST API Deployment

@Tool(description = "Deploy a 2Pack ZIP file to iDempiere via REST API PackIn")
ToolResponse packin2Pack(
    @ToolArg(description = "Path to 2Pack ZIP file") String zipPath);

Uses the REST API flow documented in docs/guides/2pack.md:

  1. POST /api/v1/models/AD_Package_Imp - Create PackIn record
  2. POST /api/v1/uploads - Initiate upload
  3. PUT /api/v1/uploads/{id}/chunks/1 - Upload file
  4. POST /api/v1/uploads/{id}/copy - Attach to record
  5. PUT /api/v1/models/AD_Package_Imp/{id} - Set Processing=Y

Consequences

Positive

  1. Clear Abstraction - Obvious distinction between tools and workflows
  2. AI Discoverability - "Wf" prefix signals orchestration to AI agents
  3. Reusability - Tools compose into workflows without duplication
  4. Consistency - Uniform pattern across MCP, LangChain4j, Chat API
  5. ADR-051 Alignment - Formalizes the Application Generator pattern

Negative

  1. Naming Discipline - Team must follow convention consistently
  2. Overhead - Workflows add orchestration complexity
  3. Testing - Workflows require integration tests, not just unit tests

Mitigation


Implementation Roadmap

Phase 1: Foundation (This ADR)

Phase 2: packin2Pack Tool

Phase 3: First Workflow

Phase 4: Application Generator Integration


References


Document Status: Proposed Next Step: Review and approve, then implement Phase 2 (packin2Pack)

Path: /docs/developers/architecture/idempiere-hub/074-workflow-tool-architecture