@sanity/workflow-engine-test 0.10.0 → 0.11.0

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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # @sanity/workflow-engine-test
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4e2b5bb: **BREAKING:** the bench now mirrors the engine's read/write surface exactly instead of paraphrasing it.
8
+ - `editDocument` performs the real write for every action it accepts, so guards scoped to that action are genuinely consulted: `publish` promotes the existing draft onto the published id and `unpublish` retracts it (both through the fake client's document actions, vetted by the guard seam under that action); previously both silently landed as a plain update and a guard scoped to `actions: ['publish']` was never consulted. `create` is no longer accepted — create through the client, which the same seam gates. The return type is now honest: `StoreDocument | undefined` (`undefined` after `delete`/`unpublish`, or when the default identity can't read the written doc) instead of a cast that crashed at runtime.
9
+ - Read helpers sharing an engine verb's name now delegate to that verb with the engine's args shape: `getInstance`/`listPendingEffects` (was `pendingEffects`)/`findPendingEffects` (new)/`children`/`instancesForDocument`/`definitionsForDocument` all take the engine's args objects, and missing/foreign-tag instances throw the engine's typed `InstanceNotFoundError` instead of a bench-local `Error`.
10
+ - `instancesByStage` speaks the engine's filter vocabulary: `definition` (was `workflowName`) and `includeCompleted` (was `openOnly` — polarity AND default flip: in-flight only unless opted in).
11
+ - `deleteDefinition` joins the engine-call wrappers with the same `actor:`/`grants:` identity shortcuts as every other write verb — no more routing around the bench via `createBenchEngine`.
12
+
13
+ `@sanity-labs/client-fake-for-test` ^0.8.0 (the document publish/unpublish guard-interception seam) is now a regular dependency — the bench has always imported it at runtime, but it was previously declared only as a devDependency, so an external install got a broken package.
14
+
15
+ - 6a46db1: **BREAKING:** the definition root's `applicableWhen` moved to `start.filter`, inside a new `start` block — `{kind?: 'interactive' | 'autonomous', filter?: GROQ}` — describing how standalone runs begin. No alias, no dual-read: definitions authored with `applicableWhen` fail the strict parse until rewritten.
16
+ - `start.kind` classifies who initiates a run — a person from a start surface (`'interactive'`, the default; `startKindOf(def)` is the one reading rule) or a system reacting to a document (`'autonomous'`, hidden from human start pickers). Classification only: `startInstance` stays one verb and one code path for every caller and kind — an interactive start of an autonomous workflow is legal. Deploy invariants: a spawn-only (`lifecycle: 'child'`) definition may not declare `start`; an `'autonomous'` kind requires every required input entry to be subject-style (`doc.ref`/`doc.refs` — the shared `isSubjectEntry` rule); a `start.filter` may read only declared **input-sourced** entries via `$fields` (a typo or a query/literal entry is a silent never-pass); and a root-reading filter (`_type == …`, `@`, or a `^` escaping a scan) requires a subject entry, since a read surface with no subject never binds a root.
17
+ - `start.filter` is a **read-side visibility rule, not a gate** — `startInstance` never evaluates it and never throws on it; it starts whenever the caller supplies the required inputs. The filter decides what appears in / is chosen from Start lists: `definitionsForDocument`/applicability and the Studio start control evaluate it in a widened, named start-filter context (`START_FILTER_VARS`) — the candidate document as root, `$tag`/`$definition`/`$now`/`$fields` bound (`$fields` = the caller's input entries as GDR envelopes, so `$fields.<entry>.id` is the GDR URI), and `*[...]` = the workflow resource's dataset. Filters that never scan keep the cheap pure evaluation (`analyzeCondition(...).readsDataset` decides); scanning filters evaluate over a fetched slice of EVERY instance in the tag (completed included — `*` carries no hidden predicate; filters qualify in-flight themselves with `!defined(completedAt)`), so a filter can bound what surfaces (WIP limits, one-run-per-subject). Absent bindings evaluate GROQ-null and fail closed.
18
+
19
+ - e5f5b77: **BREAKING:** actions become the only payload mechanism — every op, effect, and spawn in the system lives on an action; structure never does.
20
+ - Actions gain `when` (cascade-fired trigger: the engine fires it the moment it's true, at most once per stage visit, level-triggered; never fireAction-able) and `spawn` (the subworkflow block, unlocking caller-triggered and conditional spawn). Fires-on-entry is spelled `when: 'true'`.
21
+ - Deleted from the model: `activation`, activity-level `ops`/`effects`/`subworkflows`, `completeWhen`/`failWhen`, `kind`, and transition `ops`/`effects`. Transitions are pure edges `{name, when, to}` (the condition key renamed from `filter` — trigger semantics). `target` survives as the off-system marker; `kind` is now always derived, plus a new derived executor classification (autonomous / interactive / off-system / hybrid) on activity evaluations.
22
+ - Activities are `active` from stage entry (no `pending` status, no activation moment, no machine-step implicit); a new deploy invariant requires every activity to have a reachable path to a terminal status.
23
+ - A cascade hop is ONE transaction: the current stage's triggered actions run to in-memory fixpoint, then transition selection, then a single CAS commit; chain hops stay separate commits. The per-visit `firedActions` ledger on the activity entry re-arms on re-entry (a stage resets on re-entry). The cascading token executes triggered actions; `roles` on a triggered action pins which identities may execute it (an incapable token leaves it armed).
24
+ - The context bag split: `$effects` now carries completed effects' outputs ONLY (derived from `effectHistory`), and the new `$context` carries the `startInstance` seed plus a parent's `spawn.context` handoff (the persisted `effectsContext` field renamed `context`; `startInstance`'s seed arg renamed accordingly). `$effectStatus` is unchanged.
25
+ - Spawn registry rows key per (activity entry, spawning action), so two spawn actions on one activity can never cross-adopt each other's children.
26
+ - `fireAction` rejects a `when` action (`cascade-fired` verdict); evaluation projects triggered actions as narratable (`triggered: true` + `whenInsight`), never as buttons.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [11e2d6f]
31
+ - Updated dependencies [7510fd5]
32
+ - Updated dependencies [01e8042]
33
+ - Updated dependencies [79c54db]
34
+ - Updated dependencies [6a46db1]
35
+ - Updated dependencies [35b9b85]
36
+ - Updated dependencies [e5f5b77]
37
+ - Updated dependencies [01e8042]
38
+ - @sanity/workflow-engine@0.16.0
39
+
3
40
  ## 0.10.0
4
41
 
5
42
  ### Minor Changes
package/README.md CHANGED
@@ -20,15 +20,18 @@ Sanity client, an all-access default actor, and a bench-owned clock. Every
20
20
  wrapped verb injects the bench's client, tag, workflow resource, access
21
21
  default, and clock, so a test reads as the workflow it exercises:
22
22
 
23
- - **Runtime verbs** — `deployDefinitions`, `startInstance`, `fireAction`,
24
- `editField`, `completeEffect` (and `completePendingEffect`, addressed by
25
- effect name), `tick`, `evaluate`.
23
+ - **Runtime verbs** — `deployDefinitions`, `deleteDefinition`, `startInstance`,
24
+ `fireAction`, `editField`, `completeEffect` (and `completePendingEffect`,
25
+ addressed by effect name), `tick`, `evaluate`.
26
26
  - **Instance-admin overrides** — `setStage`, `abortInstance`. Same injection,
27
27
  so a forced move into a deadline-gated stage evaluates `$now` at bench time,
28
28
  not wall-clock.
29
29
  - **Reads and probes** — `getInstance`, `currentStage`, `activityStatus`,
30
- `pendingEffects`, `children`, `instancesForSubject`, `instancesByStage`,
31
- `instancesForDocument`, `query`, plus the guard helpers below.
30
+ `listPendingEffects`, `findPendingEffects`, `children`, `instancesForSubject`,
31
+ `instancesByStage`, `instancesForDocument`, `definitionsForDocument`, `query`,
32
+ plus the guard helpers below. Helpers sharing an engine verb's name take the
33
+ engine's own args objects and throw its typed errors — bench reads and engine
34
+ reads cannot drift.
32
35
 
33
36
  ```ts
34
37
  const bench = createBench({now: '2026-01-01T00:00:00Z'})
@@ -59,7 +62,7 @@ ticking — never by waiting.
59
62
  ## Out-of-band engines
60
63
 
61
64
  Some tests need an engine of their own — custom effect handlers, racing two
62
- drainers, a `deleteDefinition` call. `createBenchEngine(bench, overrides?)`
65
+ drainers, a custom clock. `createBenchEngine(bench, overrides?)`
63
66
  builds one on the bench's client, inheriting its tag, workflow resource, and
64
67
  clock (`overrides` win). The bench exposes `tag` and `workflowResource` for
65
68
  raw `workflow.*` calls, and the default tag ships as the `BENCH_TAG` constant.
@@ -80,7 +83,12 @@ bench gives you a helper for each:
80
83
  writes through the bench client (`patch` is a `{set?, unset?, insert?}` spec),
81
84
  and a write a deployed guard denies throws `MutationGuardDeniedError` — the
82
85
  rejection the lake is designed to apply to _any_ client once guard
83
- enforcement ships; the bench reproduces it on the client it mints.
86
+ enforcement ships; the bench reproduces it on the client it mints. Every
87
+ guard action is a real write: `action: 'delete'` deletes, `'publish'`
88
+ promotes the existing `drafts.<id>` draft onto the published id, and
89
+ `'unpublish'` retracts it — so a guard scoped to `actions: ['publish']` is
90
+ genuinely consulted. The helper resolves to the post-write document, or
91
+ `undefined` once a delete/unpublish removed it.
84
92
  - **Read-time preflight** — `bench.activeGuardsForDocument(id)` answers "_would_
85
93
  a write be denied right now?" _without_ writing. This is the check a UI uses
86
94
  to disable a button or show a lock ahead of time. It's a pure read over the