Skip to content

Add Kruxia Flow to the bake-off (infrastructure + DAG 3 verified) - #1

Draft
deangoodmanson wants to merge 6 commits into
soapergem:mainfrom
deangoodmanson:kruxiaflow
Draft

Add Kruxia Flow to the bake-off (infrastructure + DAG 3 verified)#1
deangoodmanson wants to merge 6 commits into
soapergem:mainfrom
deangoodmanson:kruxiaflow

Conversation

@deangoodmanson

@deangoodmanson deangoodmanson commented Aug 15, 2026

Copy link
Copy Markdown

Adds Kruxia Flow 0.8.3 — a Rust durable-execution engine, one binary plus PostgreSQL — as a thirteenth tool, evaluated head-to-head against Temporal (the category comparison, since Kruxia Flow's own README claims that category) and Conductor (the mechanism comparison, since architecturally they are near-twins). The other eleven tools are not re-run.

Please read this first

Only DAG 3 is verified. DAG 2 cannot be implemented at all and DAG 4 is written but has never run, because wait_for_signal — the engine's suspend/resume primitive — cannot complete on 0.8.3: a signalled activity always fails. Root cause is pinned to orchestrator.rs:1522-1535 and filed upstream.

Testing was stopped there rather than re-implementing DAG 2 and DAG 4 as polling workarounds. That would have measured a workaround the tool does not advertise and converted its best rubric category into its worst on the basis of a bug that is a few lines from fixed. The reasoning is recorded in kruxiaflow_bakeoff/README.md so the decision can be disagreed with.

Treat dag4_order_fulfillment.yaml and subflows/ as a design record, not as verified code — they deploy cleanly and show what composition and saga compensation have to look like in a tool with no sub-workflow construct and no OR-join, but on this repo's own evidence four unrun workflow definitions contain several defects.

comparison.md is deliberately untouched. Three of four DAGs are unverified, so a full thirteenth column would imply a completeness this evaluation does not have. The provisional score lives in kruxiaflow-comparison.md instead: 49 as-is, 55 if the blocker is fixed — the two columns reflect a real choice about how to score a defect, and both are explained.

What is verified

DAG 3 (payment), all six spec assertions, evidence from the engine's own workflow_events table:

Case Result
Happy path completed in ~2s
Non-retriable decline 1 attempt, retryable=false
Retriable 5xx exactly 5 attempts, 48.6s = 3+6+12+24
Validation rejection branch taken, gateway skipped
Duplicate idempotency key no second debit, gateway never called
Graceful degradation forced notification failure → still completed

Zero authoring defects in the DAG itself — it worked as written on the first run. Worth noting alongside Temporal's 0 and Kestra's 18 in the Implementation Friction table.

The finding worth putting in the deck: all of DAG 3's database work is declarative. Validation is a single postgres_query whose CASE expression does all five checks the other implementations do as five Python round-trips, and the money movement is one idempotent CTE inside a serializable transaction. No other YAML-defined tool in this bake-off can do that without a custom worker.

Two commits

1d353c6 is independent of Kruxia Flow and worth landing regardlessshared-services/init-runners.sh was mode 644, and under Docker Desktop the Postgres entrypoint execs a bind-mounted .sh rather than sourcing it, so it died with bad interpreter: Permission denied. The result is a fresh pgdata volume with bootstrap_bakeoff() defined but never called for any runner — no schemas at all — while up reports success throughout. Happy to cherry-pick it onto its own branch if you would rather review it separately.

ff23b8f is the Kruxia Flow work: compose profile on 8100/8101 (not its own defaults of 8080/8081, which belong to Airflow and Kestra here), image pinned to 0.8.3 because latest on Docker Hub is 0.3.0 and predates --insecure-dev, BAKEOFF_NS isolation, bakeoff-db.sh routing, Justfile teardown lists, RUNNING.md port map, and a kruxiaflow resume provider in both mock services.

Environment note

This machine has no just and podman with no initialised VM (Docker Desktop is the live runtime), so every documented recipe was run as its raw docker compose equivalent with CONTAINER_RUNNER=docker. The Justfile picks a runtime by binary presence without checking it connects — flagged in PROGRESS.md, not changed here, since it touches shared infrastructure for all twelve other tools.

Engine defects were filed upstream in the Kruxia Flow repo rather than here; kruxiaflow_bakeoff/UPSTREAM-ISSUES.md indexes them.

🤖 Generated with Claude Code

https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1

deangoodmanson and others added 2 commits August 14, 2026 20:03
The file was mode 644. The Postgres entrypoint runs docker-entrypoint-initdb.d
in lexical order and *execs* a .sh file when it looks executable, otherwise
sources it. Under Docker Desktop on macOS a bind-mounted 644 file is presented
as executable, so the entrypoint exec'd it and it died:

    /docker-entrypoint-initdb.d/02-init-runners.sh: /bin/bash:
      bad interpreter: Permission denied

The result is a fresh pgdata volume with bootstrap_bakeoff() defined but NOT
CALLED FOR ANY RUNNER -- no <ns>_dag1/_dag3/_dag4 schemas at all -- and `up`
reports success throughout. The failure only appears in the postgres container
log, and every DAG then fails later against missing tables.

Making it executable is correct under both runtimes: the script already ends
`return 0 2>/dev/null || exit 0` precisely so it works sourced or exec'd.

Found on a machine where podman has no VM and Docker Desktop is the live
runtime, so it is likely latent rather than new.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
Kruxia Flow 0.8.3 -- a Rust durable-execution engine, one binary plus Postgres.
Evaluated head-to-head against Temporal (category) and Conductor (mechanism)
rather than re-running the other eleven tools.

Infrastructure:
- `kruxiaflow` compose profile on ports 8100/8101 (NOT its own defaults of
  8080/8081, which belong to Airflow and Kestra here), with the keygen and
  catalog one-shots upstream requires. Image pinned to 0.8.3: `latest` on
  Docker Hub is 0.3.0 and predates --insecure-dev, so it 401s everything.
- `kruxiaflow` database on the shared postgres; BAKEOFF_NS schema isolation;
  bakeoff-db.sh routing; Justfile teardown lists; RUNNING.md port map.
- A `kruxiaflow` resume provider in both mock services. It checks the response
  BODY, not the status code: the signal endpoint answers HTTP 200 with
  {"signaled": false} when nothing was waiting.

DAG 3 (payment) -- verified, all six spec assertions, zero authoring defects:
happy path; non-retriable decline in 1 attempt; retriable 5xx in exactly 5
attempts (48.6s = 3+6+12+24); validation rejection; duplicate idempotency key
with no second debit; graceful degradation on notification failure. All of its
database work is declarative -- five validation checks as one CASE expression,
and the debit/credit as one idempotent CTE under `serializable`.

DAG 4 is written and deploys but has NEVER RUN, and DAG 2 cannot be written at
all: wait_for_signal cannot complete on 0.8.3 (a signalled activity always
fails). Treat the DAG 4 files as a design record, not verified code. Testing
was stopped there rather than measuring polling workarounds -- see
kruxiaflow_bakeoff/README.md for the reasoning.

comparison.md is deliberately NOT given a 13th column: three of four DAGs are
unverified, so a full column would imply a completeness this does not have.
The provisional score (49 as-is, 55 if the blocker is fixed) and the two
head-to-heads live in kruxiaflow-comparison.md.

Engine defects were filed upstream in kruxiaflow-internal, not here;
kruxiaflow_bakeoff/UPSTREAM-ISSUES.md indexes them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
@deangoodmanson
deangoodmanson marked this pull request as draft August 15, 2026 01:16
deangoodmanson and others added 4 commits August 14, 2026 21:47
Same self-review applied to the upstream PR, run over this side before the
maintainer reads it.

Corrections:
- **`http.rs:307` cited the file-download branch**, which is easy to dismiss as
  a special case. Replaced with `http.rs:130` (the `HttpResponse` struct) and
  `:364`, the main path's unconditional `Ok(...)` -- a much harder line to argue
  with, and the actual basis for the claim.
- **"Temporal: 7+ components"** came from Kruxia Flow's own README comparison
  table, appears nowhere in this repo, and is contradicted by this repo's own
  compose, which runs Temporal as three services on the auto-setup image. Now
  states what was actually observed here. Vendor framing does not belong in
  this comparison.
- **"Conductor ~60s start"** was the compose healthcheck's start grace, not a
  measurement. Reworded as configuration.
- **Binary size** was cited as 13 MB, then briefly as a measured 9.1 MB with a
  note that upstream's own docs say 7.5 MB and 13 MB in different places. That
  is splitting hairs about a number that varies by build host and toolchain --
  the material point is "one small static binary, not a JVM", so it now says
  that and nothing more.

Verified while reviewing: the provisional score table sums correctly (49 as-is,
55 if the blocker is fixed) with no category exceeding its weight, and all
remaining code citations resolve to what the text claims.

Citations in the markdown are now links to that exact line on
kruxia/kruxiaflow at commit 75f9a77, and UPSTREAM-ISSUES.md links each filed
doc on the kruxiaflow-internal branch rather than naming paths in prose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
…e instead

Every reference pointed at kruxia/kruxiaflow-internal, which is private -- a
reviewer of this PR would have hit a 404 with no explanation. All 20 are gone.

Three of them were load-bearing *evidence*, so they needed public substitutes
rather than deletion, and in each case the public version is the stronger claim:

- "no dynamic fan-out" cited an internal feature doc marked Proposed. It now
  rests on the source itself -- no such field on ActivityDefinition, no
  reference anywhere in the Rust tree -- plus the public loops-guide, which
  lists "Parallel iterations: Multiple iterations executing simultaneously"
  under Future Enhancements (Post-MVP). Checkable by anyone.
- "no sub-workflow construct; chaining is Deferred" cited an internal doc. It
  now rests on absence from ActivityDefinition and from the built-in activity
  registry, which is what actually matters to a workflow author.
- The NUMERIC finding cited an internal fixed-bug report as prior art. Reworded
  to state the fact (the maintainers have fixed this symptom before on other
  types by extending the same ladder) without pointing at an unreachable file.

UPSTREAM-ISSUES.md is restructured: its whole premise was linking private docs,
so it is now a self-contained summary of what was reported and why three of the
four DAGs are unverified, with each finding traceable to a line in the public
repo at 75f9a77.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
It noted that kruxia/kruxiaflow has issues enabled and has never received one.
Accurate, but it was a swipe at the project's maturity in a document whose job
is to explain why three of the four DAGs are unverified -- and the maturity
judgement already has a proper home in the scored comparison, where it can be
weighed rather than dropped in as an aside.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
dag4_order_fulfillment.yaml pointed at `dag4_order_fulfillment_flat.yaml` as
though it existed. It does not: the flat variant was planned to measure what
the missing sub-workflow construct costs, and was abandoned when the composed
version turned out to be unrunnable -- comparing two untested files would have
measured nothing.

The header now says that, and states plainly at the top that this file and all
three subflows/ have never been run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LaJtGQxTm5k4J8cB4AGA1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant