Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ SPDX-License-Identifier: Apache-2.0

# Changelog

## Unreleased

## 0.2.0

Add the core ownership and evidence foundation as explicit `@workit/core`
subpaths. The root import remains unchanged.

- Add `@workit/core/replay` for lifecycle receipt recording and redaction.
- Add `@workit/core/ledger` for memory and file receipt ledgers.
- Add `@workit/core/analysis` for receipt and caller-provided protocol
verification.
- Add `@workit/core/activity` for explicit terminal activity boundaries.
- Add `@workit/core/resources` for lazy, shared, and scope-owned resource
helpers.
- Add package-consumer coverage for the new subpaths across ESM, CommonJS, and
strict TypeScript fixtures.
- Add executable evidence for receipts, ledgers, analysis, activity terminal
replay, and resource ownership.
- Keep the root `@workit/core` bundle size unchanged.
- Improve npm package discoverability with targeted package keywords and a more
specific package description.
- Clarify npm README examples for retry policies, `TaskFn` invocation,
`renderTree(scope.status())`, and `work().do()` fail-fast output.
- Normalize activity results before persistence so first execution and replay
return the same JSON value.
- Derive the OpenTelemetry instrumentation version from package metadata.
- Document the buffered `work().do()` contract and cooperative cancellation
boundary for hedged work.

## 0.1.5

Move `@workit/core` to `packages/core` monorepo layout. No public API changes.
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cite the software release you used:
title = {WorkIt: A TypeScript Structured Concurrency Runtime for Node.js Server Runtimes},
year = {2026},
url = {https://github.com/WorkRuntime/workit},
version = {0.1.5},
version = {0.2.0},
license = {Apache-2.0}
}
```
Expand All @@ -91,11 +91,16 @@ Stable consumer paths for this release line:

```txt
@workit/core
@workit/core/activity
@workit/core/ai
@workit/core/analysis
@workit/core/channel
@workit/core/diagnostics
@workit/core/ledger
@workit/core/observability
@workit/core/otel
@workit/core/replay
@workit/core/resources
@workit/core/worker
```

Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "workit",
"version": "0.1.5",
"version": "0.2.0",
"private": true,
"description": "WorkIt monorepo.",
"type": "module",
Expand Down
107 changes: 92 additions & 15 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,24 @@ Changelog: <https://github.com/WorkRuntime/workit/blob/main/CHANGELOG.md>
```ts
import { work } from "@workit/core";

const doubled = await work([1, 2, 3])
const output = await work([1, 2, 3])
.inParallel(2)
.do(async (value, _ctx) => value * 2);

const doubled = output.results;
```

The context parameter is available when the task needs cancellation, progress,
budgets, or scoped resources. It can be ignored for plain transformations.

> [!IMPORTANT]
> `work().do()` defaults to fail-fast mode. If any item fails, the call throws
> and cancels sibling work. On success it returns `{ mode: "fail", results }`,
> not a bare array. Use `.onError("continue")` or `.onError("collect")` when
> callers need per-item failures in the returned value. `work().do()` buffers
> the source before execution; use `.stream()` for unbounded or very large
> async iterables.

## Why Ownership Matters

Consider this batch helper:
Expand Down Expand Up @@ -232,6 +242,55 @@ Rules:
| OpenTelemetry bridge | `@workit/core/otel` |
| Agent helper contracts | `@workit/core/ai` |

### Task Functions And Invocation

The resilience helpers `run.timeout()`, `run.retry()`, `run.fallback()`,
`run.deadline()`, `run.hedge()`, `run.bracket()`, `run.circuitBreaker()`, and
`run.uncancellable()` return a `TaskFn<T>`. A `TaskFn` is a description of owned
work; run it through `group()`/`task(...)` or `scope.spawn(...)`.

```ts
import { group, run } from "@workit/core";

const chargeWithRetry = run.retry(
run.timeout(
(ctx) => chargeCustomer(invoice, { signal: ctx.signal }),
"5s"
),
{ times: 3 }
);

const receipt = await group(async (task) => task(chargeWithRetry));
```

`renderTree()` takes a scope snapshot, not a live scope. Use
`renderTree(scope.status())`.

```ts
import { renderTree, run } from "@workit/core";

await run.scope(async (scope) => {
const handle = scope.spawn((ctx) => fetchProfile({ signal: ctx.signal }));

console.log(renderTree(scope.status()));

return handle;
});
```

## Evidence And Ownership Subpaths

The root import remains focused on the runtime primitives. Evidence and
ownership helpers live behind explicit subpaths.

| Need | Subpath | Boundary |
|---|---|---|
| Build lifecycle receipts from scope events and snapshots | `@workit/core/replay` | audit evidence, not deterministic scheduler replay |
| Persist receipts in caller-owned stores | `@workit/core/ledger` | memory and file receipt ledgers, not a database framework |
| Verify receipts and caller-provided protocol specs | `@workit/core/analysis` | bounded verification over supplied evidence, not whole-program analysis |
| Record explicit terminal activity boundaries | `@workit/core/activity` | completed activity replay, not in-flight workflow recovery |
| Compose lazy, shared, and scope-owned resources | `@workit/core/resources` | cleanup ownership through WorkIt scopes, not automatic resource detection |

## Common Use Cases

These are short entry points. The full narrative and benchmark discussion live
Expand Down Expand Up @@ -275,24 +334,41 @@ The first success wins. Losing branches receive `CancelReason { kind:
### Retry With Timeout

```ts
import { run } from "@workit/core";

const receipt = await run.retry(
(ctx) =>
run.timeout(
(timeoutCtx) =>
chargeCustomer(invoice, {
signal: AbortSignal.any([ctx.signal, timeoutCtx.signal]),
}),
"5s"
),
{ retries: 3 }
import { group, run } from "@workit/core";

const chargeWithRetry = run.retry(
run.timeout(
(ctx) => chargeCustomer(invoice, { signal: ctx.signal }),
"5s"
),
{ times: 3 }
);

const receipt = await group(async (task) => task(chargeWithRetry));
```

The retry policy, timeout, and caller cancellation share one owned execution
path instead of living in separate helper layers.

### Hedged Work

`run.hedge(task, policy)` starts delayed duplicate attempts and returns the
first successful attempt. Non-winning attempts receive an aborted `ctx.signal`.
Task bodies and any resources they acquire must observe that signal or install
their own bounded cleanup; JavaScript cannot preempt non-cooperative work that
ignores cancellation.

```ts
import { group, run } from "@workit/core";

const rerank = run.hedge(
(ctx) => reranker.score(candidates, { signal: ctx.signal }),
{ after: "75ms", max: 2 }
);

const scores = await group(async (task) => task(rerank));
```

### Backpressured Stream

```ts
Expand Down Expand Up @@ -379,8 +455,9 @@ thresholds, not exact milliseconds.

| Evidence | Current result |
|---|---:|
| Unit tests | 221 passing |
| Unit tests | 299 passing |
| Coverage gate | 100% statements, branches, functions, lines |
| Evidence proof files | 14 passing |
| Runtime dependencies | 0 |
| Article benchmark suite | 19/19 passing |
| Core group import | 14,175 B minified / 4,835 B gzip |
Expand Down Expand Up @@ -559,7 +636,7 @@ cite the software release you used:
title = {WorkIt: A TypeScript Structured Concurrency Runtime for Node.js Server Runtimes},
year = {2026},
url = {https://github.com/WorkRuntime/workit},
version = {0.1.5},
version = {0.2.0},
license = {Apache-2.0}
}
```
Expand Down
110 changes: 110 additions & 0 deletions packages/core/evidence/claims.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,56 @@
"expectedInvariant": "a hanging cleanup emits task:cleanup_timeout and the owner settles",
"limitations": "The timeout bounds WorkIt cleanup waiting; external systems still need their own timeout and idempotency policies."
},
{
"id": "LIFE-004",
"title": "replay receipts record completed scope closure",
"class": "lifecycle",
"status": "proven",
"proof": "tests/evidence/lifecycle/replay-receipts.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a closed successful scope has completed terminal outcome and no leaked tasks",
"limitations": "Receipts are audit evidence over observed WorkIt events and snapshots; they are not deterministic scheduler replay."
},
{
"id": "LIFE-005",
"title": "replay receipts preserve typed cancellation reason",
"class": "lifecycle",
"status": "proven",
"proof": "tests/evidence/lifecycle/replay-receipts.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a manually cancelled scope records the cancellation reason observed by owned work",
"limitations": "The reason is evidence captured from WorkIt-owned cancellation; external systems still need their own abort support."
},
{
"id": "LIFE-006",
"title": "shared scope resource acquires and releases once",
"class": "lifecycle",
"status": "proven",
"proof": "tests/evidence/lifecycle/resource-ownership.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "parallel tasks using one shared helper observe one acquisition and one scope-owned release",
"limitations": "This proves WorkIt helper ownership and cleanup registration; external resource semantics remain application responsibility."
},
{
"id": "LIFE-008",
"title": "file activity store replays completed activity after restart",
"class": "lifecycle",
"status": "proven",
"proof": "tests/evidence/lifecycle/activity-restart.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a fresh file activity store returns the persisted terminal result without rerunning the activity body",
"limitations": "This is terminal activity replay for explicit activity ids; it is not arbitrary in-flight crash recovery or workflow replay."
},
{
"id": "LIFE-011",
"title": "resource ownership can be audited across cleanup paths",
"class": "lifecycle",
"status": "proven",
"proof": "tests/evidence/lifecycle/resource-audit.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "explicit resource audit instrumentation records acquired/released/pending resources while WorkIt emits cleanup timeout and failure events",
"limitations": "Resource audit entries are explicit caller instrumentation; WorkIt supplies cleanup failure and timeout events through the scope event surface."
},
{
"id": "CORR-001",
"title": "budget inputs are immutable boundary values",
Expand Down Expand Up @@ -87,6 +137,56 @@
"expectedInvariant": "unbounded retry counts are rejected at the policy boundary",
"limitations": "The cap prevents accidental huge retry policies; application-level retry budgets may be stricter."
},
{
"id": "CORR-007",
"title": "receipt analysis detects leaked owned work",
"class": "correctness",
"status": "proven",
"proof": "tests/evidence/correctness/analysis-verifiers.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a non-terminal receipt with pending tasks fails analysis with leaked_tasks",
"limitations": "The verifier analyzes receipt evidence supplied to it; it does not observe work that was never captured."
},
{
"id": "CORR-012",
"title": "explicit activity boundary preserves terminal evidence",
"class": "correctness",
"status": "proven",
"proof": "tests/evidence/correctness/activity-boundary.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "completed activity returns the stored result on repeat and changed input conflicts before rerun",
"limitations": "The boundary records explicit terminal activity evidence; callers remain responsible for idempotent external side effects."
},
{
"id": "CORR-013",
"title": "receipt verifier proves declared lifecycle evidence",
"class": "correctness",
"status": "proven",
"proof": "tests/evidence/correctness/analysis-verifiers.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a closed receipt with terminal and cleanup evidence verifies no orphaned owned tasks while preserving cleanup timeout warning evidence",
"limitations": "Verification is bounded to the receipt and snapshot supplied by the caller."
},
{
"id": "CORR-015",
"title": "bounded resource model preserves cleanup balance invariant",
"class": "correctness",
"status": "proven",
"proof": "tests/evidence/correctness/resource-ownership-model.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "for linear, lazy, and shared resources, every terminal modeled scope releases exactly the resources it acquired",
"limitations": "This is a bounded model for WorkIt resource helper contracts, not a proof over arbitrary JavaScript programs or external resources."
},
{
"id": "CORR-017",
"title": "source protocol analysis detects missing ownership edges",
"class": "correctness",
"status": "proven",
"proof": "tests/evidence/correctness/source-protocol-analysis.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "bounded source protocol specs pass when WorkIt ownership edges are present and fail with stable findings when ownership edges are missing",
"limitations": "This verifies caller-provided protocol specifications, not arbitrary TypeScript or JavaScript source."
},
{
"id": "SEC-001",
"title": "worker offload rejects remote and executable URL schemes",
Expand Down Expand Up @@ -137,6 +237,16 @@
"expectedInvariant": "release-provenance script contains signed-tag verification logic",
"limitations": "A dry-run gate cannot guarantee a future maintainer will sign a tag; it verifies that the repository policy gate exists."
},
{
"id": "REL-004",
"title": "file receipt ledger persists append-only receipt evidence",
"class": "release",
"status": "proven",
"proof": "tests/evidence/release/receipt-ledger.mjs",
"command": "npm run test:evidence",
"expectedInvariant": "a receipt appended by one ledger instance is readable from a new ledger instance",
"limitations": "This proof covers WorkIt's file-backed receipt ledger, not database durability or distributed queue semantics."
},
{
"id": "PERF-001",
"title": "article benchmark suite has expected executable coverage",
Expand Down
Loading