Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
39e7ff7
Implement basic Ballerina workflow module native wrapper.
hasithaa Jan 29, 2026
aec8bc0
Implement workflow compiler plugin with code analysis and modificatio…
hasithaa Jan 29, 2026
0dd4dd5
Add validation tests for process function parameters and return types
hasithaa Jan 29, 2026
8e254d0
Refactor error messages for process function parameters and enhance a…
hasithaa Jan 30, 2026
5554b2b
Add validation tests for process function parameters and return types
hasithaa Jan 29, 2026
22d3285
Refactor error messages for process function parameters and enhance a…
hasithaa Jan 30, 2026
cb32420
Merge branch 'v2_task1' of https://github.com/hasithaa/fork-module-ba…
hasithaa Jan 30, 2026
b5380e6
Add event handling support to workflow processes and enhance registry…
hasithaa Jan 30, 2026
1e393a6
Add Temporal and gRPC dependencies with version management in build c…
hasithaa Jan 30, 2026
6f640bc
Add Temporal SDK integrtion
hasithaa Jan 31, 2026
71bad7c
Add validation for activity function calls in workflow processes
hasithaa Jan 31, 2026
db871d5
Add integration tests for Ballerina workflows with Temporal
hasithaa Jan 31, 2026
f2feb86
Refactor test server management and update configuration files for in…
hasithaa Jan 31, 2026
3555153
Use dependently return type in CallActivity
hasithaa Jan 31, 2026
cd9b999
Implement SignalFuture and TemporalFutureValue for workflow signals
hasithaa Feb 1, 2026
4287596
Merge remote-tracking branch 'ballerina/v2' into v2_runtime
hasithaa Feb 1, 2026
5d3dfc5
Fix code review suggestion
hasithaa Feb 1, 2026
2fcea93
Refactor return type validation in WorkflowValidatorTask to use Workf…
hasithaa Feb 1, 2026
8a8664c
Add OS-specific command handling for git commit and bal push tasks
hasithaa Feb 1, 2026
052c370
Update Ballerina configuration for GraalVM compatibility and add nati…
hasithaa Feb 1, 2026
3a4742f
Add dependencies for Java compilation and resource processing in ball…
hasithaa Feb 1, 2026
30eca5f
Implement correlation key extraction and registration for Temporal wo…
hasithaa Feb 1, 2026
bd21f7a
Update workflow input and signal types to require readonly fields for…
hasithaa Feb 1, 2026
9543ae4
Add tasks for publishing workflow module and building samples; update…
hasithaa Feb 1, 2026
b911ad0
Add Samples
hasithaa Feb 2, 2026
b27a261
Implement duplicate workflow detection and handling; add tests and up…
hasithaa Feb 2, 2026
85e9825
Updating copilt instruction
hasithaa Feb 2, 2026
365bb9f
Rename startProcess to createInstance in workflow functions and updat…
hasithaa Feb 2, 2026
b46b8b6
Rename startProcess to createInstance across workflow functions and u…
hasithaa Feb 2, 2026
fdb9dea
Refactor workflow context and signal handling
hasithaa Feb 2, 2026
1589539
Implement no-arg activity support in workflows; add tests for single …
hasithaa Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@
# See: https://help.github.com/articles/about-codeowners/

# These owners will be the default owners for everything in the repo.
* @hasithaa
* @rdulmina
* @hasithaa @rdulmina
183 changes: 183 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Ballerina Workflow Module - AI Coding Instructions

## Project Overview
A Ballerina standard library module providing durable workflow orchestration via Temporal SDK. The module uses a **singleton worker pattern** with a compiler plugin that transforms annotated functions.

## Architecture

### Module Structure
- `ballerina/` - Core Ballerina module (types, annotations, context, public API)
- `native/` - Java native implementation (Temporal SDK integration, worker management)
- `native-test/` - Embedded Temporal test server for integration tests
- `compiler-plugin/` - Validates `@Process`/`@Activity` annotations, transforms activity calls
- `compiler-plugin-tests/` - Compiler plugin test suite

### Key Design Patterns

**Dynamic Workflow/Activity Adapters**: All workflows route through `BallerinaWorkflowAdapter` (implements `DynamicWorkflow`), all activities through `BallerinaActivityAdapter` (implements `DynamicActivity`). See [WorkflowWorkerNative.java](native/src/main/java/io/ballerina/stdlib/workflow/worker/WorkflowWorkerNative.java).

**Singleton Worker**: One Temporal SDK instance per JVM, initialized at module load via configurable variables. No Listener pattern - use `registerProcess()` + `startWorker()`.

**Context Client Class**: `workflow:Context` is a client class with `callActivity` as a remote method. Users **must** call activities via `ctx->callActivity(activityFunc, args...)`. Direct activity function calls are not allowed.

**Compiler Plugin Validation**: The plugin at [WorkflowCompilerPlugin.java](compiler-plugin/src/main/java/io/ballerina/stdlib/workflow/compiler/WorkflowCompilerPlugin.java) performs validation:
1. Validates that `ctx->callActivity()` calls use functions with `@Activity` annotation (produces `WORKFLOW_107` error otherwise)
2. Validates that `@Activity` functions are not called directly inside `@Process` functions (produces `WORKFLOW_108` error)
3. Auto-generates `registerProcess()` call at module level for each `@Process` function

## Key Conventions

### Process Function Signature
```ballerina
@workflow:Process
function processName(
workflow:Context ctx, // Required for calling activities
T input, // Input data (anydata subtype)
record { future<U> event1; future<V> event2; } events // Optional event futures
) returns R|error { }
```

- **Context** (`workflow:Context`): Client class as first parameter. **Required** if calling activities. Provides `callActivity` remote method.
- **Input**: Workflow input data. If process has events, must have `readonly` fields for correlation (e.g., `readonly string customerId`)
- **Events**: Optional record with `future<T>` fields for receiving signals. Wait using `check events.event1`. **Requires readonly fields in input for correlation.**

### Activity Functions
```ballerina
@workflow:Activity
function sendEmailActivity(string email) returns boolean|error {
// I/O operations, external API calls, database access
}
```
- Parameters and return types must be `anydata` subtypes
- Activities are non-deterministic - executed once, results cached during replay
- **Must** be called via `ctx->callActivity()` within `@Process` functions (direct calls produce `WORKFLOW_108` error)

### Calling Activities
Activities **must** be called using `ctx->callActivity()` within `@Process` functions:

```ballerina
@workflow:Process
function orderProcess(workflow:Context ctx, OrderInput input) returns OrderResult|error {
// Call activity using Context client remote method
string result = check ctx->callActivity(sendEmailActivity, input.email);
return {status: result};
}
```

**Important**: Direct activity function calls are **not allowed** and will produce a compiler error:
```ballerina
// ERROR: WORKFLOW_108 - Direct calls to @Activity functions are not allowed
string result = check sendEmailActivity(input.email); // ❌ Not allowed
```

The compiler plugin validates:
1. All `ctx->callActivity()` calls use functions with `@Activity` annotation
2. No direct calls to `@Activity` functions inside `@Process` functions

### Error Handling
Temporal errors are valid Ballerina errors. Use standard error handling:
```ballerina
@workflow:Process
function processWithErrorHandling(workflow:Context ctx, Input input) returns Output|error {
Output|error result = ctx->callActivity(riskyActivity, input.data);
if result is error {
// Handle activity failure - workflow can retry, compensate, or fail
return error("Activity failed", result);
}
return result;
}
```

### Type Mappings (Ballerina ↔ Java ↔ Temporal)
- `map<anydata>` → `BMap<BString, Object>` (use `PredefinedTypes.TYPE_ANYDATA`)
- `function` → `BFunctionPointer` / `FPValue` (set `StrandMetadata` before calling)
- Workflow inputs with events must have `readonly` fields for correlation

### Native Code Patterns
When calling Ballerina methods from Java:
```java
Object result = ballerinaRuntime.callMethod(
serviceObject, "execute",
new StrandMetadata(true, Collections.emptyMap()), args);
```

For activity functions use `FPValue`:
```java
FPValue fpValue = (FPValue) activityFunction;
fpValue.metadata = new StrandMetadata(true, fpValue.metadata.properties());
```

## Build & Test Commands

```bash
# Full build
./gradlew build

# Run unit tests only (no Temporal server needed)
./gradlew :workflow-ballerina:test

# Run integration tests (starts embedded Temporal server on port 7233)
./gradlew :workflow-integration-tests:test

# Run compiler plugin tests
./gradlew :workflow-compiler-plugin-tests:test

# Build specific modules
./gradlew :workflow-native:build
./gradlew :workflow-compiler-plugin:build

# Update Ballerina.toml with version placeholders
./gradlew :workflow-ballerina:updateTomlFiles
```

### Test Infrastructure

**Module Structure:**
- `ballerina/tests/` - Unit tests (registration, introspection, no Temporal server needed)
- `integration-tests/tests/` - Integration tests with actual Temporal workflow execution
- `compiler-plugin-tests/` - Compiler plugin validation tests

**Integration Tests** use an embedded Temporal server managed by Gradle:
1. `startTestServer` task launches shadow JAR from `native-test/` on port **7233**
2. Writes `tests/Config.toml` with server URL
3. Ballerina tests connect via configurable
4. `stopTestServer` runs on completion (even on failure via `buildFinished` listener)

## Configuration

Via `Config.toml` or programmatic defaults:
```toml
[ballerina.workflow.workflowConfig]
provider = "TEMPORAL"
url = "localhost:7233"
namespace = "default"

[ballerina.workflow.workflowConfig.params]
taskQueue = "BALLERINA_WORKFLOW_TASK_QUEUE"
maxConcurrentWorkflows = 100
```

## Version Requirements
- **Ballerina**: 2201.13.0
- **Java**: 21
- **Temporal SDK**: 1.32.0 (with matching gRPC 1.58.1)

## Compiler Plugin Error Codes

| Code | Error | Cause |
|------|-------|-------|
| WORKFLOW_107 | callActivity target not @Activity | Calling non-activity via ctx->callActivity() |
| WORKFLOW_108 | Direct activity call | Direct call to @Activity function (must use ctx->callActivity()) |
| WORKFLOW_112 | Ambiguous signal types | Multiple signals with same structure, need explicit signalName |
| WORKFLOW_113 | Input not record type | Process input must be record type for correlation |
| WORKFLOW_114 | Missing correlation key | Signal missing readonly field present in input |
| WORKFLOW_115 | Correlation type mismatch | Readonly field type differs between input and signal |
| WORKFLOW_116 | Events need correlation | Process with events lacks readonly fields in input |

## Common Pitfalls
- Always clear registry in test `@BeforeEach` with `clearRegistry()` to avoid state leakage
- Process functions must be deterministic - no I/O, use activities instead
- Workflows with events (signals) must have `readonly` fields in input for correlation
- Don't mix Listener pattern (deprecated) with singleton pattern
- **Never use Java blocking calls** in workflow code (causes `PotentialDeadlockException`)
- Signal waiting uses `TemporalFutureValue.getAndSetWaited()` to intercept Ballerina's `wait` and use `Workflow.await()` instead of blocking `CompletableFuture.get()`
Loading
Loading