From 484b5f5a06cfc459a2ff798782c78ba25b7ff0a3 Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 22 Jul 2026 17:44:31 +0800 Subject: [PATCH] docs(cordis-tutorial): say short-circuit instead of veto --- docs/cordis-tutorial/04-events.md | 16 ++++++++-------- docs/cordis-tutorial/index.md | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/cordis-tutorial/04-events.md b/docs/cordis-tutorial/04-events.md index fbc8eae441..4c14ba5313 100644 --- a/docs/cordis-tutorial/04-events.md +++ b/docs/cordis-tutorial/04-events.md @@ -77,7 +77,7 @@ Because `ctx.on()` is an effect, the listener disappears with the plugin — no ## Dispatch modes -`emit` is one of five dispatch modes. Which one an event uses is part of its contract — it decides whether listeners can return values, run concurrently, or veto each other: +`emit` is one of five dispatch modes. Which one an event uses is part of its contract — it decides whether listeners can return values, run concurrently, or short-circuit each other: | Mode | Call | Semantics | |---|---|---| @@ -89,9 +89,9 @@ Because `ctx.on()` is an effect, the listener disappears with the plugin — no Every harness event documents its mode in the generated [events catalog](../cordis-catalog/events.md). -## Waterfall: transform or veto +## Waterfall: transform or short-circuit -Waterfall is the mode that powers interception. Each listener receives the arguments plus a `next()` continuation; it can transform what `next()` returns, or refuse to call `next()` at all — the veto. Create `waterfall-demo.ts`: +Waterfall is the mode that powers interception. Each listener receives the arguments plus a `next()` continuation; it can transform what `next()` returns, or return without calling `next()` and short-circuit the rest of the chain — what the Cordis docs call the veto. Create `waterfall-demo.ts`: ```ts import type { Context } from 'cordis' @@ -111,9 +111,9 @@ export function apply(ctx: Context) { return downstream.toUpperCase() }) - // Listener 2: veto when it owns the decision. + // Listener 2: short-circuit when it owns the decision. ctx.on('demo/transform', async (input, next) => { - if (input.includes('blocked')) return '** vetoed **' + if (input.includes('blocked')) return '** blocked **' return next() }) @@ -128,12 +128,12 @@ Point `cordis.yml` at just this file and run: ``` HELLO -** VETOED ** +** BLOCKED ** ``` -Walk through the second line: listener 1 runs first, calls `next()`, which invokes listener 2; listener 2 sees `blocked` and returns without calling `next()` — the innermost default (the function passed to `ctx.waterfall`) never runs — and listener 1 uppercases the veto message on the way out. +Walk through the second line: listener 1 runs first, calls `next()`, which invokes listener 2; listener 2 sees `blocked` and returns without calling `next()` — the innermost default (the function passed to `ctx.waterfall`) never runs — and listener 1 uppercases the replacement message on the way out. -The discipline that follows: **a waterfall listener that only observes or annotates must call `next()`**; returning without it is a deliberate veto. Forgetting `next()` in a logging listener silently swallows the default behavior for everyone downstream. This is important enough that it is a standing rule of this repository ([waterfall semantics](../cordis-primer.md#cordis-waterfall-semantics)). +The discipline that follows: **a waterfall listener that only observes or annotates must call `next()`**; returning without it is a deliberate short-circuit. Forgetting `next()` in a logging listener silently swallows the default behavior for everyone downstream. This is important enough that it is a standing rule of this repository ([waterfall semantics](../cordis-primer.md#cordis-waterfall-semantics)). The harness uses waterfalls for decisions that cooperating plugins may wrap or answer: [`agent/request`](../cordis-catalog/events.md#agentrequest--waterfall) lets a plugin replace the model-call config, and [`approval/request`](../cordis-catalog/events.md#approvalrequest--waterfall) lets a policy answer instead of the user. diff --git a/docs/cordis-tutorial/index.md b/docs/cordis-tutorial/index.md index 243926f812..9cf3966117 100644 --- a/docs/cordis-tutorial/index.md +++ b/docs/cordis-tutorial/index.md @@ -36,7 +36,7 @@ That one-file launcher (see [vendor/cordis/bin.js](../../vendor/cordis/bin.js)) 1. [Your first plugin](01-first-plugin.md) — a plugin is a function; the loader mounts it. 2. [Lifecycle and effects](02-lifecycle-and-effects.md) — Cordis-managed registrations are undone when their plugin unloads. 3. [Services](03-services.md) — expose a capability on `ctx` and depend on it with `inject`. -4. [Events](04-events.md) — typed events, broadcast dispatch, and the waterfall veto. +4. [Events](04-events.md) — typed events, broadcast dispatch, and the waterfall short-circuit. 5. [Configuration](05-config.md) — validated config from `cordis.yml`, failing loud on bad input. 6. [Composition and HMR](06-composition-and-hmr.md) — the config file as a plugin tree, hot reload, and diagnosing a plugin that never loads. 7. [Into the harness](07-into-the-harness.md) — register a model-callable tool against real harness services.