## 4.22.2 — independent live parallel Pause waits

- Park only the waiting branch, route resumes by exact token and optional node, reject duplicate/conflicting answers, and join once after all branches.
- Abort all native waits without fabricating answers; release pending timeout handles. Preserve serial Pause and cold-recovery authority boundaries.

# VL Workflow Engine Feature Checklist

This checklist records the current user-facing feature surface that must survive future refactors and integrations.

- Workflow DAG rendering: node layout, connections, legend, and PNG export
- Node interaction: drag/drop, selection, context menu, copy node ID, view details
- Node editing: modal edit forms, rerun-from-step, variable overrides, acceptance review modal
- Runtime control: run, pause, stop, checkpoint restore, SSE streaming, node status updates
- Workflow import/export: JSON import, JSON export, workflow load from URL, workflowImported postMessage
- Bundle support: direct `.vflow` open, `.vflow` save/save-as, bundle context sidebar, node list sidebar
- Bundle integration: `.vflow` read/write, `.vl` attachment support, bundle tree/file inspection, validation hooks
- Host integration: ready postMessage, loadWorkflow/loadBundle/saveBundle messaging, workflowBundleSaved broadcast

## Core Runtime

- Workflow validation for supported spec versions 3.6 through 4.1
- Step execution for Service, API, Component, Actor, Subflow, Review, GraphPatch, Score, Grow, Channel, Topology, LLM, Set, Write, Download, Unzip, Pause, Branch, Loop, Iteration, Stop, and Noop
- Alias behavior for Fork, Check, Done, ChildRun, and SpawnChildRun
- Checkpoint and resume through `Engine.executeFrom()`
- Runtime capability manifest through `engine.getCapabilities()`
- Event stream using schema `vl.workflow.run-event.v4`

## Dynamic Graph And Loop Runtime

- `engine.applyGraphPatch()` with upsert, remove, setNext, setField, appendChildren, and replaceChildren operations
- GraphPatch step execution and checkpoint replay
- Source-mode parallel Loop execution
- Parallel Loop `spawnSibling(item)` dynamic branch growth
- Parallel Loop `exitBranch(reason)` and `next: "EXIT_BRANCH"` branch exit
- Checkpoint v3 `loopState` persistence
- Runtime graph snapshot through `engine.getRuntimeSnapshot()` and `buildRuntimeGraphSnapshot()`

## Host Integration

- Actor step contract and actor event stream
- `ResultEnvelope` storage and propagation
- `ResultEnvelope` artifact/result handoff routing contract (`vl.result-handoff.v1`)
- Child run / subflow request propagation
- Multi-flow collaboration contract on child-run requests (`vl.multi-flow.collaboration.v1`)
- Step-level `tools` and `toolScope`
- Review / HumanGate acceptance flow
- Host SDK exports
- Preflight and StepGuard checks

## Bundles And Tooling

- Bundle and `.vflow` bundle exports
- Workflow capability matrix and API documentation
- Test suite coverage through `npm test`

## Fixed in 4.21.11

- Live/checkpoint graphs expose every started parallel branch from existing recovery frames instead of only the last shared currentStepID. Queued branches are pending; completed branches are done; a new loop iteration overrides prior completion in its current node status. This changes projection only, preserving execution and recovery semantics.
- Actual Engine fixtures cover concurrency2/3, independent branch completion, checkpoint parity and loop re-entry. Old projection fails all three regressions.

## Fixed in 4.21.3

- **Exact release identity** (`lib/types.js`): the exported `WORKFLOW_RELEASE_VERSION` and capability manifest now match package version `4.21.3`; the existing breakpoint version gate prevents future package/runtime drift. Workflow Spec remains `4.1`, and all DAG execution, trusted routing, checkpoint/resume, host SDK, bundle, editor and visual behavior remain intact.

## Fixed in 4.21.1

- **Run-local transaction cache** (`lib/trusted-dag.js`): verified node
  candidates remain in a private cache overlay until final E2E acceptance.
  E2E rejection leaves durable cache empty and cannot publish an authoritative
  segment commit.
- **Verified versus committed lifecycle**: a segment contract PASS emits
  `segment_verified`; `segment_committed` is emitted only after all
  cross-segment invariants and the final E2E contract PASS. Signed receipts are
  carried by `verification_passed`, and real `node_started` events make the
  lifecycle observable to VL Agent hosts.
- **Regression proof**: focused Trusted DAG coverage is **41/41**, including
  E2E failure cache/commit isolation and receipt/start event assertions. Full
  workflow-engine remains **15 suites / 0 failed**. Existing default fail-fast,
  threshold quarantine, SESE validation, access control, schema/check policy,
  ConstrainedDAGRouter, Subflow/Loop, cache revalidation, checkpoint/resume,
  Workflow 3.6–4.1, Host SDK, bundle, editor, and visual DAG behavior remain
  intact.

## Added in 4.21.0

- **Explicit threshold branch quarantine** (`lib/trusted-dag.js`): parallel
  internal nodes remain fail-fast by default. A node may use
  `onBranchFailure:"quarantine"` only inside a SESE segment with an explicit
  `branchPolicy:{mode:"threshold",nodes,minimumSuccessful}`. Rejected branches
  wait for sibling settlement, publish no writes, retain structured failure
  evidence, and emit `branch_quarantined`; the Join runs only when the declared
  verified-branch threshold is met. Entry/exit quarantine, malformed policies,
  and insufficient successes fail closed. Join, segment, invariant, and E2E
  signed PASS requirements remain intact.
- **Regression proof**: focused Trusted DAG coverage is **36/36**, including
  one-valid/one-rejected branch commit, quarantined-write isolation, and
  insufficient-threshold rejection. Full workflow-engine remains **15 suites /
  0 failed**. Existing default fail-fast parallelism, SESE validation,
  disjoint-write merge, Builder/Repairer/Verifier/Aggregator routing,
  Subflow/Loop, cache, checkpoint/resume, bundles, Host SDK, editor, Workflow
  3.6–4.1 behavior, and visual UI remain intact.

## Added in 4.20.0

- **Segment Contract v2 and signed verifier authority** (`lib/json-schema.js`, `lib/verifier-registry.js`, `lib/segment-contract-v2.js`): versioned contracts now require minimal `reads`/`writes`, fail-closed JSON Schema 2020-12 subset validation, stable checks with `all|any|threshold` policy, bounded failure action/attempt/time/token/cost controls, authority-signed digest-pinned `verifierRef`, and Ed25519 receipts binding run/layer/subject/contract/input/output/evidence/access/cache provenance. Unsupported schema keywords, external `$ref`, undeclared access, unavailable/tampered verifiers, unsigned receipts, and `cache.reverify:false` are rejected.
- **Trusted multi-node intelligent DAG runtime** (`lib/intelligent-router.js`, `lib/trusted-dag.js`): `TrustedDAGExecutor` validates real multi-node SESE regions, runs independent branches concurrently against isolated snapshots, rejects overlapping parallel writes, verifies a Join before segment commit, recursively supports trusted `Subflow`, bounds and verifies `Loop` body nodes plus loop invariants, and requires node → segment → cross-segment invariant → final E2E acceptance receipts. Constrained routing selects only registered topology and exact Builder/Worker/Repairer/Verifier/Aggregator identities; bounded repair, failure reason codes, anti-oscillation, evidence-based route statistics, and digest-bound reverified cache are included.
- **Product consumption and visual proof** (`@vl/engine-workflow-runtime` 0.3.0, `GraphRunner.runTrustedGraph()`, `examples/workflows/trusted-intelligent-dag.visual.json`, `examples/run-trusted-dag-demo.js`): the same ordinary Workflow 4.1 nodes remain visualizable while the trusted profile supplies stronger execution semantics. The runnable demo proves parallel Builders → verified Join → bounded Repair Loop → trusted Subflow → E2E acceptance with ephemeral signing keys and no committed secret. New focused suite passes **31/31** and the full engine passes **15 suites / 0 failed**.
- **Benchmark provenance repair** (`scripts/benchmark-segments.mjs`): git metadata now resolves from the declared VL-Base repo directory instead of the undefined `REPO_ROOT`; smoke evidence reports the exact commit, `main` branch, production dirty state, tracked harness, and engine version instead of `unknown`. Existing Workflow Spec 3.6–4.1 execution, Strict Segment v1 ABI, all canonical step families and aliases, checkpoint/resume, dynamic GraphPatch/Loop behavior, bundles, Host SDK, editor interactions, and visual UI remain intact.

## Added in 4.19.2

- **Strict Segment candidate/verify/commit boundary** (`lib/segment-contracts.js`, `lib/engine.js`, `lib/expression.js`, `lib/types.js`, `index.js`): an opt-in `segmentContract` executes supported pure-state steps in an isolated candidate context, binds contract/input/candidate/verifier digests into a verification request, and commits only an exact `PASS` receipt. `FAIL`, `UNKNOWN`, malformed/mismatched receipts, verifier errors/timeouts/unavailability and `onError:ignore` all fail closed before authoritative mutation. Strict expressions cannot reach ambient `process`, `globalThis` or `Function`; effectful/custom/file-output paths are rejected by validation.
- **Receipt-bound recovery and cache**: accepted receipts are emitted before `step_done`, persisted in checkpoint v4 and attached to cache entries; cache hits reverify by default before restoring candidate output. New suite passes **57/57**, all **14 suites / 0 failed**; workflows without `segmentContract`, existing step semantics, checkpoint compatibility, bundles and editor behavior remain intact.

## Fixed in 4.19.1

- **Object-literal expressions that are valid JS but not valid JSON now evaluate instead of silently becoming `undefined`** (`lib/expression.js`, `package.json`, `test/run.js`): `evaluate()` tried `JSON.parse` on a `{...}` expression and, on failure, fell through to `_resolveVariable()`. That path returns `undefined` for an unknown identifier **without throwing**, so the sandboxed JS-scope fallback below it was never reached — every authored object literal such as `={ topic: topic }`, `={ from: _item, ok: true }`, `={...$state, status: 'x'}`, or `={"userGoal": (userIntent || 'fallback')}` silently evaluated to `undefined`, and because `JSON.stringify` hides `undefined` values the affected step ran with the input key simply gone and no error anywhere. 56 shipped workflow files author exactly this shape. The `{...}` branch now attempts `_evalViaScope('(expr)')` after strict JSON fails, so full-JS object literals (shorthand keys, spread, ternaries, `||` fallbacks, `_item`/`$var` references) evaluate with the same lenient identifier surface used by IIFEs. Added a regression test covering shorthand keys, `_item` locals, spread-merge, falsy-param `||` fallback, and the strict-JSON / `={}` fast paths. All 13 test suites green (108 core assertions). Strict-JSON objects, arrays, IIFEs, operators, ternary, path navigation, and missing-variable→undefined behavior remain intact.

## Fixed in 4.16.3

- **Set/Write deep-evaluate expressions nested in object/array values + `now()` builtin** (`lib/executor.js`, `lib/expression.js`, `lib/types.js`, `package.json`, `test/run.js`, `test/breakpoint.js`): `executeSetStep`/`executeWriteStep` used `evaluateValue` (top-level string only), so authored values like `{ "archivedAt": "=now()" }` or `Write` payload objects containing `"=$var"` were persisted as literal strings — 5 shipped Agent-OS workflows author exactly this shape and silently stored template text. Both steps now use `evaluateDeep` (top-level string behavior unchanged, `==` escape preserved). Separately `now()` was not a recognized root callable and evaluated to `undefined` (dropping the key from the result object); added `now` → ISO-8601 timestamp to `ROOT_GLOBALS`. Added 2 regression tests (Set/Write nested deep-eval) + verified `now()` end-to-end; all 11 test suites green (105 core assertions). Existing expression surface, `out:` deep-set mapping, Write modes, and step semantics remain intact.

## Fixed in 4.16.2

- **Expression evaluator: standard JS globals + parenthesized method-call fallback** (`lib/expression.js`, `lib/types.js`, `package.json`, `test/run.js`, `test/breakpoint.js`): the custom path-evaluator (`_resolveRootValue`) previously whitelisted only `Math`/`JSON` as root globals, so `String()/Number()/Boolean()/Object/Array/Date/RegExp/parseInt/parseFloat/isNaN/isFinite` silently returned `undefined` outside an IIFE — breaking authored gate conditions such as `=!!($x && String($x).trim())` (every such gate evaluated false). Added a `ROOT_GLOBALS` allowlist of safe, pure value/constructor globals (no I/O, no `process`/`require`/`eval`). Separately, method calls on parenthesized expressions like `=($childRun.sourceFilesWritten || []).some(f => ...)` threw `Invalid callable path` because the parser only models `name.field.method()` shapes; the IIFE scope-eval was refactored into a reusable `_evalViaScope()` and is now used as a fallback whenever the custom path parser throws, so full-JS gate/transform expressions evaluate correctly with the same lenient identifier surface. Added regression tests (root globals + parenthesized `.some()/.filter()` fallback). All 11 test suites remain green; existing IIFE evaluation, simple `$var.field[i]` paths, operators, ternary, and missing-variable→undefined behavior remain intact.
- **WorkflowSpec 4.1 host profile now documents Agent App DAG Shell and Node Capsule authoring without changing engine semantics** (`docs/workflow-spec-4.1.md`, `docs/workflow-spec-4.1-full.md`, `docs/workflow-host-capability-boundary.md`, `docs/workflow-capability-matrix.md`): added the SysDoc document-version `4.1.6` Host Profile that keeps topology in ordinary workflow steps, stores Shell/Capsule logic as Event Panel events/AST, treats step `in/out` as runtime fact checked against `contract.schema`, keeps node skills inline under `resources.skills[]`, keeps author samples separate from control-plane evidence/artifact/error, and requires host fail-closed validation for schemas, tool permissions, and dry-run before writing success state. Existing workflow `version:"4.1"`, supported step families, Event schema `vl.workflow.run-event.v4`, checkpoint/resume, VisualLogic custom handler, DAG-complexity steps, multi-flow collaboration, result handoff routing, and all runtime APIs remain intact.

## Fixed in 4.17.2

- **Stop steps now close their step lifecycle before ending the run** (`lib/engine.js`, `test/run.js`): `case 'Stop'` previously set `ExecutionStatus.Stopped` and emitted only `workflow_done` — no `step_done`, no `completedSteps` entry, no checkpoint — so every consumer projecting node state from the event stream or the final checkpoint left the Stop node stuck at `step_start`/running forever after a successful run (Agent-OS 验收 AGT-004 的「Stop 节点仍显示 running」根因). Stop now emits `step_done` (with collected outputs), pushes itself into `ctx._completedSteps`, and emits a final checkpoint (whose `currentStepID` is the Stop step) BEFORE `workflow_done`, preserving event ordering. Updated the `onCheckpoint fires after each step` regression to expect the Stop checkpoint and assert `Stop_End ∈ completedSteps`. All 11 test suites pass; Pause/Branch/Loop/child-branch checkpoint semantics, resume via `executeFrom`, and `workflow_done` payload (`stop_id`, `duration_ms`) remain intact.

## Added in 4.17.1

- **`collectResourceManifest` now collects `tools`** (`lib/preflight.js`): Tool_* host custom-handler steps (getStepType returns null for them — handled explicitly) + LLM step `tools` allow-lists. This makes a flow's tool dependencies a first-class manifest fact so hosts can verify them at install/run time (C1 checked-dependency contract). All 11 suites pass; existing manifest keys unchanged (additive).

## Added in 4.17.0

- **clean-1 `.vflow` bundle format finalized** (`lib/vflow-bundle.js`, `test/test-bundle.js`, spec docs 4.1/4.1-full + capability matrix/boundary): entry `workflow/main.json` (was `main.workflow.json`), `support/**` → `assets/{docs,prompts,schemas,images}`, `vl/` structured as `Apps|Sections|Services|Theme|ExtComponents`; legacy v0/v1.0-engine layouts accepted ONLY by parse/migration helpers, new builds + strict validation always clean-1. This commits the previously-uncommitted working-tree migration (taken over 2026-07-08 as part of the unified base/resource-format convergence — Agent-OS was already executing this code via symlink). All 11 test suites pass. All existing engine features remain intact.

## Added in 4.16.1

- **DAG-as-tools dynamic selection profile and demo** (`.vl-code/memory/MEMORY.md`, `docs/dag-as-tools-design.md`, `docs/workflow-capability-matrix.md`, `docs/workflow-host-capability-boundary.md`, `docs/workflow-spec-4.1-full.md`, `examples/workflows/dynamic-dag-tool-selection.json`, `examples/run-dag-tools-demo.js`, `examples/README.md`, `test/run.js`, `test/breakpoint.js`, `package.json`, `lib/types.js`, source release headers): documented and demonstrated the recommended `Actor selector -> GraphPatch slot replacement -> Subflow DAG-tool -> downstream parent step` pattern, with selector access to `ctx.snapshot()` and `engine.getRuntimeSnapshot(ctx)`. Existing step execution for Service/API/Component/Actor/Subflow/Review/GraphPatch/Score/Grow/Channel/Topology/LLM/Set/Write/Download/Unzip/Pause/Branch/Loop/Iteration/Stop/Noop, workflow-of-workflows, dynamic graph patching, result envelopes, runtime graph snapshots, Host SDK exports, bundle support, UI rendering, and `npm test` coverage remain intact.

## Added in 4.6.1

- **Versionless workflow resources** (`lib/engine.js`, `dist/workflow-editor-lite/workflows/`, `examples/workflows/`): Workflow JSON resources can omit pinned spec versions while the engine still uses the current runtime contract internally and continues to reject explicitly unsupported legacy version values.

## Added in 4.7.0

- **Runtime graph snapshot** (`lib/runtime-snapshot.js`, `lib/engine.js`): Hosts can read a stable `vl.workflow.runtime-graph.v1` projection through `engine.getRuntimeSnapshot()` or `buildRuntimeGraphSnapshot()` without mutating execution state.

## Added in 4.8.0

- **ResultEnvelope full coverage** (`lib/executor.js`, `lib/types.js`): every data step (Service, Component, Actor, Subflow, Review, GraphPatch, LLM, API, Set, Write, Download, Unzip, Pause) now emits a `ResultEnvelope` by default through `materializeStepResultEnvelopeIfNeeded`; previously envelope creation was gated on `step.resultEnvelope` config or output-mapping references. Engine declares `features.resultEnvelopeCoverage === 'full'` alongside the existing `resultEnvelopeContract`. Control-flow steps (Branch, Loop, Stop, Noop, Swarm) do not pass through output mapping and remain envelope-free by design.

## Fixed in 4.9.1

- **Capability manifest release assertions** (`lib/types.js`, `index.js`, `test/run.js`): the exported release constant, package version, source headers, and capability manifest tests now stay aligned so `npm test` validates the current release instead of a stale `4.7.0` literal.

## Added in 4.12.0

- **Native DAG-complexity step types + meta-flows** (`lib/types.js`, `lib/executor.js`, `lib/scorer.js`, `lib/grower.js`, `lib/topology.js`, `test/{score-grow,channel,topology,iteration,meta-flow}.js`): five new canonical step families, all real executors with dedicated test suites.
  - **`Score`** — inference-time scoring of a node / candidate list / trajectory into a `0..1` score + `keep`/`prune`/`expand` decision from weighted signals vs thresholds (`lib/scorer.js`).
  - **`Grow`** — scored dynamic node growth turned into `GraphPatch` ops, with optional live `apply` through `engine.applyGraphPatch` (`lib/grower.js`).
  - **`Channel`** — bidirectional cross-DAG FIFO message channels shared across parallel branches (`send`/`recv`/`drain`/`peek`), checkpoint-serialized; emits `channel_send`/`channel_recv`.
  - **`Topology`** — adaptive agent/edge selection + pruning via the Scorer (`select` top-K agents, `prune-edges`) (`lib/topology.js`).
  - **`Iteration`** — bounded `generate → test → fix` container aliasing `Loop` (`maxRounds`/`until`/`while`; both `while`+`until` rejected).
  - **Meta-flows** — `SpawnChildRun.workflow` may be a computed expression resolving a runtime-generated workflow object (`features.workflowOfWorkflows`).

## Added in 4.13.0

- **Phase B — retry telemetry + opt-in result cache** (`lib/engine.js`, `lib/types.js`, `test/retry-cache.js`): new additive events `step_retrying` (`{ attempt, maxAttempts, error, retryDelayMs }`) and `step_cached` (`{ cacheKey, outputs }`, an opt-in per-step result cache), under the unchanged `vl.workflow.run-event.v4` schema.
- **Release version alignment** (`lib/types.js`): `WORKFLOW_RELEASE_VERSION` corrected to `4.13.0` (it had regressed to `4.11.0` when the Phase B merge overwrote it; `package.json` was already `4.13.0`). Spec number stays `4.1`.

## Added in 4.14.0

- **Interact step — self-contained `.vflow` interaction** (`lib/executor.js`, `lib/types.js`, `docs/workflow-spec-4.1.md`, `test/run.js`): additive `interact` block on `Pause` (`buildInteractPayload`). A Pause carrying `interact` still emits `PauseStart` (legacy) and additionally emits a channel-agnostic `interact_requested` carrying `title`/`prompt`/`choices`/`selectMode`/`media`/`channel`/`fallback`/`ui`/`expireAt`, so Flow-tab RUN and AI-Assistant invoke render identically. Preferred `channel` defaults to `aiAssistant`; an automatic `fallback` chain (always ending in `popup`) means an absent AI Assistant never strands a run. `selectMode` defaults to `single` when `choices` exist, else `freeform`. On resume, sets `resumeResultTarget` and emits `interact_resolved` (`resolvedBy:'human'`). `timeoutSec` + `defaultDecision` let an unattended/headless run auto-resolve on timeout (`resolvedBy:'timeout-default'`, `timeoutAction:'interact-default'`) and continue down `step.next` instead of hanging. Legacy `humanTask`/`reason` steps are lifted into the same interact shape. New events `RunEventType.InteractRequested`/`InteractResolved` and capability flag `features.interactStep === true`, under the unchanged `vl.workflow.run-event.v4` schema.
- **Lenient IIFE expression scope** (`lib/expression.js`): IIFE evaluation runs under `with(Proxy)` so a bare identifier that is not a real JS global resolves to `undefined` (matching the `$var` path) instead of throwing `ReferenceError`; genuine throws inside the IIFE still propagate.
- **Resume default re-materialization** (`lib/engine.js` `executeFrom`): declared param defaults dropped by the checkpoint JSON round-trip are re-materialized, so resumed runs see the same param surface as the original run.

## Added in 4.15.0

- **First-class multi-flow collaboration + result handoff routing** (`lib/executor.js`, `lib/engine.js`, `lib/types.js`, `test/run.js`): Subflow/ChildRun/SpawnChildRun steps can declare a versioned `collaboration`/`multiFlow` contract (`vl.multi-flow.collaboration.v1`) that is passed to workflow adapters, stored in child-run metadata, exposed on child-run events, and injected into local child flow system variables. Data steps can declare `handoff`/`resultHandoff` routes; full-coverage `ResultEnvelope` materialization stores them under `metadata.handoff`/`metadata.routing` (`vl.result-handoff.v1`) and includes route summaries on `result_envelope_stored` events for host AI Assistant / surface / database routing.

## Added in 4.16.0

- **Breakpoints — pause-on-entry at pre-marked steps** (`lib/engine.js`, `lib/types.js`, `test/breakpoint.js`, `scripts/run-all-tests.mjs`): step IDs supplied via `runParams.breakpoints` cause the run to pause *before* a matching step executes — `ctx.status = Paused`, emits new event `breakpoint_hit` (`{ breakpointAt }`) alongside `workflow_paused` (`{ pausedAt, reason:'breakpoint' }`) so existing paused-state UI lights up unchanged, then checkpoints and returns. Breakpoints survive resume because they live in `runParams` (checkpointed). Resume via the normal `engine.executeFrom(ctx.checkpoint())` path, which performs a one-shot release (`_breakpointReleased`) of the resumed-from step so it does not immediately re-pause; any *other* breakpoint, or a later loop back onto the same step, still pauses as expected. New event `RunEventType.BreakpointHit`, under the unchanged `vl.workflow.run-event.v4` schema. (No new `features.*` flag was added — gate on the `breakpoint_hit` event / `runParams.breakpoints` support.)
