Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
bb2909f
fix: do not await immediate outbox publish on command completion
patrickleet Aug 22, 2026
d31e0dc
test: wait for spawned immediate outbox publish in sqlite tests
patrickleet Aug 22, 2026
39381af
fix: spawn immediate outbox publish only on a current runtime
patrickleet Aug 22, 2026
658affb
fix: publish outbox through a bounded worker, not spawn-per-commit
patrickleet Aug 22, 2026
669ad74
feat: mount e2e-ui Todo commands from todo-domain
patrickleet Aug 22, 2026
4345e72
feat: mount e2e-ui Chat and Blob commands from domain crates
patrickleet Aug 22, 2026
a0e5d72
fix: harden live Playwright login, admin empty, and chat seed
patrickleet Aug 22, 2026
c6d3dee
feat: add cell host adapter and aggregate cell class
patrickleet Aug 22, 2026
c08191c
feat: parent-shard game cells for Blob and bomberman tick
patrickleet Aug 22, 2026
d8da5e8
test: add celld compose host and TodoCell worker
patrickleet Aug 22, 2026
fc26a3a
test: run local celld compose against Azurite
patrickleet Aug 22, 2026
ca502d0
feat: run Todo AggregateCell as workers-rs wasm on celld
patrickleet Aug 22, 2026
555c59e
feat: persist Todo cell event log in Durable Object SQLite
patrickleet Aug 22, 2026
ebd3836
feat: enable repository snapshots on the celld host
patrickleet Aug 23, 2026
39f089b
feat: public in-process causal invoke with receipt
patrickleet Aug 23, 2026
9c0abb3
feat: HTTP and gRPC causal wait-path with receipt
patrickleet Aug 23, 2026
13a0a07
feat: GraphQL wait-path through CommandHost, not Service
patrickleet Aug 23, 2026
42aef9f
feat: GraphQL CommandHost without Service unwrap
patrickleet Aug 23, 2026
8e39eae
feat: cell sealed row and command-named wait-path HTTP
patrickleet Aug 23, 2026
191d1b1
feat: GraphQL ReadStore SQL scan vs cell GET-by-pk
patrickleet Aug 23, 2026
7f182ce
feat: optional celld+NATS e2e-ui profile
patrickleet Aug 23, 2026
eaceb1a
fix: GraphQL command status test injects CommandHost
patrickleet Aug 23, 2026
4263463
chore: add make up-celld-nats for the optional profile
patrickleet Aug 23, 2026
1ed1d57
fix: make up-celld-nats tolerate an occupied NATS port
patrickleet Aug 23, 2026
028906e
feat: serve GraphQL WS through graphql_router_with_host
patrickleet Aug 23, 2026
40413c9
feat: add e2e-celld GraphQL host wait-dispatching Todo to celld
patrickleet Aug 23, 2026
ab4a1db
docs(e2e-ui): point optional celld profile at sibling example
patrickleet Aug 23, 2026
609564a
feat: add portable_command! for domain command mounts
patrickleet Aug 23, 2026
dad147c
refactor(e2e-celld): move Zitadel auth into an identity service crate
patrickleet Aug 23, 2026
f0e3369
docs: explain celld vs one-process hosts on home and README
patrickleet Aug 23, 2026
0a7193d
feat(e2e-celld): drain ChatCell outbox through MessagePublisher
patrickleet Aug 23, 2026
1ad7b7e
refactor: lift celld CommandHost into the framework
patrickleet Aug 23, 2026
00022a6
feat(e2e-celld): use Postgres for GraphQL read models
patrickleet Aug 23, 2026
da81358
fix(replica): keep SPA nav and Eventual lists from flashing stale SQL
patrickleet Aug 23, 2026
5cb8e64
ci: run celld and e2e-celld tests on PRs and main
patrickleet Aug 23, 2026
ea4b348
fix(ci): unblock js-client, quality, all-features, and chat e2e
patrickleet Aug 24, 2026
9d23358
fix: snapshot tail loads, Eventual projected, and chat Send
patrickleet Aug 24, 2026
05ba89e
fix(js): settle Eventual projected on command-free live frames only
patrickleet Aug 24, 2026
f5e9b9b
fix(replica): keep Atomic membership fences off @load lists
patrickleet Aug 24, 2026
4c47958
fix: keep Eventual projectors live and fill auth_users on scrape
patrickleet Aug 24, 2026
5944acf
fix(celld): make command lifecycle durable and fast
patrickleet Aug 24, 2026
ddf1c76
fix(replica): avoid stale celld revalidation races
patrickleet Aug 24, 2026
5c7dd25
fix(replica): preserve soft-navigation authority
patrickleet Aug 24, 2026
84c1576
fix: settle exact command projections without refetch
patrickleet Aug 24, 2026
84aacc3
fix(celld): harden command and outbox boundaries
patrickleet Aug 24, 2026
06feac3
fix(celld): use wasm-compatible claim clock
patrickleet Aug 24, 2026
e2043e5
test(celld): authenticate optional profile reads
patrickleet Aug 24, 2026
e515082
test(celld): harden local live smoke parity
patrickleet Aug 24, 2026
b654795
refactor(e2e-ui): organize portable commands
patrickleet Aug 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
1 change: 1 addition & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ existing unbounded-tech quality provider plus the integration/* jobs below —
| [`test-all-features.yaml`](./test-all-features.yaml) | yes | **This repo:** workspace `--all-features` |
| [`integration-*.yaml`](./) | yes | **This repo:** broker / DB / CLI / observability / GraphQL identity+OIDC |
| [`integration-e2e-ui.yaml`](./integration-e2e-ui.yaml) | yes | **This repo:** `tests/e2e-ui` offline suite + Playwright browser e2e |
| [`integration-celld.yaml`](./integration-celld.yaml) | yes | **This repo:** `tests/e2e-celld` workspace tests + live Azurite+celld+NATS |
| [`integration-js.yaml`](./integration-js.yaml) | yes | **This repo:** install, typecheck, test, build, and packed-consumer smoke test for `js/` |
| [`on-pr-quality.yaml`](./on-pr-quality.yaml) | entry | **This repo** PR gate (not the consumer quality contract) |
| [`on-push-main-version-and-tag.yaml`](./on-push-main-version-and-tag.yaml) | entry | **This repo** main → **vnext** tag |
Expand Down
265 changes: 265 additions & 0 deletions .github/workflows/integration-celld.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
name: celld (live + e2e-celld)

# Reusable workflow: referenced via `uses: ./.github/workflows/integration-celld.yaml`
# from both the PR-quality and push-to-main pipelines.
#
# Local parity:
# make -C tests/e2e-celld test
# make -C tests/e2e-ui up && make -C tests/e2e-ui up-celld-nats
# WATCH=0 WATCH_WORKER=0 make -C tests/e2e-celld run
# E2E_UI_ORIGIN=http://localhost:5180 npx --prefix tests/e2e-ui playwright test \
# todos.user.spec.ts chat.user.spec.ts --project chromium-user
#
# Default `cargo test` (quality) still runs fixture-only celld checks and
# skips live HTTP unless CELLD_URL is set. This job sets CELLD_URL / NATS_URL.
on:
workflow_call:

env:
CARGO_TERM_COLOR: always
# Zitadel owns :18080 in the browser topology.
CELLD_HTTP_PORT: "18880"
CELLD_URL: http://127.0.0.1:18880
NATS_PORT: "14222"
NATS_URL: nats://127.0.0.1:14222
DISTRIBUTED_INTERNAL_SECRET: test-only-internal-secret-change-me-2026
AZURE_STORAGE_USE_EMULATOR: "true"
AZURE_STORAGE_ACCOUNT_NAME: devstoreaccount1
AZURE_STORAGE_ACCOUNT_KEY: Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==

jobs:
e2e-celld:
name: e2e-celld workspace tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: tests/e2e-celld
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: tests/e2e-celld -> target
shared-key: e2e-celld-workspace
- name: Run e2e-celld workspace tests
run: cargo test --workspace --verbose

live:
name: celld live (Azurite + worker + NATS)
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
persist-credentials: false

- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
with:
toolchain: stable
targets: wasm32-unknown-unknown

- uses: Swatinem/rust-cache@v2
with:
workspaces: |
. -> target
tests/celld/worker -> tests/celld/worker/target
shared-key: celld-live

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: |
js/package-lock.json
tests/e2e-ui/package-lock.json
tests/e2e-ui/ui/package-lock.json

- name: Install host tools
run: sudo apt-get update && sudo apt-get install -y jq openssl curl

- name: Install esbuild
run: npm install -g esbuild

- name: Install wasm-pack and worker-build
run: |
cargo install wasm-pack --locked || cargo install wasm-pack
cargo install worker-build --locked || cargo install worker-build

- name: Install celld CLI
run: |
curl -fsSL https://celld.dev/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Bring up Postgres + Zitadel and bootstrap OIDC
run: make -C tests/e2e-ui up

- name: Bring up Azurite + celld + NATS
run: |
command -v celld
command -v worker-build
make -C tests/e2e-ui up-celld-nats

- name: Live celld HTTP + NATS profile tests
run: make -C tests/e2e-ui test-celld

- name: Build e2e-celld API and UI
run: |
cargo build --manifest-path tests/e2e-celld/Cargo.toml \
-p e2e-celld-runner --bin e2e-celld
make -C tests/e2e-ui ui-install
npm install --prefix tests/e2e-ui

- name: Start e2e-celld API + UI
run: |
set -euo pipefail
set -a
# shellcheck disable=SC1091
. tests/e2e-ui/e2e-ui.env
set +a
export BIND=127.0.0.1:8791
export CELLD_URL=http://127.0.0.1:18880
export NATS_URL=nats://127.0.0.1:14222
export AUTH_URL=http://localhost:5180
export AUTH_USE_SECURE_COOKIES=false
export AUTH_TRUST_HOST=true

tests/e2e-celld/target/debug/e2e-celld \
> tests/e2e-celld/.ci-runner.log 2>&1 &
echo $! > tests/e2e-celld/.ci-runner.pid

ok=0
for i in $(seq 1 120); do
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
"http://127.0.0.1:8791/graphql" \
-H 'content-type: application/json' \
-d '{"query":"{ __typename }"}' 2>/dev/null || echo 000)
if [ "$code" = "200" ] || [ "$code" = "401" ]; then ok=1; break; fi
sleep 0.5
done
if [ "$ok" != "1" ]; then
echo "e2e-celld API failed to become ready"
tail -120 tests/e2e-celld/.ci-runner.log
exit 1
fi

cd tests/e2e-ui/ui
PUBLIC_E2E_PROFILE=celld-nats \
E2E_API_ORIGIN=http://127.0.0.1:8791 \
npm run dev -- --host localhost --port 5180 \
> ../.ci-celld-ui.log 2>&1 &
echo $! > ../.ci-celld-ui.pid
cd ../../..

ok=0
for i in $(seq 1 60); do
code=$(curl -s -o /dev/null -w '%{http_code}' \
"http://localhost:5180/" 2>/dev/null || echo 000)
if [ "$code" = "200" ] || [ "$code" = "302" ] || [ "$code" = "303" ]; then
ok=1
break
fi
sleep 0.5
done
if [ "$ok" != "1" ]; then
echo "e2e-celld UI failed to become ready (last HTTP $code)"
tail -100 tests/e2e-ui/.ci-celld-ui.log
exit 1
fi

- name: Install Playwright + Chromium
working-directory: tests/e2e-ui
run: npx playwright install chromium --with-deps

- name: Red-team private HTTP boundaries
run: |
set -euo pipefail
assert_status() {
expected="$1"
shift
actual=$(curl -sS -o /tmp/celld-red-team-response -w '%{http_code}' "$@")
if [ "$actual" != "$expected" ]; then
echo "expected HTTP $expected, got $actual"
sed -n '1,40p' /tmp/celld-red-team-response
exit 1
fi
}

assert_status 401 -X POST \
http://127.0.0.1:8791/internal/outbox/drain \
-H 'content-type: application/json' \
-d '{"kind":"todo","id":"red-team"}'
assert_status 401 -X POST \
http://127.0.0.1:8791/internal/outbox/drain \
-H 'content-type: application/json' \
-H 'x-distributed-internal-secret: forged-red-team-secret-000000' \
-d '{"kind":"todo","id":"red-team"}'
assert_status 400 -X POST \
http://127.0.0.1:8791/internal/outbox/drain \
-H 'content-type: application/json' \
-H "x-distributed-internal-secret: $DISTRIBUTED_INTERNAL_SECRET" \
-d '{"kind":"todo","id":"red-team","outbox":[]}'
assert_status 401 -X POST \
http://127.0.0.1:8791/zitadel.scrape.v1 \
-H 'content-type: application/json' \
-d '{}'
assert_status 401 \
http://127.0.0.1:18880/todo/red-team
assert_status 401 -X POST \
http://127.0.0.1:18880/todo/red-team/outbox.complete \
-H 'content-type: application/json' \
-d '{"ids":["forged"]}'

- name: Todo + Chat browser lifecycle through celld
working-directory: tests/e2e-ui
run: >-
npx playwright test todos.user.spec.ts chat.user.spec.ts
--project chromium-user
env:
E2E_UI_ORIGIN: http://localhost:5180
E2E_API_ORIGIN: http://127.0.0.1:8791
CI: true

- name: Upload celld Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: celld-playwright-report
path: |
tests/e2e-ui/playwright-report
tests/e2e-ui/test-results
if-no-files-found: ignore
retention-days: 7

- name: Dump logs on failure
if: failure()
run: |
echo '=== celld compose ==='
docker compose -f tests/celld/docker-compose.yml ps -a || true
docker compose -f tests/celld/docker-compose.yml logs --tail=200 || true
echo '=== NATS profile compose ==='
docker compose -f tests/e2e-ui/celld-nats-profile/docker-compose.yml ps -a || true
docker compose -f tests/e2e-ui/celld-nats-profile/docker-compose.yml logs --tail=80 || true
echo '=== e2e-celld API ==='
tail -180 tests/e2e-celld/.ci-runner.log || true
echo '=== e2e-celld UI ==='
tail -100 tests/e2e-ui/.ci-celld-ui.log || true
echo '=== Postgres + Zitadel ==='
docker compose -f tests/e2e-ui/docker/docker-compose.yml ps -a || true
docker compose -f tests/e2e-ui/docker/docker-compose.yml logs --tail=100 || true

- name: Tear down
if: always()
run: |
[ -f tests/e2e-ui/.ci-celld-ui.pid ] && \
kill "$(cat tests/e2e-ui/.ci-celld-ui.pid)" 2>/dev/null || true
[ -f tests/e2e-celld/.ci-runner.pid ] && \
kill "$(cat tests/e2e-celld/.ci-runner.pid)" 2>/dev/null || true
lsof -ti:5180 2>/dev/null | xargs -r kill -9 2>/dev/null || true
lsof -ti:8791 2>/dev/null | xargs -r kill -9 2>/dev/null || true
make -C tests/e2e-ui down-celld-nats || true
make -C tests/e2e-ui down-celld || true
docker compose -f tests/e2e-ui/docker/docker-compose.yml down -v || true
3 changes: 3 additions & 0 deletions .github/workflows/on-pr-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ jobs:
js-client:
needs: [contracts]
uses: ./.github/workflows/integration-js.yaml

celld:
uses: ./.github/workflows/integration-celld.yaml
5 changes: 4 additions & 1 deletion .github/workflows/on-push-main-version-and-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ jobs:
js-client:
uses: ./.github/workflows/integration-js.yaml

celld:
uses: ./.github/workflows/integration-celld.yaml

# This uses commit logs and tags from git to determine the next version number and create a tag for the release.
# Some commits such as chore: will not trigger a version bump and tag; this is by design.
version-and-tag:
name: Version and Tag
needs: [quality, all-features, postgres, nats, rabbitmq, kafka, distributed-cli, observability, graphql, e2e-ui, js-client]
needs: [quality, all-features, postgres, nats, rabbitmq, kafka, distributed-cli, observability, graphql, e2e-ui, js-client, celld]
uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1.22.2
secrets:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ tests/workshop-service/*.db

# fixture crate build artifacts
tests/fixtures/**/target/
tests/celld/worker/target/
tests/celld/worker/build/
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
members = ["distributed_macros", "distributed_cli"]
exclude = ["tests/e2e-ui"]
exclude = ["tests/e2e-ui", "tests/celld/worker"]
resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -58,7 +58,7 @@ base64 = "0.23.0"
futures = { version = "0.3", optional = true }
lapin = { version = "4", optional = true }
rdkafka = { version = "0.39", features = ["cmake-build", "tokio"], optional = true }
reqwest = { version = "0.13", default-features = false, features = ["rustls"], optional = true }
reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"], optional = true }
jsonwebtoken = { version = "9", optional = true }
bitcode = { version = "0.6.9", features = ["serde"] }
event-emitter-rs = { version = "0.1.4", optional = true }
Expand All @@ -79,6 +79,10 @@ tracing = { version = "0.1", optional = true }
tracing-opentelemetry = { version = "0.33", default-features = false, optional = true }
uuid = { version = "1", features = ["v7"] }

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
js-sys = "0.3"
uuid = { version = "1", features = ["v7", "js"] }

[build-dependencies]
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
Expand Down
Loading
Loading