fix(events): address Codex confirmation review — strengthen /goal guard, fix doc drift
Second-round Codex review of the PR-A taxonomy change found four issues, all verified against the code: - The /goal regression guard asserted only that the steered content reached requests[1], which passes even with the hasSteering override (loop.ts) disabled: leftover steering is re-enqueued as a next-turn queued message and also lands in requests[1], one turn later. The guard now asserts the same-turn shape — ONE turn, TWO steps, a steering/message recorded before step 2 — which is the mechanism the override drives. Proven to fail red with the override disabled. - The event-domain-semantics RFC's consequence list still described the pre-fix behavior (step marked open AFTER step/start, so no step/end owed). It now states the shipped behavior: the loop marks the step open BEFORE the append, so a throwing step/start listener gets a balancing step/end via closeStep(). - architecture.md's loop pseudocode said only continuation listeners force continuation; step/end session-event listeners (the /goal pattern) do too. - The agent/turn-end JSDoc listed a `rejected` TurnEndReason that does not exist on this branch (it belongs to the later interception work). Removed it and regenerated the cordis catalog; `interrupted` (a real variant) stays.
This commit is contained in:
@@ -151,7 +151,8 @@ forever:
|
||||
drain steering → session('steering/message'); emit agent/steering
|
||||
session('step/end') ⟵ durable step boundary (no agent/* mirror)
|
||||
cont = waterfall agent/turn-continuation(default = hadToolCalls || steered)
|
||||
steering pending from continuation listeners forces cont = true
|
||||
steering pending forces cont = true (from continuation listeners OR from
|
||||
step/end session-event listeners — the /goal pattern; hasSteering override)
|
||||
if !cont: break
|
||||
session('turn/end'); emit agent/turn-end
|
||||
await ctx.parallel('session/flush', session) ⟵ durability checkpoint (failure
|
||||
|
||||
@@ -49,7 +49,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:244`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:245`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/queued` — emit
|
||||
|
||||
@@ -73,7 +73,7 @@ Waterfall: mutate the fully-assembled GenerateOptions before the model call (hoo
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [GenerateOptions](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:213`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:214`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/status` — emit
|
||||
|
||||
@@ -97,7 +97,7 @@ Steering content was injected into a running turn.
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:238`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:239`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/step-result` — waterfall
|
||||
|
||||
@@ -109,7 +109,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:219`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:220`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/stream-chunk` — emit
|
||||
|
||||
@@ -121,7 +121,7 @@ A raw StreamChunk arrived from the model (token-level UI/log feed).
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:233`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:234`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/turn-continuation` — waterfall
|
||||
|
||||
@@ -133,11 +133,11 @@ Waterfall: override the turn-continuation decision. The default (computed by the
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:226`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:227`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/turn-end` — emit
|
||||
|
||||
A turn ended. `reason` distinguishes a clean stop from a truncated, aborted, or hook-rejected one (`completed` | `aborted` | `error` | `disposed` | `max-tokens` | `rejected` | `interrupted`).
|
||||
A turn ended. `reason` distinguishes a clean stop from a truncated, aborted, failed, disposed, or crash-interrupted one (`completed` | `aborted` | `error` | `disposed` | `max-tokens` | `interrupted`); the reason union is merge-extensible, so a plugin can add further variants.
|
||||
|
||||
```ts cordis-catalog
|
||||
'agent/turn-end'(agent: Agent, turn: number, reason: TurnEndReason): void
|
||||
@@ -145,7 +145,7 @@ A turn ended. `reason` distinguishes a clean stop from a truncated, aborted, or
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [TurnEndReason](../core-data-structures/session.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:204`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:205`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
#### `agent/turn-start` — emit
|
||||
|
||||
|
||||
@@ -34,6 +34,6 @@ This is the foundational change in a stack that adds a Hooks subsystem; it estab
|
||||
|
||||
- The loop no longer emits `agent/step-start`/`agent/step-end`; `closeStep` appends `step/end` only, and a throwing `step/end` session-event listener is the surviving step-boundary-listener failure path (contained by `closeStep` → `failTurn`, the turn closes balanced).
|
||||
- Tests that observed step boundaries via the removed emits now observe the durable `step/start`/`step/end` session events — the behavior they pin (boundary ordering, step counting, a throwing boundary listener failing the turn balanced) is unchanged; only the feed they read moved to the canonical one. Per [AGENTS.md "tests document behavior, not golden truth"](../../../../AGENTS.md), the behavior and its test moved together.
|
||||
- One behavior genuinely shifts and is documented in its test: a throwing `step/start` session-event listener throws INSIDE `session.append('step/start')`, before the loop marks the step open, so no `step/end` is owed (the old `agent/step-start` emit fired after the step was open). The turn still closes balanced with an error.
|
||||
- The loop marks the step open (`stepOpen = true`) BEFORE appending `step/start`, because `Session.append` pushes the event to the log before notifying `session/event` listeners (validation throws happen earlier, before the push — see [the session append contract](../../../core-data-structures/session.md)). So a throwing `step/start` session-event listener runs with the step already open and the event already in the log: the loop's outer catch then calls `closeStep()`, which appends the balancing `step/end`, and the turn closes balanced with an error (`turn/start → step/start → step/end → turn/end` — verified by the invariants oracle in the regression test). Closing the open step is owed precisely because the marker is set first.
|
||||
- This is a partial, conservative realization of the broader [proposed simplification "Stop mirroring durable boundaries as agent events"](../../proposed/simplification/2026-06-20-remove-agent-boundary-mirror-events.md): that RFC proposes removing ALL boundary mirrors (including the turn boundaries and `agent/steering`) and migrating the stdio UI's turn rendering onto `session/event`. This RFC removes only the two step mirrors that have no live consumer; the turn mirrors stay until the stdio UI is migrated. The proposed RFC remains the home for finishing that migration.
|
||||
- The cordis catalog (`docs/cordis-catalog/events-and-services.md`) is regenerated to drop the two events.
|
||||
|
||||
Reference in New Issue
Block a user