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
1 change: 1 addition & 0 deletions .github/allowed_signers
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
admilsoncossa@gmail.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP9aimezLMuhjkApDOumfx/GXhW4IwdEw79kLzlaIHZ7 admilsoncossa@gmail.com
admilsoncossa@gmail.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxMA5TIcWbUzpPXkmwc2xQfFRmFGyokIuegaF86Ydum admilsoncossa@gmail.com WorkIt signing 2026
3 changes: 3 additions & 0 deletions .github/workflows/release-provenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ jobs:
- name: Verify coverage
run: npm run test:coverage

- name: Verify release evidence readiness
run: npm run check:release-readiness

- name: Verify release tag
if: inputs.dry_run == 'false'
shell: bash
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ SPDX-License-Identifier: Apache-2.0

## Unreleased

## 0.6.0

- Add the explicit `@workit/core/candidates` subpath with deterministic
`firstAcceptable()` selection, provider-neutral failure dispositions, typed
semantic quality decisions, existing retry/deadline/budget reuse, and bounded
redacted candidate-attempt evidence.
- Add conservative built-in taxonomy for WorkIt cancellation, timeout, and
retry-budget exhaustion while leaving unknown provider failures under the
caller's explicit classifier policy.
- Keep real cancellation authoritative, return terminal and user-input stops as
exhaustive typed results, and leave root runtime exports unchanged.
- Add ESM, CommonJS, strict TypeScript, unsupported-runtime, unit, property, and
installed-package coverage for candidate policy contracts.
- Add a manifest-driven evidence runner, per-claim actual-result capture in the
ignored coverage directory, and a source-digest ledger gate that rejects
missing, failing, unregistered, or stale evidence.

## 0.5.0

Add runtime deadline introspection, shared retry admission limits, and bounded
Expand Down
11 changes: 9 additions & 2 deletions 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.5.0},
version = {0.6.0},
license = {Apache-2.0}
}
```
Expand All @@ -87,13 +87,16 @@ owned by `packages/core`.

The monorepo layout must not change how users install or import WorkIt.

Stable consumer paths for this release line:
Consumer paths in the `0.6.0` release candidate are listed below. The new
candidate-policy subpath is not part of npm `latest` until the signed `v0.6.0`
tag and provenance-backed publish complete.

```txt
@workit/core
@workit/core/activity
@workit/core/ai
@workit/core/analysis
@workit/core/candidates
@workit/core/channel
@workit/core/contracts
@workit/core/diagnostics
Expand All @@ -118,6 +121,10 @@ npm run check:size
npm run check:package-consumer
```

The `0.6.0` release candidate's candidate-policy subpath is documented in the
[`@workit/core` package README](packages/core/README.md#candidate-selection).
It remains unavailable from npm `latest` until the release is published.

Run the site gates from the repository root:

```sh
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.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "workit",
"version": "0.5.0",
"version": "0.6.0",
"private": true,
"description": "WorkIt monorepo.",
"type": "module",
Expand Down Expand Up @@ -34,6 +34,7 @@
"check:public-proof": "npm --workspace @workit/core run check:public-proof",
"check:worker-contract": "npm --workspace @workit/core run check:worker-contract",
"check:release-policy": "npm --workspace @workit/core run check:release-policy",
"check:release-readiness": "npm --workspace @workit/core run check:release-readiness",
"check:release": "npm --workspace @workit/core run check:release",
"bench:articles": "npm --workspace @workit/core run bench:articles",
"bench:articles:repeated": "npm --workspace @workit/core run bench:articles:repeated",
Expand Down
120 changes: 111 additions & 9 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ ownership helpers live behind explicit subpaths.
| 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, file, and caller-owned SQL 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 |
| Select the first semantically acceptable candidate | `@workit/core/candidates` | sequential candidate policy, not implicit racing or durable idempotency |
| 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 |
| Plan declared retry, hedge, timeout, deadline, series, and parallel time bounds | `@workit/core/time-policy` | conservative planning over declared policies, not wall-clock execution proof |
Expand All @@ -332,6 +333,97 @@ deduplication record. Use `@workit/core/activity` with a caller-owned store for
terminal replay across process restarts, and `@workit/core/ledger` when lifecycle
receipts must be persisted independently.

### Candidate Selection

> **0.6.0 release candidate:** this subpath is present in the prepared package,
> but it is not included in npm `latest` until the signed `v0.6.0` tag and
> provenance-backed publish complete.
>
> Publication is also blocked by evidence claim `REL-011` until Oryn passes a
> real integration canary at its provider and durable-idempotency boundaries.
> The bounded fixtures in this repository do not satisfy that claim.

`@workit/core/candidates` separates transport success, semantic quality, and
failure policy. Candidates run sequentially in caller order. WorkIt's built-in
taxonomy handles its own cancellation, timeout, and retry-budget errors first.
The caller classifier owns unknown failures such as provider responses and
decides whether to retry the same candidate, advance, stop terminally, or
require user input. Real WorkIt cancellation always throws and cannot be
converted into fallback.

```ts
import {
classifyWorkItFailure,
firstAcceptable,
} from "@workit/core/candidates";
import { TimeoutError } from "@workit/core";

const result = await firstAcceptable([primaryModel, fallbackModel], {
execute: (model, ctx) => model.generate({ signal: ctx.signal }),
accept: (value) => value.confidence >= 0.8
? { accepted: true }
: { accepted: false, reasonCode: "confidence_too_low" },
classifyFailure: (error) => isTransient(error)
? { disposition: "retry_same_candidate", reasonCode: "transient_provider_failure" }
: { disposition: "try_next_candidate", reasonCode: "candidate_unavailable" },
retry: { times: 2, initialDelay: "100ms" },
maxCandidates: 2,
evidence: { maxAttempts: 8 },
candidateMetadata: (_model, candidateIndex) => ({ candidateIndex }),
});

if (result.status === "accepted") {
useResult(result.value);
}

// The same conservative taxonomy is available for policy inspection.
const knownFailure = classifyWorkItFailure(new TimeoutError(1_000));
```

`classifyWorkItFailure()` returns these stable built-in decisions:

| Error | Decision | `reasonCode` | `firstAcceptable()` behavior |
|---|---|---|---|
| `CancellationError` | `cancelled` | `workit_cancelled` | rethrows the original cancellation; no fallback is admitted |
| `TimeoutError` | `terminal` | `workit_timeout` | returns a terminal result; no fallback is admitted |
| `BudgetExceededError` | `terminal` | `workit_budget_exhausted` | returns a terminal result; no fallback is admitted |
| unknown/provider error | `undefined` | caller-defined | invokes `classifyFailure` exactly once for the normalized decision |

The result is an exhaustive discriminated union:

| `status` | Meaning | Additional fields |
|---|---|---|
| `accepted` | first transport-successful value accepted by the quality predicate | `candidate`, `candidateIndex`, `value` |
| `exhausted` | no candidate produced an acceptable value and no policy stop occurred | none |
| `terminal` | a built-in or caller terminal decision stopped the chain | `reasonCode`, original `error` |
| `requires_user_input` | caller policy stopped for approval or missing input | `reasonCode` |

Every result includes bounded `evidence` and `droppedEvidence`. Each evidence
entry records candidate index, attempt, outcome, decision, optional reason code,
timing, normalized error, and redacted metadata.

`deadlineAt` is one absolute aggregate deadline for the entire candidate chain,
not a fresh timeout per candidate. Every admitted task context observes the same
effective deadline. When it expires, the result is terminal and later candidates
are not admitted.

Quality rejection is recorded as a successful transport attempt with the
separate `quality_rejected` decision. Evidence uses the existing attempt
recorder, validates lowercase bounded reason codes, bounds and redacts metadata,
truncates error text, and reports dropped attempts. Defaults admit at most 16
candidates and retain at most 256 evidence entries; larger candidate lists must
raise `maxCandidates` explicitly, up to the hard cap of 1,000. Configuration is
rejected when `candidateCount * retry.times` exceeds 10,000 admitted attempts.

The helper does not race candidates and does not make side effects idempotent.
Use caller-owned idempotency for operations that may be repeated by retries or
fallback. `retryIf` is intentionally not accepted in the retry option because
`classifyFailure` is the single retry admission policy.

Exceptions thrown by `accept`, `classifyFailure`, or `candidateMetadata` are
configuration/callback failures and are not silently converted into fallback.
WorkIt cancellation thrown by a callback remains authoritative.

### Attempt Evidence

For a scheduled task wrapped by `run.retry()`, the outer retry boundary emits
Expand Down Expand Up @@ -671,9 +763,9 @@ thresholds, not exact milliseconds.

| Evidence | Current result |
|---|---:|
| Unit tests | 375 passing |
| Coverage gate | 100% statements, branches, functions, lines |
| Evidence proof files | 22 passing |
| Unit and property tests | 403 passing |
| Coverage gate | 100% statements (2,902/2,902), branches (1,837/1,837), functions (688/688), lines (2,797/2,797) |
| Evidence proof files | 30 passing / 56 executable claims captured |
| Runtime dependencies | 0 |
| Article benchmark suite | 19/19 passing |
| Core group import | 13,807 B minified / 4,842 B gzip |
Expand All @@ -688,7 +780,7 @@ Representative article-benchmark results:
|---|---:|---:|
| Provider race losers after winner | losers continue until their sleeps finish | losers cancelled in scope close |
| Retry after cancellation | 7 extra attempts, 622 ms latency | 0 extra attempts, 1 ms latency |
| Context `.with()` over 5,000 keys | 31.68 ms | 0.014 ms |
| Context `.with()` over 5,000 keys | 31.253 ms | 0.011 ms |
| 1B-row source, take 25 | 281 items pulled | 40 items pulled |
| Sampling volume | 1,300 events | 36 events |

Expand All @@ -699,10 +791,11 @@ npm run verify
```

`npm run verify` runs type-checking, header and test hygiene, unit tests,
security checks, vulnerability audit, SBOM validation, API and bundle-size
locks, runtime benchmarks, stream and soak gates, exporter stress,
package-consumer fixtures, public-proof validation, worker-contract checks,
release-policy checks, and `npm pack --dry-run`.
manifest-driven evidence proofs, source-digest ledger validation, security
checks, vulnerability audit, SBOM validation, API and bundle-size locks,
runtime benchmarks, stream and soak gates, exporter stress, package-consumer
fixtures, public-proof validation, worker-contract checks, release-policy
checks, and `npm pack --dry-run`.

Run the article benchmark suite:

Expand Down Expand Up @@ -758,6 +851,15 @@ Supported:
- Next.js route-shaped handlers
- Express, Fastify, tRPC, and Vercel AI SDK fixtures

Compatibility-tested, but outside the primary Node.js support commitment:

- Bun `1.3.13` installed-package fixture
- Deno `2.2.7` installed-package fixture

These fixtures prove the tested package surface on the pinned versions. They do
not broaden the documented primary runtime target beyond Node.js server
runtimes.

Unsupported today:

- browser client runtime
Expand Down Expand Up @@ -852,7 +954,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.5.0},
version = {0.6.0},
license = {Apache-2.0}
}
```
Expand Down
1 change: 1 addition & 0 deletions packages/core/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ provenance enabled. A release is not approved unless these commands pass:
```sh
npm run verify
npm run test:coverage
npm run check:release-readiness
npm run check:vulnerabilities
npm run check:sbom
npm pack --dry-run --json
Expand Down
19 changes: 17 additions & 2 deletions packages/core/benchmarks/public-proof.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
"npm run check:stream-memory",
"npm run check:soak",
"npm run check:package-consumer",
"npm run check:claims"
"npm run check:claims",
"npm run test:evidence",
"npm run check:evidence-ledger",
"npm run check:candidates-api",
"npm run check:candidates-performance",
"npm run check:candidate-scenarios"
],
"benchmarkFixtures": [
{
Expand Down Expand Up @@ -55,6 +60,16 @@
"status": "supported",
"evidence": "npm run check:package-consumer"
},
{
"runtime": "Bun",
"status": "pinned compatibility fixture; not primary support contract",
"evidence": "npm run check:package-consumer"
},
{
"runtime": "Deno",
"status": "pinned compatibility fixture; not primary support contract",
"evidence": "npm run check:package-consumer"
},
{
"runtime": "Express",
"status": "supported fixture",
Expand All @@ -73,7 +88,7 @@
{
"runtime": "Next.js route",
"status": "supported fixture",
"evidence": "npm run check:claims"
"evidence": "npm run check:package-consumer"
},
{
"runtime": "Vercel AI SDK handler",
Expand Down
23 changes: 23 additions & 0 deletions packages/core/evidence/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ runtime source + npm run verify
benchmarks/articles/run-all.mjs
tests/evidence/run-all.mjs
evidence/claims.json
coverage/evidence/latest.json (temporary, ignored)
-> README, articles
```

Expand All @@ -38,12 +39,20 @@ the impact and invariant are security-relevant.
npm run verify
npm run bench:articles
npm run test:evidence
npm run check:evidence-ledger
```

`benchmarks/results/articles.latest.json` stores the captured article benchmark
run used by README and articles for representative values. The benchmark
assertions remain the portable proof.

`test:evidence` rebuilds the package, executes every proof declared in
`tests/evidence/manifest.mjs`, and writes the per-claim actual results to the
ignored `coverage/evidence/latest.json` artifact. The capture includes a SHA-256 digest over the
claim ledger, package contract, API snapshots, runtime source, and executable
evidence. `check:evidence-ledger` rejects missing claims, missing proof files,
unregistered evidence scripts, failing results, or a stale digest.

## Evidence Stack

| Layer | Source of truth | Role |
Expand All @@ -53,10 +62,24 @@ assertions remain the portable proof.
| Captured bench run | `benchmarks/results/articles.latest.json` | representative publication values for this revision |
| Claim ledger | `evidence/claims.json` | claim IDs, class, proof path, invariant, status, and limitation |
| Evidence tests | `tests/evidence/run-all.mjs` | curated lifecycle, correctness, security, release, and performance proofs |
| Temporary captured results | `coverage/evidence/latest.json` | ignored environment, source digest, elapsed time, and actual result used by the current verification run |

## Publication Rule

README summarizes. Articles teach. Neither invents claim status. Public prose
must cite one of the executable sources above, and security claims must stay
security-specific rather than using "security" as a label for every adversarial
or lifecycle proof.

Historical tags are never rewritten: later backfills remain explicitly labeled
as backfills in the ledger.

## Oryn 0.6.0 Canary

`oryn-candidate-canary.v0.6.0.json` is the redacted external-integration receipt
for `REL-011`. It binds the packed `0.6.0` tarball hash, WorkIt and Oryn commits,
real provider routing decisions, daemon-backed receipt round-trip, durable replay,
and controlled retry-budget, deadline, and user-input-stop scenarios. The receipt
retains environment warnings and limitations; it contains neither provider response
bodies nor credentials. `release/oryn-candidate-canary.mjs` validates this receipt
as part of `test:evidence`, and the evidence-source digest includes the receipt.
Loading