Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
ed1e672
minor improvements all around
mping Dec 9, 2025
f928d2b
use poller, fix threading
mping Dec 9, 2025
0d38aea
bump fdb
mping Dec 10, 2025
7772c2a
fix warn
mping Dec 11, 2025
e95d08d
fix shutdown sequence
mping Dec 16, 2025
52a7f38
use separate thread
mping Dec 18, 2025
6f1627d
implement basic update-if for task attrs, fix tests
mping Dec 19, 2025
5cc6dbf
fix earthly
mping Dec 20, 2025
91ec50c
fix test
mping Dec 20, 2025
d556b99
experiment with docker
mping Dec 21, 2025
0c58cb5
fixup! experiment with docker
mping Dec 21, 2025
1f3abab
fixup! fixup! experiment with docker
mping Dec 21, 2025
b119947
fixup! fixup! fixup! experiment with docker
mping Dec 21, 2025
be36d00
fixup! fixup! fixup! fixup! experiment with docker
mping Dec 21, 2025
6f3835a
fixup! fixup! fixup! fixup! fixup! experiment with docker
mping Dec 21, 2025
c2fd000
fixup! fixup! fixup! fixup! fixup! fixup! experiment with docker
mping Dec 21, 2025
7d00a23
fixup! fixup! fixup! fixup! fixup! fixup! fixup! experiment with docker
mping Dec 21, 2025
13fe529
start 10 workfows
mping Dec 21, 2025
ab332c0
restore earthly
mping Dec 21, 2025
67cd89b
bump fdb to 7.3.62
mping Dec 22, 2025
e29f8bf
use one task per activity due to error recovery
mping Dec 22, 2025
9cc624e
fix bad ex state
mping Dec 23, 2025
7dae1b3
replace with phaser
mping Dec 23, 2025
963f357
add hikari
mping Dec 24, 2025
4eb386f
log info
mping Dec 24, 2025
db7f44c
add basic test
mping Dec 24, 2025
2c6e2da
intemporal next
mping Jan 12, 2026
71b8ee4
checkp
mping Jan 13, 2026
47a9830
refactor ns
mping Jan 13, 2026
ec9b8e4
split examples
mping Jan 14, 2026
51a056c
new id
mping Jan 15, 2026
ea80c59
refactor
mping Jan 15, 2026
c1c1074
continue store/ctx refactor
mping Jan 15, 2026
659e9d9
fix cancellation
mping Jan 16, 2026
1df5b6c
refactor
mping Jan 16, 2026
fb6ea64
small improvement
mping Jan 16, 2026
b2d575d
add old src
mping Jan 16, 2026
1331c6d
small fix
mping Jan 16, 2026
c8b7a9c
refactor
mping Jan 16, 2026
02c1c34
add test
mping Jan 16, 2026
222c975
dont throw
mping Jan 17, 2026
86a4ff5
tests passing
mping Jan 17, 2026
ab201eb
lint
mping Jan 17, 2026
5aff242
add runonce facility
mping Jan 17, 2026
8253987
tidy
mping Jan 17, 2026
36588c4
add race
mping Jan 21, 2026
e60e526
dont need to register activity
mping Jan 21, 2026
79a00de
lint
mping Jan 21, 2026
0de5b14
Add tracing test
mping Jan 21, 2026
108c325
cleanup
mping Jan 21, 2026
775e93a
remove comments
mping Jan 21, 2026
5c53467
add logging
mping Jan 22, 2026
5c5e29e
tidy up logging
mping Jan 22, 2026
e0c8922
add test
mping Jan 24, 2026
f5c20ff
fixup! add test
mping Feb 9, 2026
11df14f
add tests
mping Feb 9, 2026
47796e1
next-js, still promisifying
mping Feb 10, 2026
990a272
progress a bit more
mping Feb 10, 2026
5146a36
add cljs todo
mping Feb 11, 2026
df1c9ae
cljs
mping Feb 11, 2026
fcf4347
bunch of cljs
mping Feb 13, 2026
71a434b
fx logging, macro, use telemere
mping Feb 13, 2026
5e023fd
fix leak
mping Feb 13, 2026
0c26644
support protos
mping Feb 14, 2026
c9adb65
fdb/pg
mping Feb 14, 2026
0b93a73
cleanup lint, implement jdbc
mping Feb 14, 2026
de58ce4
fix failing test
mping Feb 14, 2026
011cbcc
fix tests
mping Feb 15, 2026
7ab5acb
context prop
mping Feb 15, 2026
efd6d0b
macros cljs only, fix test
mping Feb 15, 2026
ba79e41
fix ns
mping Feb 15, 2026
0eb6be1
fix example
mping Feb 15, 2026
3bf9b4d
add migraitons
mping Feb 15, 2026
befa553
fix logging
mping Feb 15, 2026
f351063
fix bad macro
mping Feb 15, 2026
e6af5b6
fix bad macro
mping Feb 15, 2026
ef2e64a
update docs
mping Feb 23, 2026
9b3d0bf
Fix earthly
mping Feb 23, 2026
4d54383
add missing folder
mping Feb 23, 2026
9380ca5
run quiet
mping Feb 24, 2026
51f6e89
fixup! run quiet
mping Feb 24, 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
9 changes: 8 additions & 1 deletion .clj-kondo/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
intemporal.macros/env-let clojure.core/let
intemporal.macros/with-failure clojure.core/let
intemporal.store.foundationdb/with-tx clojure.core/with-open
intemporal.test-utils/with-result clojure.core/let
intemporal.tests.utils/with-result clojure.core/let

intemporal.internal.context/blet clojure.core/let
intemporal.tests.utils/bthen promesa.core/then
intemporal.tests.utils/finally promesa.core/finally

intemporal.core/with-workflow-engine clojure.core/let

promesa.core/let clojure.core/let
promesa.core/loop clojure.core/loop
promesa.core/recur clojure.core/recur}
Expand Down
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JAVA_OPTS="$JAVA_OPTS -Dio.netty.tryUnsafe=false --enable-native-access=ALL-UNNAMED "
11 changes: 6 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ concurrency:

jobs:
earthly:
if: true
name: Earthly ci
runs-on: ubuntu-latest
permissions: write-all
Expand All @@ -27,7 +28,7 @@ jobs:
- name: Check earthly version
run: earthly --version
- name: Run tests with earthly
run: earthly -P +test --ci --remote-cache=ghcr.io/mping/intemporal:ci-cache
run: earthly -P +test --ci --quiet --remote-cache=ghcr.io/mping/intemporal:ci-cache
- name: Setup LCOV
uses: hrishikesh-kadam/setup-lcov@v1
- uses: kcjpop/coverage-comments@v2.2
Expand Down Expand Up @@ -73,10 +74,10 @@ jobs:

- name: Setup FDB
run:
curl -L "https://github.com/apple/foundationdb/releases/download/7.1.31/foundationdb-clients_7.1.31-1_amd64.deb" --output /tmp/foundationdb-clients_7.1.31-1_amd64.deb --fail;
sudo dpkg -i /tmp/foundationdb-clients_7.1.31-1_amd64.deb;
sudo rm -f /tmp/foundationdb-clients_7.1.31-1_amd64.deb;
sudo curl --fail -L "https://github.com/apple/foundationdb/releases/download/7.1.31/libfdb_c.x86_64.so" --output "/usr/lib/libfdb_c.7.1.31.x86_64.so";
curl -L "https://github.com/apple/foundationdb/releases/download/7.3.57/foundationdb-clients_7.3.57-1_amd64.deb" --output /tmp/foundationdb-clients_7.3.57-1_amd64.deb --fail;
sudo dpkg -i /tmp/foundationdb-clients_7.3.57-1_amd64.deb;
sudo rm -f /tmp/foundationdb-clients_7.3.57-1_amd64.deb;
sudo curl --fail -L "https://github.com/apple/foundationdb/releases/download/7.3.57/libfdb_c.x86_64.so" --output "/usr/lib/libfdb_c.7.3.57.x86_64.so";

- name: Setup docker compose
id: compose
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ coverage/
.calva/
.venv/
memory-bank/
.claude

src2/
test2/
235 changes: 235 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**intemporal** is a Clojure/ClojureScript library inspired by Temporal.io and Uber Cadence. It enables defining functions with side effects that can persist and resume their state, providing resilience to process crashes.

### Core Concepts

- **Activities**: Protocol implementations or functions that handle side effects. Activities are the unit of work that can fail and be retried.
- **Workflows**: Functions that orchestrate activities with at-least-once semantics. Workflows can safely resume after crashes by replaying from persisted event history.
- **Event Sourcing**: Workflow state is reconstructed from an event log stored via the `IStore` protocol.
- **Replay**: On resume, workflows replay their event history to reconstruct state without re-executing activities.

### Architecture

The codebase is organized into several layers:

1. **Core API** ([src/intemporal/core.cljc](src/intemporal/core.cljc))
- `stub` - Creates activity stubs for use in workflows
- `stub-protocol` - Creates protocol stubs for use in workflows
- `start-workflow`, `resume-workflow` - Workflow execution entry points
- `make-workflow-engine`, `with-workflow-engine` - Engine lifecycle
- `wait-for-signal`, `send-signal`, `sleep`, `async`, `join` - Workflow operations
- `run-child-workflow`, `cancel-workflow` - Child workflows and cancellation

2. **Protocol Definitions** ([src/intemporal/protocol.cljc](src/intemporal/protocol.cljc))
- `IStore` - Workflow persistence (history, signals, cancellation)
- `IActivityExecutor` - Activity execution with timeout/retry
- `IScheduler` - Timer scheduling
- `IWorkflowObserver` - Event observation for monitoring/tracing

3. **Internal Components** (src/intemporal/internal/)
- `context.cljc` - Dynamic workflow context with sequence counters, pending events
- `execution.clj` / `execution.cljs` - Workflow execution engine (platform-specific)
- `runtime.clj` / `runtime.cljs` - Default implementations of `IActivityExecutor` and `IScheduler` (platform-specific)
- `activity.cljc` - Activity registration and metadata
- `error.cljc` - Error types (suspensions, interruptions, rejections, cancellations)
- `logging.cljc` - Structured logging via taoensso/telemere
- `macros.cljc` - `stub-protocol` macro
- `fns/start_workflow.clj` / `fns/start_workflow.cljs` - Workflow start logic (platform-specific)

4. **Store Implementations** ([src/intemporal/store.cljc](src/intemporal/store.cljc))
- `InMemoryStore` - In-memory implementation of `IStore`
- Additional stores: FoundationDB (`store/fdb.clj`, `:fdb` alias), JDBC (`store/jdbc.clj`, `:jdbc` alias)

5. **Observer** ([src/intemporal/observer.cljc](src/intemporal/observer.cljc))
- `noop-observer`, `make-logging-observer` - Observer factories
- `observer/otel.clj` - OpenTelemetry observer implementation

### Key Mechanisms

- **Sequence Numbers**: Each activity/operation gets a monotonic sequence number for deterministic replay
- **Suspensions**: Workflows can suspend (e.g., waiting for signal, timer) and resume later
- **Cancellation**: Workflows check cancellation status at each sequence point
- **Pending Events**: During workflow execution, events are buffered and atomically saved to store

## Development Commands

### Grep

Ensure you run grep with `--color=never`

```
grep --color=never
```

### Running Tests

```bash
# Run all tests (includes JVM and ClojureScript tests)
bin/kaocha

# Run specific test suite
bin/kaocha :test # JVM tests only
bin/kaocha :in-memory # In-memory tests (skips :integration)
bin/kaocha :test-cljs # ClojureScript tests

# Run a single test namespace (note: use hyphens, not underscores)
bin/kaocha :test --focus intemporal.tests.signal-test

# Run crash recovery tests
bin/kaocha :test --focus intemporal.tests.crash.signal-wait-crash-test
bin/kaocha :test --focus intemporal.tests.crash.future-cancel-test

# Run tests via npx
npx shadow-cljs compile node

# Focus cljs tests
bin/kaocha :test-cljs --focus cljs:intemporal.tests.crash.future-cancel-test

```

**Important**: Test namespaces use hyphens (e.g., `signal-wait-crash-test`), which map to underscored file names (`signal_wait_crash_test.clj`).

### Test Configuration

Test configuration is in [tests.edn](tests.edn):
- Line 37: `:kaocha.filter/skip-meta [:crash]` can be uncommented to skip crash tests
- Coverage reports: `target/coverage/index.html`
- JUnit XML reports: `target/test-reports/report.xml`

### REPL Development

```bash
# Start REPL with development dependencies
clojure -A:dev

# With FoundationDB support
clojure -A:dev:fdb

# With JDBC/PostgreSQL support
clojure -A:dev:jdbc
```

### Building

```bash
# Compile main namespaces
clojure -T:build compile-main

# Compile development namespaces
clojure -T:build compile-dev

# Build JAR
clojure -T:build jar
```

### Debug Configuration

The test runner ([bin/kaocha](bin/kaocha)) has a commented-out debug agent on port 5005 that can be enabled:
```
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
```

### OpenTelemetry Tracing

The project includes OpenTelemetry instrumentation (see [deps.edn](deps.edn) `:dev` alias, `_jvm-opts`):
- Java agent: `./opentelemetry-javaagent.jar`
- OTLP endpoint: `http://localhost:4317` (gRPC)
- Service name: `intemporal`
- Metrics and logs are disabled by default
- Note: The OTel JVM opts use the `_jvm-opts` key (underscore prefix), so they are **not active by default** - they must be manually enabled

## Code Patterns

### Defining a Workflow

Workflows are regular functions that use `stub` to wrap activity calls:

```clojure
(require '[intemporal.core :as intemporal])

(defn my-activity [arg]
[:processed arg])

(defn my-workflow [arg]
(let [act (intemporal/stub #'my-activity)]
(act arg)))
```

### Activities

Activities can be:
- Regular functions (via var): `(intemporal/stub #'my-function)`
- Protocol methods: `(intemporal/stub-protocol MyProtocol)`

### Running Workflows

```clojure
(require '[intemporal.core :as intemporal])

;; Using with-workflow-engine (ensures cleanup)
(intemporal/with-workflow-engine [engine {:threads 4}]
(intemporal/start-workflow engine my-workflow [arg]))

;; Or manually managing the engine
(let [engine (intemporal/make-workflow-engine :threads 4)]
(try
(intemporal/start-workflow engine my-workflow [arg])
(finally
(intemporal/shutdown-engine engine))))
```

## Testing Guidelines

### Test Organization

- [test/intemporal/tests/](test/intemporal/tests/) - Main test directory
- `async_test.clj` / `.cljs` - Async operation tests
- `cancellation_test.clj` / `.cljs` - Workflow cancellation
- `child_workflow_test.clj` / `.cljs` - Nested workflow tests
- `error_test.clj` / `.cljs` - Error handling and retry policies
- `signal_test.clj` / `.cljs` - Signal send/receive
- `timer_test.clj` / `.cljs` - Timer scheduling
- `tracing_test.clj` - OpenTelemetry tracing (JVM only)
- `protocol_test.clj` - Protocol tests
- `replay_check_test.clj` - Replay verification
- `stub_protocol_test.cljc` - Protocol stubbing tests
- `context_macros_test.cljs` - Context macros (ClojureScript only)
- `utils.cljc` - Test utilities
- [store/](test/intemporal/tests/store/) - Store-specific tests
- [crash/](test/intemporal/tests/crash/) - Crash recovery scenarios

### Crash Recovery Tests

Tests in `test/intemporal/tests/crash/` verify workflow resilience:
- Execute workflow until suspension point
- Simulate crash (exception)
- Resume workflow and verify activities aren't re-executed
- Check workflow completes with correct result

## Dependencies

### Required Dependencies (all environments)
- Clojure 1.12.1+
- taoensso/telemere (structured logging)
- clj-otel-api (OpenTelemetry tracing)
- macrovich (cross-platform macros)
- promesa (promises, required for ClojureScript)
- cheshire (JSON)
- shadow-cljs

### Optional Dependencies (via aliases)
- `:fdb` - FoundationDB client
- `:jdbc` - PostgreSQL/JDBC persistence (next.jdbc, PostgreSQL, HikariCP, migratus)
- `:cljs` - ClojureScript support
- `:dev` - Testing libraries (Kaocha, kaocha-cloverage, kaocha-junit-xml, kaocha-cljs, logback, spy, matcher-combinators, clj-async-profiler)

## Important Notes

- **Determinism**: Workflows must be deterministic for replay to work correctly. Use activities for any non-deterministic operations (random numbers, time, I/O).
- **Namespace Conventions**: File names use underscores (`signal_test.clj`), namespace names use hyphens (`signal-test`).
- **Production Readiness**: Per README, this library is NOT production-ready. Use at your own risk.
19 changes: 14 additions & 5 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ clj -A:dev:doc:cljs
# Tests

```shell
bin/kaocha test
bin/kaocha test-cljs
bin/kaocha :test
bin/kaocha :test-cljs

# or run everything
bin/run-coverage

# focusing
./bin/kaocha test --focus intemporal.shutdown-restart-test
./bin/kaocha :test --focus intemporal.tests.signal-test

# cljs focus is a bit different
./bin/kaocha test-cljs --focus 'cljs:intemporal.shutdown-restart-test'
./bin/kaocha :test-cljs --focus 'cljs:intemporal.tests.signal-test'

```

Expand All @@ -70,4 +70,13 @@ $ JAVA_OPTS="-DFDB_LIBRARY_PATH_FDB_C=/usr/local/lib/libfdb_c.dylib -DFDB_LIBRAR
(.invoke method com.apple.foundationdb.JNIUtil (object-array ["fdb_java"]))
(.invoke method com.apple.foundationdb.JNIUtil (object-array ["fdb_c"])))

```
```

# Telemetry

# Get the OT javaagent

```shell
wget --content-disposition https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.21.0/opentelemetry-javaagent.jar
```
Run with the `dev` profile to activate the java agent.
16 changes: 5 additions & 11 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@
CACHE ~/.m2
RUN clj -Stree
RUN npm install
#RUN wget https://github.com/apple/foundationdb/releases/download/7.3.63/foundationdb-clients_7.3.63-1_aarch64.deb
#RUN dpkg -i foundationdb-clients_7.3.63-1_aarch64.deb
RUN wget -q https://github.com/apple/foundationdb/releases/download/7.1.31/foundationdb-clients_7.1.31-1_amd64.deb
RUN dpkg -i foundationdb-clients_7.1.31-1_amd64.deb
RUN wget -nv https://github.com/apple/foundationdb/releases/download/7.3.57/foundationdb-clients_7.3.57-1_amd64.deb
RUN dpkg -i foundationdb-clients_7.3.57-1_amd64.deb
RUN echo "docker:docker@127.0.0.1:4500" > /etc/foundationdb/fdb.cluster
RUN wget -nv --content-disposition https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

build-base:
FROM +deps
# copy src here cause we want to maximize chance to cache deps
COPY --dir .clj-kondo build bin dev src doc test resources /build
COPY --dir .clj-kondo build bin doc src test resources /build

lint:
FROM +build-base
Expand All @@ -43,11 +41,8 @@
build-main:
FROM +build-base
RUN clj -T:build compile-main
build-dev:
FROM +build-main
RUN clj -T:build compile-dev
build-jar:
FROM +build-dev
FROM +build-main
RUN clj -T:build jar
build-cljs:
FROM +build-base
Expand All @@ -56,7 +51,6 @@
build-all:
BUILD +lint
BUILD +build-main
BUILD +build-dev
BUILD +build-jar
BUILD +build-cljs

Expand All @@ -66,7 +60,7 @@
COPY docker ./docker
COPY docker-compose.yaml ./
WITH DOCKER --compose docker-compose.yaml
RUN bin/run-coverage

Check failure on line 63 in Earthfile

View workflow job for this annotation

GitHub Actions / Earthly ci

Error

The command WITH DOCKER RUN --privileged bin/run-coverage did not complete successfully. Exit code 1
END
SAVE ARTIFACT ./coverage AS LOCAL ./coverage
SAVE ARTIFACT ./target AS LOCAL ./target
Expand Down
Loading
Loading