akm-cli 0.9.29 β 0.9.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +10 -10
- package/ROADMAP.md +112 -0
- package/dist/commands/improve/improve-cli.js +33 -12
- package/dist/commands/sources/plugin-upgrade.js +46 -23
- package/dist/core/file-lock.js +10 -1
- package/dist/llm/client.js +33 -3
- package/dist/scripts/akm-migrate-node.js +5 -1
- package/dist/scripts/akm-migrate.js +5 -1
- package/docs/README.md +42 -41
- package/docs/agents/README.md +15 -0
- package/docs/agents/agent-install.md +14 -0
- package/docs/architecture/README.md +67 -0
- package/docs/architecture/adapters.md +272 -0
- package/docs/architecture/akm-architecture-decision-history.md +2055 -0
- package/docs/architecture/akm-core-principles.md +126 -0
- package/docs/architecture/architecture.md +579 -0
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
- package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
- package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
- package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
- package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
- package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
- package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
- package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
- package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
- package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
- package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
- package/docs/architecture/decisions/README.md +62 -0
- package/docs/architecture/improvement.md +440 -0
- package/docs/architecture/internals/classification.md +154 -0
- package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
- package/docs/architecture/internals/functional-contract-patterns.md +340 -0
- package/docs/architecture/internals/health-advisories.md +110 -0
- package/docs/architecture/internals/improve-workflow.md +713 -0
- package/docs/architecture/internals/indexing.md +498 -0
- package/docs/architecture/internals/registry-network-boundary.md +57 -0
- package/docs/architecture/internals/search.md +249 -0
- package/docs/architecture/internals/storage-locations.md +943 -0
- package/docs/architecture/persisted-data-compat.md +59 -0
- package/docs/architecture/reviews/env-secret-access.md +319 -0
- package/docs/architecture/runtime-boundary-design.md +153 -0
- package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
- package/docs/architecture/specs/0.9.0-decisions.md +528 -0
- package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
- package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
- package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
- package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
- package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
- package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
- package/docs/architecture/specs/di-seams-plan.md +539 -0
- package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
- package/docs/architecture/specs/fact-asset-type.md +142 -0
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
- package/docs/architecture/specs/okf-support.md +148 -0
- package/docs/architecture/specs/ref.md +329 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
- package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
- package/docs/architecture/specs/stash-organization-conventions.md +377 -0
- package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
- package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
- package/docs/architecture/specs/workflow-format-unification.md +437 -0
- package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
- package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
- package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
- package/docs/architecture/testing/testing-workflow.md +446 -0
- package/docs/architecture/workflow-engine.md +274 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/author-bundles.md +310 -0
- package/docs/guides/author-workflows.md +520 -0
- package/docs/guides/bundles.md +321 -0
- package/docs/guides/capture-knowledge.md +136 -0
- package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
- package/docs/guides/concepts.md +93 -0
- package/docs/guides/discover-and-load.md +158 -0
- package/docs/guides/getting-started.md +240 -0
- package/docs/guides/improve-the-library.md +246 -0
- package/docs/guides/knowledge-management.md +18 -0
- package/docs/guides/recipes/headless-install.md +97 -0
- package/docs/guides/recipes/website-source.md +28 -0
- package/docs/guides/run-workflows.md +279 -0
- package/docs/guides/scheduling.md +123 -0
- package/docs/guides/use-with-any-agent.md +129 -0
- package/docs/guides/wikis.md +81 -0
- package/docs/maintainers/README.md +19 -0
- package/docs/maintainers/curate-workmap.md +336 -0
- package/docs/maintainers/eval.md +3 -0
- package/docs/maintainers/local-development.md +108 -0
- package/docs/maintainers/release-checklist.md +103 -0
- package/docs/migration/README.md +2 -2
- package/docs/migration/release-notes/0.0.13.md +1 -1
- package/docs/migration/release-notes/0.1.0.md +1 -1
- package/docs/migration/release-notes/0.2.0.md +1 -1
- package/docs/migration/release-notes/0.3.0.md +1 -1
- package/docs/migration/release-notes/0.5.0.md +1 -1
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.3.md +2 -2
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +1 -1
- package/docs/migration/release-notes/0.8.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +1 -1
- package/docs/migration/release-notes/0.9.14.md +1 -1
- package/docs/migration/release-notes/0.9.15.md +1 -1
- package/docs/migration/release-notes/0.9.16.md +1 -1
- package/docs/migration/release-notes/0.9.17.md +1 -1
- package/docs/migration/release-notes/0.9.19.md +1 -1
- package/docs/migration/release-notes/0.9.2.md +1 -1
- package/docs/migration/release-notes/README.md +27 -31
- package/docs/migration/v0.5-to-v0.6.md +759 -0
- package/docs/migration/v0.8-to-v0.9.md +1 -1
- package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
- package/docs/posts/README.md +30 -0
- package/docs/posts/akm-command-tour.md +495 -0
- package/docs/posts/akm-logo-sized.webp +0 -0
- package/docs/posts/belief-aware-memory-12.md +139 -0
- package/docs/posts/config-v2-lessons-16.md +191 -0
- package/docs/posts/improve-loop-autonomous-10.md +225 -0
- package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
- package/docs/posts/intro-part-01.md +76 -0
- package/docs/posts/intro-part-02.md +221 -0
- package/docs/posts/intro-part-03.md +190 -0
- package/docs/posts/intro-part-04.md +221 -0
- package/docs/posts/llm-mode-reflect-13.md +166 -0
- package/docs/posts/openpalm-built-on-akm.md +86 -0
- package/docs/posts/progressive-disclosure-06.md +129 -0
- package/docs/posts/proposal-safety-net-15.md +112 -0
- package/docs/posts/release-0.5.0.md +247 -0
- package/docs/posts/release-0.6.0.md +117 -0
- package/docs/posts/release-0.7.0.md +159 -0
- package/docs/posts/release-0.8.0.md +332 -0
- package/docs/posts/scattered-skills-05.md +100 -0
- package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
- package/docs/posts/team-skills-07.md +198 -0
- package/docs/posts/wiki-agent-knowledge-08.md +223 -0
- package/docs/posts/workflows-vaults-09.md +321 -0
- package/docs/product-surface.md +54 -0
- package/docs/reference/README.md +1 -2
- package/docs/reference/asset-types.md +168 -0
- package/docs/reference/cli.md +5 -6
- package/docs/reference/env-and-secrets.md +126 -0
- package/docs/reference/memory.md +124 -0
- package/docs/reference/refs.md +156 -0
- package/docs/reference/registry.md +489 -0
- package/docs/reference/website-sources.md +141 -0
- package/package.json +12 -15
package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# OpenWorkflow as a replacement/underpinning for akm's workflow engine
|
|
2
|
+
|
|
3
|
+
Evaluated: `openworkflow` v0.9.2 (npm, published 2026-07-21) β https://github.com/openworkflowdev/openworkflow
|
|
4
|
+
Assessed 2026-08-01 against akm @ `/home/user/akm` (`akm-cli` 0.9.0-rc.13, MPL-2.0, Bun).
|
|
5
|
+
|
|
6
|
+
Method: repo cloned to scratch, source read directly; docs read from `apps/docs/docs/*.mdx`;
|
|
7
|
+
library installed from npm and **executed under Bun 1.3.11** to verify embedding, crash-resume,
|
|
8
|
+
long-step lease behavior, and a schema-collision hazard. No claim below rests on training knowledge.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Verdict summary
|
|
13
|
+
|
|
14
|
+
| # | Rubric item | Score | Evidence |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| 1 | Server requirement | **PASS** | Embedded worker class, SQLite file, zero deps |
|
|
17
|
+
| 2 | Durability & crash-resume | **PASS** | Verified by SIGKILL probe; memoized replay |
|
|
18
|
+
| 3 | Orchestration primitives | **PARTIAL** | No concurrency cap, no error classification, no step timeout |
|
|
19
|
+
| 4 | Long-running steps | **PARTIAL** | Heartbeat holds 40s step; no in-flight cancellation |
|
|
20
|
+
| 5 | Runtime & platform | **PARTIAL** | Bun first-class; Windows untested in CI |
|
|
21
|
+
| 6 | Maturity | **PARTIAL** | 1.3k stars, 9mo old, bus factor 1, pre-1.0 |
|
|
22
|
+
| 7 | Integration | **PARTIAL** | ~8-12% of exec+ir deletable; adapter code added |
|
|
23
|
+
| 8 | Risks | **PARTIAL** | Solo maintainer, 0.x churn, well-funded competitor |
|
|
24
|
+
|
|
25
|
+
**Bottom line: technically excellent and a genuinely close conceptual match, but it replaces only
|
|
26
|
+
the small durable-plumbing fraction of akm's engine while imposing a second database file, a
|
|
27
|
+
1000-step ceiling, and loss of in-flight unit cancellation β not worth adopting as a replacement.**
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 1. Server requirement β PASS
|
|
32
|
+
|
|
33
|
+
**Fully embeddable in a short-lived CLI process. No daemon, broker, or DB server.**
|
|
34
|
+
|
|
35
|
+
- The `Worker` is an ordinary in-process class, not a service:
|
|
36
|
+
`new Worker({ backend, workflows, concurrency })` with `start()` / `stop()` / `tick()`
|
|
37
|
+
β `packages/openworkflow/worker/worker.ts:43-118`. Exported from the package root
|
|
38
|
+
(`packages/openworkflow/index.ts`).
|
|
39
|
+
- Client and worker share one process: `OpenWorkflow.newWorker()` constructs a worker bound to the
|
|
40
|
+
same backend and registry (`client/client.ts:54-60`).
|
|
41
|
+
- **SQLite is a first-class backend**, exported as `openworkflow/sqlite`
|
|
42
|
+
(`packages/openworkflow/package.json` `exports["./sqlite"]`).
|
|
43
|
+
`BackendSqlite.connect(path)` is **synchronous** and auto-migrates
|
|
44
|
+
(`sqlite/backend.ts:114-128`; `apps/docs/docs/sqlite.mdx`).
|
|
45
|
+
- **Zero runtime dependencies.** Confirmed at install time: `bun add openworkflow` β
|
|
46
|
+
"1 package installed"; `dependencies: None`, only an *optional* peer dep on `postgres`
|
|
47
|
+
(needed solely for the Postgres backend). Supply-chain surface is essentially nil.
|
|
48
|
+
- Postgres is offered but strictly optional (`openworkflow/postgres`).
|
|
49
|
+
|
|
50
|
+
**Verified empirically.** A single Bun script that connects SQLite, defines a workflow, starts a
|
|
51
|
+
worker, runs a workflow to completion, stops the worker, and exits cleanly:
|
|
52
|
+
`/tmp/.../scratchpad/probe/run.ts` β final log line `process exiting cleanly`, exit code 0.
|
|
53
|
+
|
|
54
|
+
The only caveat: the *documented* path is `npx @openworkflow/cli worker start` as a long-lived
|
|
55
|
+
process (`apps/docs/docs/workers.mdx`). The embedded one-shot pattern works but is not a
|
|
56
|
+
documented, first-class use case, so it is not covered by the project's own regression focus.
|
|
57
|
+
|
|
58
|
+
## 2. Durability & crash-resume β PASS
|
|
59
|
+
|
|
60
|
+
This is the library's strongest dimension and it maps almost exactly onto what akm already built.
|
|
61
|
+
|
|
62
|
+
- **Deterministic replay with step memoization.** The worker re-executes the workflow function
|
|
63
|
+
from the top on every claim, serving completed steps from a cache keyed on step name
|
|
64
|
+
(`worker/step-history.ts:46-76`, `createStepExecutionStateFromAttempts`), so completed steps are
|
|
65
|
+
never re-executed. Documented in `apps/docs/docs/workers.mdx` ("How Workers Execute Workflows").
|
|
66
|
+
- **State lives entirely in the DB**, in three tables β `workflow_runs` (also the job queue),
|
|
67
|
+
`step_attempts` (the memoization log), `workflow_signals`
|
|
68
|
+
(`sqlite/sqlite.ts:58-227`, `migrations()`).
|
|
69
|
+
- **Crash detection via lease expiry**: claiming sets `available_at = now + leaseDuration`;
|
|
70
|
+
a crashed worker stops heartbeating, the timestamp lapses, and any worker can re-claim
|
|
71
|
+
(`ARCHITECTURE.md`; `worker/worker.ts:270-285`).
|
|
72
|
+
|
|
73
|
+
**Verified empirically β this is the decisive test.** Probe workflow: `step-a`, then a 4-way
|
|
74
|
+
dynamic fan-out (`unit:u1..u4`), then a 40-second child-process step. Run 1 `SIGKILL`ed itself
|
|
75
|
+
mid-long-step. Run 2 was a fresh process against the same SQLite file:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
# run 1 (killed)
|
|
79
|
+
EXEC step-a / EXEC unit:u1 / EXEC unit:u2 / EXEC unit:u3 / EXEC unit:u4
|
|
80
|
+
EXEC long-agent-spawn START ... CRASHING process mid-long-step (SIGKILL self)
|
|
81
|
+
|
|
82
|
+
# run 2 (fresh process)
|
|
83
|
+
REPLAY: workflow fn entered <- top-of-function replay
|
|
84
|
+
EXEC long-agent-spawn START <- ONLY the incomplete step re-ran
|
|
85
|
+
EXEC long-agent-spawn END
|
|
86
|
+
RESULT {"a":{"a":1},"out":["done-u1","done-u2","done-u3","done-u4"],"long":"agent-output",...}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`step-a` and all four fan-out units produced **no** `EXEC` lines on resume β they were served from
|
|
90
|
+
the memoization cache, and their values were correctly threaded into the final result. This is
|
|
91
|
+
precisely akm's "durable-row resume: re-invoking a partially-executed run re-dispatches only work
|
|
92
|
+
that never completed" (`src/workflows/exec/run-workflow.ts` header).
|
|
93
|
+
|
|
94
|
+
**Measured friction:** the lease is a hardcoded `DEFAULT_LEASE_DURATION_MS = 30 * 1000`
|
|
95
|
+
(`worker/worker.ts:12`) and is **not exposed** in `WorkerOptions` (`worker/worker.ts:33-37`).
|
|
96
|
+
In the probe, the crash was at `23:09:16`, the new process started at `23:09:27`, and it could not
|
|
97
|
+
claim until `23:09:45` β an **18-second dead wait**, up to 30s worst case. akm's own lease is 90s
|
|
98
|
+
but akm controls it; here a user typing `akm workflow run <id>` after a crash would watch the CLI
|
|
99
|
+
sit idle for up to half a minute with no way to tune it short of patching the library.
|
|
100
|
+
|
|
101
|
+
## 3. Orchestration primitives β PARTIAL
|
|
102
|
+
|
|
103
|
+
The model is "durable functions", not a declarative graph engine: control flow is plain
|
|
104
|
+
TypeScript, and only side effects are wrapped in `step.run`.
|
|
105
|
+
|
|
106
|
+
| Primitive | Status | Evidence |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Step graph | **N/A by design** | Ordinary TS control flow; no graph IR. `core/workflow-function.ts` |
|
|
109
|
+
| Fan-out | **Yes** | `Promise.all` over `step.run`; auto-disambiguates duplicate names to `name:1`, `name:2` (`apps/docs/docs/dynamic-steps.mdx`) |
|
|
110
|
+
| Concurrency cap | **NO** | Not implemented. Roadmap "Coming Soon: Priority and concurrency controls" (`apps/docs/docs/roadmap.mdx`); issue **#20 "Workflow concurrency limit" open since 2025-11-13**. Docs tell you to hand-roll batching (`apps/docs/docs/parallel-steps.mdx`, "Limit Parallelism for External APIs") |
|
|
111
|
+
| Reduction | **Manual** | Plain JS over the `Promise.all` result array |
|
|
112
|
+
| Conditional routing | **Manual** | `if`/`else` in the workflow body; no route/branch construct |
|
|
113
|
+
| Bounded retry | **Yes** | Per-step `retryPolicy` (`initialInterval`, `backoffCoefficient`, `maximumInterval`, `maximumAttempts`), default 10 attempts (`apps/docs/docs/retries.mdx`) |
|
|
114
|
+
| Error classification | **NO** | `RetryPolicy = BackoffPolicy & {maximumAttempts}` (`core/workflow-definition.ts:134`). No non-retryable error types; a repo-wide grep for `nonRetryable`/`NonRetryable` finds only an unrelated internal comment |
|
|
115
|
+
| Per-step timeout | **NO** | `StepFunctionConfig` is `{ name, retryPolicy? }` only (`core/workflow-function.ts:16-27`). `timeout` exists **only** on the *wait* primitives `runWorkflow` and `waitForSignal` |
|
|
116
|
+
| Cancellation | **Partial** | Run-level only, cooperative and non-preemptive (see Β§4) |
|
|
117
|
+
| Signals | **Yes** | `sendSignal` / `waitForSignal` with payloads and timeouts |
|
|
118
|
+
| Child workflows | **Yes** | `step.runWorkflow(spec, input, {timeout})` |
|
|
119
|
+
| Sleep | **Yes** | `step.sleep(name, duration)` parks the run and frees the worker slot |
|
|
120
|
+
|
|
121
|
+
Three gaps land directly on features akm already has and relies on:
|
|
122
|
+
|
|
123
|
+
1. **No concurrency cap.** akm's `src/workflows/exec/scheduler.ts` takes the minimum of the map
|
|
124
|
+
request, the frozen workflow cap, the frozen LLM-engine cap, and a CPU-derived host cap β
|
|
125
|
+
deliberately re-applying host safety at dispatch "when a frozen run resumes on a smaller
|
|
126
|
+
machine." OpenWorkflow has no equivalent, so akm's scheduler would be **kept, not deleted**.
|
|
127
|
+
2. **No error classification.** akm's `isRetryEligibleFailure` (`src/workflows/exec/step-work.ts:831`)
|
|
128
|
+
retries only when `retry.on.includes(failureReason)`. Under OpenWorkflow you would re-implement
|
|
129
|
+
this by catching inside the step and re-throwing or returning a discriminated result.
|
|
130
|
+
3. **No per-step timeout.** akm resolves one at freeze time β `effectiveTimeout`
|
|
131
|
+
(`src/workflows/ir/freeze.ts`) layers the unit's `timeout:`, the document's `defaults.timeout`,
|
|
132
|
+
`engines.<name>.timeoutMs`, then the engine-kind default (`DEFAULT_LLM_TIMEOUT_MS = 600_000`;
|
|
133
|
+
`DEFAULT_AGENT_TIMEOUT_MS = null`, i.e. agent units are unbounded unless declared) β and freezes
|
|
134
|
+
the result into `IrInvocation.timeoutMs`, which dispatch applies verbatim. There is no engine-side
|
|
135
|
+
backstop on top of it, deliberately: `timeout: none` freezes to `null` and must stay unbounded.
|
|
136
|
+
Workaround is a `Promise.race` inside every step β easy, but it is akm's code again, not the
|
|
137
|
+
library's.
|
|
138
|
+
|
|
139
|
+
**Hard capacity ceiling.** `WORKFLOW_STEP_LIMIT = 1000` step attempts per run, enforced, and
|
|
140
|
+
retries count against it (`worker/step-history.ts:8`; `apps/docs/docs/retries.mdx`). akm permits
|
|
141
|
+
`WORKFLOW_MAX_MAP_EXPANSION = 10_000` (`src/workflows/resource-limits.ts:15`). **A 10x reduction in
|
|
142
|
+
maximum fan-out.** Escaping it means one child workflow per unit, i.e. a full `workflow_runs` row
|
|
143
|
+
per agent invocation.
|
|
144
|
+
|
|
145
|
+
## 4. Long-running steps β PARTIAL
|
|
146
|
+
|
|
147
|
+
**Heartbeats: PASS, and verified.** The heartbeat is a `setInterval` at half the lease (15s) that
|
|
148
|
+
extends the lease independently of the step promise (`worker/worker.ts:270-285`). In the probe, a
|
|
149
|
+
**40-second** step (an actual `Bun.spawn` child process) ran under a 30-second lease and completed
|
|
150
|
+
normally β the lease was held throughout, nothing was stolen, no duplicate execution. Minutes-long
|
|
151
|
+
agent-CLI spawns and LLM calls are safe.
|
|
152
|
+
|
|
153
|
+
**Stalled recovery: PASS.** A dead worker's run becomes claimable once `available_at` lapses
|
|
154
|
+
(verified in Β§2).
|
|
155
|
+
|
|
156
|
+
**Sleep/wait: PASS.** `step.sleep` durably parks the run and releases the worker slot rather than
|
|
157
|
+
blocking it (`ARCHITECTURE.md`), and `waitForSignal` supports waits up to a 1-year default
|
|
158
|
+
(`worker/step-history.ts:83-87`, `defaultWaitTimeoutAt`).
|
|
159
|
+
|
|
160
|
+
**Cancellation of in-flight work: FAIL β the sharpest functional regression.**
|
|
161
|
+
|
|
162
|
+
- `StepFunction<Output> = () => Promise<Output | undefined> | Output | undefined`
|
|
163
|
+
(`core/workflow-function.ts:32`). The step function receives **no arguments** β no `AbortSignal`,
|
|
164
|
+
no context, no attempt number.
|
|
165
|
+
- The docs are explicit: "If a workflow is mid-execution when canceled, the current step may
|
|
166
|
+
complete, but no new steps will run on the next poll" (`apps/docs/docs/canceling.mdx`).
|
|
167
|
+
|
|
168
|
+
akm's scheduler deliberately does the opposite: "Cooperative cancellation via AbortSignal (workers
|
|
169
|
+
stop claiming items; **the same signal is passed into each dispatch so in-flight units can be
|
|
170
|
+
preempted too**)" (`src/workflows/exec/scheduler.ts:16-18`). Under OpenWorkflow, cancelling an akm
|
|
171
|
+
run would leave a 20-minute agent CLI running to completion with its output discarded. There is no
|
|
172
|
+
clean fix inside the model β you would keep akm's abort plumbing *outside* `step.run`, which
|
|
173
|
+
fights the framework rather than using it.
|
|
174
|
+
|
|
175
|
+
Related: `handle.result()` defaults to a **5-minute** timeout (`client/client.ts:298`). Trivially
|
|
176
|
+
overridden per call (`result({ timeoutMs })`, as the probe does with 180s), but the default is
|
|
177
|
+
wrong for akm's workloads and would bite on first use.
|
|
178
|
+
|
|
179
|
+
## 5. Runtime & platform β PARTIAL
|
|
180
|
+
|
|
181
|
+
- **TypeScript: excellent.** The package *is* TypeScript, ships `.d.ts`, and uses
|
|
182
|
+
Standard Schema so Zod/Valibot/ArkType/Yup all work for input validation
|
|
183
|
+
(`apps/docs/docs/standard-schema.mdx`). `@tsconfig/strictest` in the root devDeps.
|
|
184
|
+
- **Bun: first-class and CI-tested.** The SQLite driver explicitly branches on
|
|
185
|
+
`process.versions["bun"]` and loads `bun:sqlite`, falling back to `node:sqlite`
|
|
186
|
+
(`sqlite/sqlite.ts:23-52`). CI runs a dedicated `ci-bun` job (`bun run ci:bun`) alongside the
|
|
187
|
+
Node job (`.github/workflows/ci.yaml`). Verified working under Bun 1.3.11 in every probe.
|
|
188
|
+
This matters β Bun support in this space is usually an afterthought, and here it is not.
|
|
189
|
+
- **Node:** requires >= 20 per `engines`, but the SQLite path needs `node:sqlite`, i.e.
|
|
190
|
+
**Node 22.5+** (`apps/docs/docs/sqlite.mdx`). akm already requires Node >= 22 (`package.json`
|
|
191
|
+
`preinstall`), so no conflict.
|
|
192
|
+
- **Windows: untested.** CI is `runs-on: ubuntu-latest` for every job β no Windows, no macOS
|
|
193
|
+
(`.github/workflows/ci.yaml`). Nothing in the code is obviously Unix-bound (no native modules,
|
|
194
|
+
no `child_process` in the library, paths handled by the SQLite driver), so it will *probably*
|
|
195
|
+
work, but akm ships `install.ps1` and supports Windows, and this dependency would be carrying
|
|
196
|
+
zero Windows regression coverage.
|
|
197
|
+
|
|
198
|
+
## 6. Maturity β PARTIAL
|
|
199
|
+
|
|
200
|
+
| Metric | Value | Source |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| Stars | ~1,286-1,300 | GitHub repo page; dependents page |
|
|
203
|
+
| Forks | 53-61 | same |
|
|
204
|
+
| Age | **9 months** β first commit 2025-10-27 | `git log` on the clone |
|
|
205
|
+
| Commits | 1,135 | `git rev-list --count HEAD` |
|
|
206
|
+
| Last commit | 2026-07-29 (3 days before assessment) | `git log -1` |
|
|
207
|
+
| Releases | 25 npm versions, `0.0.1` (2025-10-26) β **`0.9.2`** (2026-07-21) | registry.npmjs.org |
|
|
208
|
+
| Open issues | **10**, all enhancements/questions β zero open bug reports | GitHub issues |
|
|
209
|
+
| License | **Apache-2.0** | `LICENSE.md` |
|
|
210
|
+
| Tests | 19 test files; `worker/execution.test.ts` is 128 KB, `worker/worker.test.ts` 64 KB, plus a `chaos.test.ts` | clone |
|
|
211
|
+
| Coverage gates | statements/functions/lines **90%**, branches 80%, and **100% on `core/**`** | `vitest.config.ts:17-28` |
|
|
212
|
+
| Dependents | **9 repositories / 11 packages** | GitHub dependents page |
|
|
213
|
+
|
|
214
|
+
**Strengths.** The engineering discipline is unusually high for a 9-month-old project: enforced
|
|
215
|
+
coverage thresholds including 100% on core, a chaos test, `knip` dead-code checks, `jscpd`
|
|
216
|
+
duplication checks, `cspell`, ESLint with `boundaries`/`functional`/`sonarjs`, commitlint, Postgres
|
|
217
|
+
service containers in CI, and a separate Bun CI lane. Zero runtime dependencies. Clean, fully
|
|
218
|
+
parameterized SQL (`sqlite/backend.ts`), no `eval`, no `new Function`, no `child_process` anywhere
|
|
219
|
+
in the shipped library. The architecture doc is genuinely good.
|
|
220
|
+
|
|
221
|
+
**Weaknesses.**
|
|
222
|
+
- **Bus factor 1.** `git shortlog -sne`: James Martinez **663** commits; dependabot 436;
|
|
223
|
+
github-actions 19; **the largest outside human contributor has 3 commits**, and 11 of the 13
|
|
224
|
+
other humans have exactly 1. This is one person's project with drive-by PRs.
|
|
225
|
+
- **Pre-1.0 with real churn.** 25 releases in 9 months; `succeeded` status already deprecated in
|
|
226
|
+
favor of `completed` (`client/client.ts:345`), `declareWorkflow` already deprecated in favor of
|
|
227
|
+
`defineWorkflowSpec` (`core/workflow-definition.ts:41`). Both are visible in the current public
|
|
228
|
+
surface, i.e. breaking renames are happening at roughly the pace of minor versions.
|
|
229
|
+
- **Cadence is uneven.** Commits/month: Nov 158, Dec 149, Jan 203, Feb 246, Mar 46, Apr 106,
|
|
230
|
+
**May 15**, Jun 74, Jul 113. The Mar-May trough coincides with a 3-month release gap
|
|
231
|
+
(0.9.0 on 2026-04-10 β 0.9.1 on 2026-07-18). Recovered, but it shows what a single maintainer's
|
|
232
|
+
availability does to the project.
|
|
233
|
+
- **Adoption is thin.** ~1.3k stars but only **9 dependent repositories**, the largest of which
|
|
234
|
+
(`mistlehq/mistle`) has 88 stars. Stars are interest; dependents are usage. I found no
|
|
235
|
+
independent production write-ups or third-party reviews via search β only the project's own site
|
|
236
|
+
and docs.
|
|
237
|
+
|
|
238
|
+
**On the "Security Disclosure: Database Credentials Exposed" issue (#482, closed 2026-04-23):
|
|
239
|
+
this is a false positive and should not count against the project.** I verified the claim directly
|
|
240
|
+
against the clone. Every `postgres://` string in the repo is
|
|
241
|
+
`postgresql://postgres:postgres@localhost:5432/postgres` β the standard local-dev default β in
|
|
242
|
+
`examples/*`, `openworkflow/client.ts`, and the Postgres backend's default constant
|
|
243
|
+
(`postgres/postgres.ts:4`), plus placeholder text like `user:password@localhost` in
|
|
244
|
+
`apps/cli/commands.ts:1059` and the docs. There is no real credential and no rotation commit in the
|
|
245
|
+
surrounding history. It reads as automated bug-bounty spam.
|
|
246
|
+
|
|
247
|
+
## 7. Integration sketch
|
|
248
|
+
|
|
249
|
+
### How akm's concepts would map
|
|
250
|
+
|
|
251
|
+
| akm | OpenWorkflow | Fit |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| Frozen plan (`plan_json` + `plan_hash`) | Workflow run `input` (JSON) | Good β one registered generic workflow interpreting the plan from input |
|
|
254
|
+
| `workflow_run_units` rows | `step_attempts` rows | Partial β see below |
|
|
255
|
+
| Engine lease (90s, holder id, renewed between steps) | Worker lease + heartbeat (30s, fixed) | Direct replacement, but not tunable |
|
|
256
|
+
| Durable-row resume | Step memoization | Direct replacement β verified working |
|
|
257
|
+
| Unit retry with `retry.on` classification | `retryPolicy` | Partial β loses classification |
|
|
258
|
+
| Unit concurrency caps | β | **No equivalent; keep akm's scheduler** |
|
|
259
|
+
| Judge-gate loop (`gate.max_loops`, feedback threading) | β | **No equivalent; entirely akm's** |
|
|
260
|
+
| Driver protocol (`brief`/`report`) | `waitForSignal` / `sendSignal` | **Good fit conceptually** β `report` becomes `sendSignal` |
|
|
261
|
+
| Route/branch (`evaluateRoute`, `cascadeSkippedRouter`) | plain `if`/`else` | Rewrite, not reuse |
|
|
262
|
+
| Markdown β IR compile/freeze | β | **No equivalent; entirely akm's** |
|
|
263
|
+
|
|
264
|
+
The `brief`/`report` protocol is the pleasant surprise: an external harness reporting unit results
|
|
265
|
+
maps naturally onto `sendSignal`, with the workflow parked on `waitForSignal` in between. That part
|
|
266
|
+
of akm's design is genuinely idiomatic OpenWorkflow.
|
|
267
|
+
|
|
268
|
+
### Blocking hazard: table-name collision (verified)
|
|
269
|
+
|
|
270
|
+
OpenWorkflow's SQLite backend creates a table literally named **`workflow_runs`**
|
|
271
|
+
(`sqlite/sqlite.ts:77`) β and akm **already has** a `workflow_runs` table with a completely
|
|
272
|
+
different schema (`src/core/state/migrations.ts:881`), plus `workflow_run_steps` (`:910`) and
|
|
273
|
+
`workflow_run_units` (`:929`).
|
|
274
|
+
|
|
275
|
+
Because the migration uses `CREATE TABLE IF NOT EXISTS`, pointing OpenWorkflow at akm's state DB
|
|
276
|
+
**fails silently at migration time and then explodes at query time**. Reproduced:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
COLLISION ERROR: SQLite backend failed to open database. ...: no such column: parent_step_attempt_id
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
There is **no table-prefix option**: `BackendSqliteOptions` is `{ namespaceId?, runMigrations? }`
|
|
283
|
+
(`sqlite/backend.ts:55-58`), and `namespaceId` is a row-level discriminator, not a naming scheme.
|
|
284
|
+
The Postgres backend supports a custom `schema` (`postgres/backend.ts:64`); **the SQLite backend
|
|
285
|
+
does not**. The private constructor also means you cannot inject akm's existing Bun `Database`
|
|
286
|
+
handle.
|
|
287
|
+
|
|
288
|
+
So adoption forces **a second SQLite file**. That is the real cost: akm currently writes step
|
|
289
|
+
completion, unit rows, and `usage_events` under one transaction in one database. Split across two
|
|
290
|
+
files, **run-state advancement and akm's domain journal are no longer atomic** β a crash between
|
|
291
|
+
the two writes leaves them inconsistent, and akm's gate-spine invariant ("every step advances
|
|
292
|
+
through `completeWorkflowStep`... so the summary-validation gate and run-state derivation stay
|
|
293
|
+
authoritative", `run-workflow.ts` header) would now straddle two durability domains.
|
|
294
|
+
|
|
295
|
+
The escape hatch is to implement the `Backend` interface over akm's own tables β it *is* public via
|
|
296
|
+
`openworkflow/internal` (`internal.ts` re-exports `core/backend.js`). But that is **~20 methods** of
|
|
297
|
+
subtle claim/lease/transaction semantics; the reference SQLite implementation is ~35 KB. You would
|
|
298
|
+
be writing more durability code than you deleted.
|
|
299
|
+
|
|
300
|
+
Separately, `step_attempts` cannot absorb `workflow_run_units`: akm's unit rows carry `tokens`,
|
|
301
|
+
`model`, `runner`, `engine`, `phase`, `worktree_path`, `session_id`, `last_checkin_at`,
|
|
302
|
+
`input_hash`, `parent_unit_id` (`migrations.ts:929-953`). OpenWorkflow's step attempts model none
|
|
303
|
+
of these, so akm keeps its unit table regardless β meaning the two systems would journal the same
|
|
304
|
+
work twice.
|
|
305
|
+
|
|
306
|
+
### What gets replaced vs orphaned
|
|
307
|
+
|
|
308
|
+
`src/workflows/exec` = **7,070** LOC, `src/workflows/ir` = **1,635** LOC; **total 8,705**.
|
|
309
|
+
|
|
310
|
+
**Replaced (deletable):**
|
|
311
|
+
- Engine lease acquire/renew/release in `run-workflow.ts` (~80)
|
|
312
|
+
- Resume/journal-reuse logic in `native-executor.ts` β `classifyUnitReuse`, `reuseCompletedUnit`,
|
|
313
|
+
`dispatchJournaledAttempt` bookkeeping (~300)
|
|
314
|
+
- Step-advance/resume orchestration in `run-workflow.ts` (~170)
|
|
315
|
+
- Row-selection/terminality helpers in `step-work.ts` β `selectUnitAttemptRow`,
|
|
316
|
+
`unitStillNeedsReport`, `isWorkListFullyTerminal`, part of `isRetryEligibleFailure` (~120)
|
|
317
|
+
|
|
318
|
+
**β 670 LOC β 8% of exec+ir.** Even on a generous reading β akm abandoning its own unit journal
|
|
319
|
+
and accepting `step_attempts` as the sole record, which would forfeit token/model/worktree/session
|
|
320
|
+
tracking β it does not exceed **~12%**.
|
|
321
|
+
|
|
322
|
+
**Orphaned / kept (the other ~88-92%):**
|
|
323
|
+
- **All of `ir/` (1,635, 0% deletable)** β markdownβIR compile, freeze, plan-hash, params, schema.
|
|
324
|
+
OpenWorkflow has no plan compiler and no concept of a frozen plan.
|
|
325
|
+
- `report.ts` (1,977) and `brief.ts` (713) β driver protocol; the transport changes to signals but
|
|
326
|
+
the work-list computation, validation, and merge logic stay.
|
|
327
|
+
- `step-work.ts` (~1,400 of 1,536) β work-list computation, unit ids, input hashes, prompt
|
|
328
|
+
assembly, reducers, routing, gate loops, artifact schema validation.
|
|
329
|
+
- `native-executor.ts` (~1,000 of 1,334) β agent-CLI/LLM dispatch, env bindings, secret redaction,
|
|
330
|
+
frozen runner materialization.
|
|
331
|
+
- `scheduler.ts` (118) β kept in full; OpenWorkflow has no concurrency caps.
|
|
332
|
+
- `frozen-judge.ts`, `workflow-engine-gate.ts`, `param-secrets.ts`, `worktree.ts`,
|
|
333
|
+
`unit-dispatch.ts`, `unit-writer.ts` (578 total) β all kept.
|
|
334
|
+
|
|
335
|
+
**And new code is added:** a second-DB adapter (or a ~20-method custom `Backend`), a
|
|
336
|
+
plan-interpreter workflow function, per-step `Promise.race` timeouts, error-classification
|
|
337
|
+
wrappers, and a dual-journal reconciliation path. **Net LOC is plausibly negative** β i.e. adopting
|
|
338
|
+
it likely means *more* code in akm, not less.
|
|
339
|
+
|
|
340
|
+
## 8. Risks
|
|
341
|
+
|
|
342
|
+
- **Abandonment β MODERATE-HIGH.** One maintainer with 663 of 682 human commits; the next-largest
|
|
343
|
+
contributor has 3. The May 2026 trough (15 commits, 3-month release gap) shows the project tracks
|
|
344
|
+
one person's availability. 9 dependent repos means little community pressure to keep it alive.
|
|
345
|
+
- **API instability β MODERATE-HIGH.** 0.9.2 after 25 releases in 9 months, with two deprecations
|
|
346
|
+
already visible in the shipped surface (`succeeded` status, `declareWorkflow`). Four features akm
|
|
347
|
+
needs β concurrency controls, manual retry (#520), OTEL (#501), custom serializers (#506) β are
|
|
348
|
+
unshipped, so the surface will keep moving in exactly the areas akm depends on.
|
|
349
|
+
- **Commercial/open-core β LOW-MODERATE.** The README advertises "OpenWorkflow Cloud" managed
|
|
350
|
+
hosting, so there is a commercial entity behind the OSS. Apache-2.0 is irrevocable for shipped
|
|
351
|
+
versions, but open-core projects tend to route features toward the hosted product, and
|
|
352
|
+
"Priority and concurrency controls" is precisely the kind of feature that lands there first.
|
|
353
|
+
- **Ecosystem consolidation β MODERATE.** Vercel shipped a directly competing TypeScript durable-
|
|
354
|
+
execution library (`vercel/workflow`, "Workflow Development Kit", public beta) with far greater
|
|
355
|
+
resourcing. A solo-maintained competitor in the same niche is at elevated risk of losing
|
|
356
|
+
mindshare and contributors.
|
|
357
|
+
- **Security β LOW.** Zero runtime dependencies (minimal supply-chain surface), fully parameterized
|
|
358
|
+
SQL, no `eval`/`new Function`/`child_process` in the library, no network I/O of its own. The one
|
|
359
|
+
filed "security disclosure" (#482) is a verified false positive (see Β§6). Note the library will
|
|
360
|
+
execute *any* registered workflow whose name appears in a run row β akm must keep treating the
|
|
361
|
+
plan as untrusted input, exactly as it does today.
|
|
362
|
+
- **License β NONE.** Apache-2.0 dependency in an MPL-2.0 project is clean: MPL-2.0 is file-level
|
|
363
|
+
copyleft and akm merely consumes the library. Apache-2.0 imposes attribution and, if upstream
|
|
364
|
+
ever adds a `NOTICE`, redistribution of that notice (there is none today). Apache-2.0 also grants
|
|
365
|
+
an express patent license, which is a mild plus. No relicensing of akm files is implied.
|
|
366
|
+
- **Capacity β MODERATE.** The 1,000-step-attempt hard cap vs akm's 10,000-unit expansion is a real
|
|
367
|
+
ceiling on large fan-outs, with child workflows as the only escape.
|
|
368
|
+
- **Operational UX β LOW-MODERATE.** The fixed, unconfigurable 30s lease imposes a measured
|
|
369
|
+
18-30s stall on post-crash CLI resume.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Recommendation
|
|
374
|
+
|
|
375
|
+
**Do not adopt as a replacement, and do not adopt as an underpinning.**
|
|
376
|
+
|
|
377
|
+
OpenWorkflow is a well-built library β the crash-resume semantics are correct and verified, Bun
|
|
378
|
+
support is genuine and CI-tested, the engineering hygiene is better than most projects ten times
|
|
379
|
+
its age, and it has no dependencies. If akm were starting from zero and needed durable execution,
|
|
380
|
+
it would be a reasonable candidate.
|
|
381
|
+
|
|
382
|
+
But akm is not starting from zero, and the overlap is in the wrong place. OpenWorkflow supplies the
|
|
383
|
+
generic durable-execution core β lease, memoized replay, retry/backoff, status machine β which is
|
|
384
|
+
roughly 8-12% of `exec+ir`. Everything that makes akm's engine large is domain logic OpenWorkflow
|
|
385
|
+
has no opinion about: the markdownβIR compiler, the frozen plan, judge gates with bounded feedback
|
|
386
|
+
loops, unit dispatch to agent CLIs, artifact promotion and schema validation, worktrees, secret
|
|
387
|
+
redaction, and the brief/report driver protocol. Meanwhile adoption *subtracts* three capabilities
|
|
388
|
+
akm currently has (in-flight unit cancellation, concurrency caps, retry error classification),
|
|
389
|
+
imposes a 10x lower fan-out ceiling, and forces run state into a second database file that breaks
|
|
390
|
+
the single-transaction invariant akm's gate spine is built on.
|
|
391
|
+
|
|
392
|
+
**Worth stealing instead of adopting:** the `available_at`-as-both-queue-and-lease trick that makes
|
|
393
|
+
one column serve as job queue, sleep timer, and crash detector (`ARCHITECTURE.md`); the automatic
|
|
394
|
+
`name:1`/`name:2` disambiguation for dynamic fan-out with the documented "use a stable ID for
|
|
395
|
+
mutable collections" caveat (`apps/docs/docs/dynamic-steps.mdx`) β akm's `unitIdFor` already does
|
|
396
|
+
something similar but the failure mode is worth reviewing; and the 100%-coverage gate scoped to the
|
|
397
|
+
`core/**` directory only (`vitest.config.ts:22-27`), which is a nice pattern for holding a
|
|
398
|
+
durability kernel to a higher bar than the surrounding code.
|
|
399
|
+
|
|
400
|
+
**Reconsider if** the project reaches 1.0 with a stable API, gains a second sustained maintainer,
|
|
401
|
+
ships intra-workflow concurrency controls (#20) and configurable leases, and adds either a SQLite
|
|
402
|
+
table prefix or a supported way to inject an existing database handle.
|