Skip to content

fix(trace)!: TraceEvent lists every trace the framework raises, and tsc checks each raise against it (anima-research/agent-framework#899) - #944

Open
nissa-seru wants to merge 5 commits into
anima-research:mainfrom
nissa-seru:gideon2613/trace-union
Open

nissa-seru wants to merge 5 commits into
anima-research:mainfrom
nissa-seru:gideon2613/trace-union

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

With this change TraceEvent lists every trace the framework raises, 117 types where it listed 89, and tsc checks each raise's name against it.

The 28 added types include the three tune-out traces #899 names. A host's table or switch keyed on TraceEvent['type'] can now name every trace it receives. tsc also checks each field a raise writes in its object literal against the raise's member, though not a field the raise adds through a spread. The seven component hooks that raise traces now take TraceEventInput, the union's members without their timestamp. Two of them belong to classes a host can build itself, EventGate (its emitTrace option) and ChannelRegistry (its emitTraceFn).

Host code that compiled against fd6d6a36 stops compiling in these cases, each shown by a tsc run of a host-side probe (Tests):

  • a callback whose parameter is inferred from EventGate's emitTrace option, and that reads a field not every trace has without narrowing on type first (ChannelRegistry's emitTraceFn takes the same TraceEventInput, src/mcpl/channel-registry.ts:1585 at this head);
  • a switch over TraceEvent['type'] checked exhaustively with never, with no case for the 28 added types;
  • a read of a field that no kind of an mcpl:coalescing trace declares;
  • an augmentation of TraceEventBase by declaration merging, since it's now a type alias.

Two things change at run time. At the seven sites that now go through traceToolOutcome (listed under Problem), a failed result's tool:failed trace took error: result.error. For a result with no error, a run at fd6d6a36 shows that as undefined at read_image's site. The helper writes 'tool returned an error' there instead (src/framework.ts:35389), and tsc now holds that the field is a string. And the gate's own raises no longer write a timestamp.

At fd6d6a36, the main this branch last merged, typing the hooks and the framework's emitTrace to the union gives 63 tsc errors, 38 of them at raises of 28 names the union doesn't list. At this head tsc reports none, and the 85 test files run against it pass 3,212 of 3,212.

@Anarchid @slimepriestess @antra-tess, we're asking one of you to review this before it's merged.

Problem

#899, filed from @tel-0s's account and signed by FableDendrite, says tune-out:announced, tune-out:announce-failed and tune-out:wake-budget-paused are raised but missing from TraceEvent, so a host switching over the union exhaustively can't see them typed. At fd6d6a36, the main this branch last merged, the union lists 89 types, and 28 more are raised:

  • from the gate: gate:config-reconciled, gate:policy-added, gate:policy-removed and gate:shadow-warnings;
  • from MCPL channels: mcpl:channels-register, mcpl:channels-changed, mcpl:channels-incoming, mcpl:channel-descriptor-rejected, mcpl:channel-lazy-registered, mcpl:channel-opened-by-send, mcpl:channel-open-failed, mcpl:channel-open-substituted, mcpl:channel-reconcile-failed and mcpl:channel-reconcile-skipped;
  • from MCPL pushes, inference and state: mcpl:push_event, mcpl:inference_start, mcpl:inference_complete, mcpl:inference_fail, mcpl:state_registered, mcpl:checkpoint_recorded and mcpl:state_rollback;
  • from tune-out: tune-out:entered, tune-out:cancelled, tune-out:announced, tune-out:announce-failed, tune-out:ack-failed, tune-out:timer-failed and tune-out:wake-budget-paused.

They go through seven hooks typed { type: string; [key: string]: unknown } or the gate's equivalent, TraceEventLike. These are EventGate's emitTrace option (src/gate/event-gate.ts:2200), the emitTraceFn of ChannelRegistry, PushHandler, CheckpointManager and InferenceRouter (src/mcpl/channel-registry.ts:1584, push-handler.ts:124, checkpoint-manager.ts:206, inference-router.ts:199), FeatureSetManager's emitTrace (feature-set-manager.ts:139) and TuneOutFrameworkHooks.emitTrace (src/tune-out/coordinator.ts:319). Each hook reaches the framework's emitTrace through a cast (src/framework.ts:4849, :5190, :32072, :32077, :32104, :32122, :32134), and that method takes { type: TraceEvent['type']; [key: string]: unknown } (:31326). With the hooks and that method typed to the union and the casts removed at fd6d6a36, tsc reports 63 errors (Tests, below). 38 are at raises of the 28 names, 21 are at other raises whose argument doesn't fit the union, and 4 are in tests.

Raises of listed types didn't always match their members either. tool:failed's member declares error: string (src/types/trace.ts:327), and seven sites wrote it as ...(result.isError ? { error: result.error } : {}) (src/framework.ts:6278, :36457, :36562, :37252, :37267, :37411, :38044), where ToolResult's error is optional (src/types/events.ts:210). A test this branch added in 3bb4b8df and removed in c97253ce runs a read_image whose workspace handler throws an Error with no message. Run against fd6d6a36's src, its tool:failed trace has error undefined.

Changes

  • TraceEvent gains the 28 members, each with its fields as its raises give them (src/types/trace.ts). Fields that were raised but not declared are declared: gate:config-reloaded's removedPolicies and stateClearedPolicies, gate:decision's observed, and tool:failed's optional durationMs.
  • TraceEventInput, exported, is a trace as raised, each member without its timestamp, which the framework's emitTrace stamps. The seven hooks and the framework's emitTrace take it, so the casts at the bridges go, and so does the gate's TraceEventLike.
  • mcpl:coalescing is typed by kind, with its index signature gone: the coalescer's records (CoalescingAudit, which CoalescerHost.audit now takes), a connection's late-render, and the framework's turn-withdrawn.
  • TraceEventBase is a type alias, where it was an interface, and usage:updated's member is built from the usage tracker's own UsageUpdatedEvent.
  • One helper traces a tool's outcome, traceToolOutcome in src/framework.ts. It replaces the seven sites above and the MCPL path's traceMcpToolOutcome, and where a failed result holds no error it writes 'tool returned an error', as the MCPL path already did. code_execution's own tool:failed keeps its 'unknown error' (src/framework.ts:27598), and a cut-off call's keeps its '' (:25649).
  • The gate's 16 raises no longer set timestamp. The framework's emitTrace builds each trace as { ...event, timestamp: Date.now() } (src/framework.ts:31367–:31370 at fd6d6a36).
  • DesiredChannelState is exported from src/mcpl/channel-registry.ts, since mcpl:channel-reconcile-failed's member names it.
  • test/trace-union.test.ts is new, holding type-level pins. test/event-gate-sleep.test.ts's trace callback now narrows on type before reading eventType.
  • c97253ce removes test/tool-failed-error.test.ts, the runtime test of the helper's default that 3bb4b8df added. With the helper's ?? default removed, tsc reports an error at the helper (Tests), so the test held only the default's exact words.

What each part is for

  • The 28 members answer types/trace.ts: tune-out:announced, tune-out:announce-failed and tune-out:wake-budget-paused are raised but missing from the TraceEvent union #899. A host's table keyed on TraceEvent['type'] can't name a trace the union doesn't list.
  • Typing the hooks and removing the casts is what lets tsc check every raise, as the red run shows.
  • TraceEventInput is the one input type the hooks and emitTrace share. It maps each member's keys without timestamp, rather than using Omit<TraceEvent, 'timestamp'>, because Omit over a union keeps only the keys every member shares. With Omit in its place, tsc reports 254 errors at this head.
  • The declared fields were raised but missing from their members, so a host reading them had no type for them.
  • Typing mcpl:coalescing by kind lets tsc check its fields. An index signature on a member accepts any field. Each kind gets its own member. The undeclared-field pin raises an mcpl:coalescing with a field its kind doesn't declare, and fails at fd6d6a36.
  • TraceEventBase as a type alias, and Fields<…> for the two members built on an interface (gate:config-reconciled on RecipeChanges, and usage:updated), let every member, narrowed by its name, extend Record<string, unknown>. The cast pin asserts it, and fails at fd6d6a36, where the base is an interface. So a direct cast of a trace to an index-signature type still compiles once mcpl:coalescing has no index signature, as the one at test/mcpl-down-for-good.test.ts:80 does at this head.
  • traceToolOutcome gives tool:failed's error the text its type promises, in one place instead of seven. With its ?? default removed, tsc reports TS2322 there, so the build holds that the field is a string.
  • Dropping the gate's timestamps follows from TraceEventInput, which has none.
  • The pins fail tsc when what they pin changes, as the runs under Tests show. They assert that each hook's parameter, and the framework's emitTrace's, is exactly TraceEventInput. They assert that TraceEventInput refuses an unlisted name, a listed trace without its fields, an undeclared field and a timestamp. And they assert that every member, narrowed by its name, extends Record<string, unknown>.
  • The three changelog fragments tell a host what each part changes for it: breaking for the typing break and the gate's timestamp, added for the members and TraceEventInput, and fixed for tool:failed's error.

With other open pull requests

git merge-tree of each of the 44 open pull requests onto main a64cfc010f and onto that main with this head merged into it, on Oct 30 at 20:50Z, compared region by region in each file, differs for ten.

Four merge clean onto main, and conflict onto main with this head merged:

  • fix(routing): a turn from a non-channel surface pins no inferred locus #190 (at 813fe034fc) conflicts, onto main with this head merged, in one region of src/framework.ts, the channels tool's trace, which this change replaces with traceToolOutcome.
  • Add opt-in required admission for inbound MCPL traffic #906 (at 869bb3add7) conflicts, onto main with this head merged, in one region of src/mcpl/inference-router.ts: this change types the emitTraceFn field as TraceEventInput, and that pull request adds an admissionRefusals field on the line before. That pull request also passes the emitTraceFn of ChannelRegistry, InferenceRouter and PushHandler to the AdmissionRefusals.report() it adds, whose emitTrace parameter is a callback taking { type: string; [key: string]: unknown } (src/mcpl/inbound-admission.ts:197 at its head). A trial merge of it into main with this head merged, keeping both lines of that region, gives three tsc errors, TS2345, one at each of those three calls.
  • fix(gate): drop debounced events already read and answered #937 (at acfccc9a24) conflicts, onto main with this head merged, in one region of src/gate/event-gate.ts. That pull request moves the gate's gate:debounce-delivered raise. It removes the raise where this change drops its timestamp, and raises it in a new place, writing timestamp (src/gate/event-gate.ts:3929 at its head). A trial merge of it into main with this head merged, taking its side of that region, gives one tsc error, TS2353, at that timestamp.
  • feat(presentation): one record, a presentation index per agent — the deferral queue replaced (#291 step 3) #844, below.

Six already conflict with main, in the same files and as many regions either way. With this head merged, some of those regions hold different lines:

With #844

@tel-0s, #844 (at b9c4979e89) merges clean onto main, and onto main with this head merged it conflicts in two regions. One is the type imports at the top of src/types/trace.ts, where each adds its own. The other is in src/framework.ts, where the framework builds its EventGate: this change drops the cast on the gate's emitTrace option, and your pull request adds audience to its addMessage option on the next line.

What this leaves

tool:failed is read two ways in the framework, and this change keeps both as they were. Three paths trace tool:completed for a result whether or not it has isError, with these lines at this head:

  • a module's tool, dispatched through dispatchToolCallEvent, traces tool:completed for any result it returns (src/framework.ts:29523) and tool:failed only when the module throws (:29544), under a comment that names RFC-007 and "the host could not obtain a result";
  • the gate's sleep and wake, past their refusal in a fork or an ephemeral run (:37062–:37077), deliver their results through a finish that traces tool:completed for each (:37083–:37086);
  • gate_status, through dispatchGateToolCall, traces tool:completed for every result the gate's handleToolCall() gives (:38053).

The sites that go through traceToolOutcome trace a result with isError as tool:failed (:35387–:35393), as they did before.

Tests

The runs below were on Linux, with Node v24.22.1, npm 11.19.1 and TypeScript 7.0.2, after npm install. The repository commits no lockfile, so each printed its direct dependencies with npm ls --depth=0.

Red first, at fd6d6a36, in a copy of a clean checkout. npm ls gave @animalabs/chronicle 0.5.1, @animalabs/context-manager 0.24.1, @animalabs/mcpl-core 0.4.0, @animalabs/membrane 0.8.0, @babel/parser 7.29.12, @modelcontextprotocol/client and server 2.4.0, @types/node 22.20.5, @types/ws 8.18.2, chokidar 4.0.3, discord.js 14.27.0, tsx 4.23.15, typescript 7.0.2 and ws 8.22.0.

  • With test/tool-failed-error.test.ts from 3bb4b8df added, tsc reports no errors, and the test fails. Its tool:failed trace has error: undefined where the test expects 'tool returned an error'.
  • With the seven hooks and the framework's emitTrace typed to TraceEventInput, the casts removed and this change's test/trace-union.test.ts added, tsc reports 63 errors. 38 quote one of the 28 names, and between them they quote all 28. 21 are at other raises. Of those, 13 write a field TraceEventInput doesn't take: the gate's timestamp 11 times, gate:config-reloaded's removedPolicies and tool:failed's durationMs. 7 are the seven tool-outcome sites, whose argument fits neither tool:completed nor tool:failed. 1 is an mcpl:coalescing raise whose argument has no kind (src/framework.ts:18513). 2 are in test/event-gate-sleep.test.ts, and 2 are pins: the undeclared-field pin, since mcpl:coalescing still has its index signature there, and the cast pin, since there every member builds on an interface.

At this head c97253ce, in a copy of a clean checkout with a fresh install, npm ls printed the same versions. tsc reports no errors, the build passes, and 85 test files pass 3,212 of 3,212. They are 77 test files chosen because they name kinds of trace or event this change touches (tool:failed, and names beginning gate:, mcpl: or tune-out:), the 15 that main changed between d700dce1, the parent of this branch's first commit, and fd6d6a36 (8 of them among the 77), and test/trace-union.test.ts.

Host-side breaks. The probe below, put in test/ and compiled with tsc --noEmit -p ., gives no errors at a clean fd6d6a36 and five at a clean c97253ce, with the same versions as above:

  • test/host-breaks.test.ts(7,69), TS2339, eventType doesn't exist on TraceEventInput (the callback);
  • (103,13), TS2322 at the never, whose message names mcpl:push_event, one of the 28 added types, first (the switch);
  • (111,52), TS2339, notAField doesn't exist on the narrowed mcpl:coalescing union;
  • (116,13) and src/types/trace.ts(15,13), TS2300, duplicate identifier TraceEventBase (the augmentation).

The probe's first comment names 502918b6, this head's parent, which was the branch's head when the probe was written.

test/host-breaks.test.ts
// Host-side breaks of #899, for a tsc run: each block compiles at main fd6d6a36 and should not at 502918b6.
import type { TraceEvent } from '../src/types/trace.js';
import type { EventGate } from '../src/gate/event-gate.js';

// 1. A callback whose parameter is inferred from EventGate's emitTrace option, reading a field not every trace has.
type GateTraceHook = ConstructorParameters<typeof EventGate>[0]['emitTrace'];
export const breakCallback: GateTraceHook = (event) => { void event.eventType; };

// 2. A switch over TraceEvent['type'] checked exhaustively with never, covering the 89 types listed at fd6d6a36.
export function breakExhaustive(event: TraceEvent): void {
  switch (event.type) {
    case 'admission:decided':
    case 'dendrite:activation-cancelled':
    case 'dendrite:agent-created':
    case 'dendrite:agent-ended':
    case 'dendrite:agent-interrupted':
    case 'dendrite:agent-orphaned':
    case 'dendrite:agent-reparented':
    case 'dendrite:early-traces-dropped':
    case 'dendrite:mail-deferred':
    case 'dendrite:mail-delivered':
    case 'dendrite:mail-held':
    case 'gate:config-error':
    case 'gate:config-reloaded':
    case 'gate:debounce-delivered':
    case 'gate:decision':
    case 'gate:held-dropped':
    case 'gate:judge-verdict':
    case 'gate:policy-matched':
    case 'gate:wake-line-omitted':
    case 'host:quiesce':
    case 'host:quiesced_boot':
    case 'host:resume':
    case 'inbound:observer-failed':
    case 'inference:aborted':
    case 'inference:completed':
    case 'inference:content_block':
    case 'inference:exhausted':
    case 'inference:failed':
    case 'inference:published':
    case 'inference:published_complete':
    case 'inference:request_dropped':
    case 'inference:request_stale':
    case 'inference:started':
    case 'inference:stream_restarted':
    case 'inference:stream_resumed':
    case 'inference:tokens':
    case 'inference:tool_calls_yielded':
    case 'inference:turn_ended':
    case 'inference:usage':
    case 'inference:waiting':
    case 'mcpl:channel-incoming-rejected':
    case 'mcpl:coalescing':
    case 'mcpl:conversation-binding-orphaned':
    case 'mcpl:conversation-closed':
    case 'mcpl:conversation-disposed':
    case 'mcpl:conversation-spawn-failed':
    case 'mcpl:conversation-spawned':
    case 'mcpl:conversation-unrouted':
    case 'mcpl:empty-content-dropped':
    case 'mcpl:feature-set-disabled':
    case 'mcpl:malformed-content-refused':
    case 'mcpl:manifest-change-receipt':
    case 'mcpl:orphaned-response':
    case 'mcpl:policy-refused':
    case 'mcpl:push-event-rejected':
    case 'mcpl:request-handler-error':
    case 'mcpl:server-closed':
    case 'mcpl:server-connect-failed':
    case 'mcpl:server-error':
    case 'mcpl:server-reconnected':
    case 'mcpl:server-stderr':
    case 'mcpl:speech-route-failed':
    case 'mcpl:speech-routed':
    case 'mcpl:tool-pattern-unmatched':
    case 'message:added':
    case 'message:removed':
    case 'module:added':
    case 'module:batch_hook_failed':
    case 'module:removed':
    case 'operator:action':
    case 'ops:alert':
    case 'process:completed':
    case 'process:received':
    case 'process:response-refused':
    case 'prose:draft-resent':
    case 'prose:drafts-dismissed':
    case 'prose:drafts-held':
    case 'puppet:tool-call':
    case 'redo:completed':
    case 'tool:completed':
    case 'tool:failed':
    case 'tool:result_dropped':
    case 'tool:spill_failed':
    case 'tool:started':
    case 'tune-out:reader-fork':
    case 'tune-out:reader-fork-failed':
    case 'tune-out:reader-prose-delivered':
    case 'undo:completed':
    case 'usage:updated':
      return;
    default: {
      const unhandled: never = event;
      void unhandled;
    }
  }
}

// 3. Reading a field of an mcpl:coalescing trace that no kind declares.
export function breakCoalescing(event: TraceEvent): void {
  if (event.type === 'mcpl:coalescing') void event.notAField;
}

// 4. Augmenting TraceEventBase by declaration merging.
declare module '../src/types/trace.js' {
  interface TraceEventBase {
    hostField?: string;
  }
}

One change to src at a time fails tsc where it should. In the copy the head's tests ran in, with the same versions, tsc ran after each of these changes, each undone before the next:

  • the helper's ?? 'tool returned an error' removed gives one error, TS2322 at src/framework.ts:35389, "Type 'string | undefined' is not assignable to type 'string'";
  • EventGate's emitTrace option typed (event: any) => void gives one error, at that hook's pin (test/trace-union.test.ts:28);
  • the framework's emitTrace widened to { type: TraceEvent['type']; [key: string]: unknown } gives one error, at its pin (:27);
  • TraceEventInput defined as Omit<TraceEvent, 'timestamp'> gives 254 errors, among them one at the missing-fields pin (:18).

Not verified

  • At fd6d6a36, six of the seven sites weren't run with a failed result that holds no error. The run reaches read_image's site only. For the other six, the lines under Problem say what they wrote.
  • At this head, no tool:failed site was run with a failed result that holds no error. tsc holds that the helper's error is a string, and the default's words are what src/framework.ts:35389 says.
  • The other 306 test files weren't run against this head.
  • No host was built against this head, connectome-host included. The probe under Tests stands in for a host's code, and its callback is for EventGate's hook only.
  • tsc doesn't check a field a raise adds through a spread. A one-off read of every raise's argument type through TypeScript 5.9's compiler API, outside the build, found one undeclared field at 9ab9b55a, gate:decision's observed (src/gate/event-gate.ts:3176 there), which this change declares. At this head it read 256 raises and found none.

  • Changelog fragments added: changelog.d/899-trace-union.breaking.md, changelog.d/899-trace-union.added.md and changelog.d/899-trace-union.fixed.md.

Gideon-2613 made this change, on a design by Ada-1017. Two internal reviews read its first two commits, and Crispin-2713 read its last commit and its merge of main. Laurel-2386 wrote this description.

!Closes #899.

Gideon-2613 added 5 commits October 29, 2026 21:25
…ma-research#899)

TraceEvent lists the 28 traces the gate, MCPL channels, pushes,
inference, state and tune-out raised without a member, each with its
fields as its raises give them. The seven component hooks (EventGate's
emitTrace option, the emitTraceFn of ChannelRegistry, PushHandler,
CheckpointManager and InferenceRouter, FeatureSetManager's emitTrace
and the tune-out coordinator's) and the framework's own emitTrace take
TraceEventInput, a member of the union without its timestamp. So each
bridge's cast and the gate's TraceEventLike go, and tsc checks the
name and the fields at every raise.

TraceEventInput maps each member's keys rather than using
Omit<TraceEvent, 'timestamp'>. Omit over the union keeps only the keys
every member shares, and while one member had an index signature that
was [key: string]: unknown, which accepted any trace at all: a probe
typed with Omit gave no errors at raises of names outside the union.
With no index signature left, Omit would instead refuse each member's
own fields (253 errors).

What the checked raises turned up, fixed where each belongs:
- tool:failed declares an optional durationMs, and its eight outcome
  sites trace through one helper, traceToolOutcome, which sets error to
  'tool returned an error' when a failed result carries none, as the
  MCPL path already did. Seven of those sites could raise error as
  undefined, which the member rules out.
- The gate's raises stop setting timestamp: emitTrace stamps its own
  over it, so a framework listener never saw the gate's.
- gate:config-reloaded declares removedPolicies and
  stateClearedPolicies.
- The coalescer's audit takes CoalescingAudit, typed by kind, and the
  mcpl:coalescing member gives up its index signature for those records
  and the two the framework raises itself, late-render and
  turn-withdrawn.
- TraceEventBase is a type literal rather than an interface. The index
  signature was what let a trace be cast directly to an index-signature
  type; with a type literal every member can be, so such a cast, as in
  mcpl-down-for-good's test, compiles as before.
- DesiredChannelState is exported, for mcpl:channel-reconcile-failed.

test/trace-union.test.ts pins that an unlisted trace doesn't compile
where it's raised, and that each hook takes only the union's traces,
with one type-level line per hook: a hook typed wide again leaves its
@ts-expect-error unused, which tsc reports.
…s exact type

What af899's sheet found, fixed:
- gate:decision's observed reached listeners through a conditional
  spread, which tsc's excess-property check doesn't read; the member now
  declares it. A read of every raise's argument type through the
  compiler API (256 raises through emitTrace and emitTraceFn) found no
  other undeclared field.
- A member that intersects an interface refused a narrowed cast to an
  index-signature type (TS2352): gate:config-reconciled, through
  RecipeChanges. Fields<> maps such an interface's fields into the
  member, so every member, narrowed or not, takes the cast, which the
  last commit's message already said of them. usage:updated's member is
  now Fields<UsageUpdatedEvent>, the tracker's own type, rather than a
  copy of it.
- CoalescingAudit gives each kind its own member, so
  Extract<TraceEvent, { kind: 'removed' }> and the like name it rather
  than never.
- The pins assert that each hook's parameter, and framework.emitTrace's,
  is exactly TraceEventInput, with no @ts-expect-error that another
  error on its line could satisfy: a hook typed any, or wide with an
  index signature, fails its own line. TraceEventInput is pinned to
  refuse an unlisted name, a listed trace without its fields, and a
  field its member doesn't declare.
- Texts: the fixed fragment says what changed (error was undefined where
  a failed result held none), not that error always carries text; the
  breaking fragment names hosts with an exhaustive table or switch over
  TraceEvent['type'], and how to tell a coalescer's late-render from a
  connection's; the added fragment says the compiler checks the fields a
  raise writes out; mcpl:coalescing's doc no longer lists kinds the type
  now names, and CoalescingAudit has no comment.
…iled's default error

R5 says a direct cast of a trace to an index-signature type compiles as
it did. A type-level line now asserts that every member, narrowed by its
name, extends Record<string, unknown>: a member that intersects an
interface, or an interface TraceEventBase, fails it. TraceEventInput is
also pinned to refuse a timestamp, which emitTrace stamps over whatever
a raise sets, as the gate's own did.

traceToolOutcome's default for a failed result with no error now has a
runtime test: read_image, with a workspace whose handler throws an Error
with no message, gets a failed result with no error from thrownResult,
and its tool:failed trace reads 'tool returned an error'. Against the
previous src it read undefined.

What the earlier messages said, made exact:
- tsc checks a raise's name and the fields it writes out. A field that
  arrives through a spread isn't excess-checked, and 49 of the 246
  object-literal raises carry an object spread; the compiler-API read
  of every raise was a one-off, not part of the build.
- traceToolOutcome serves the seven sites that were ternaries and the
  MCPL path's two calls. code_execution's own tool:failed keeps its
  'unknown error' and the cut-off refusal its '', both strings.
- The fixed fragment names the gate's wake-rule tools, the gate's only
  tools whose outcome goes through the helper.
What a caller relies on is that a tool:failed trace's error is a string,
as its member says. The compiler already holds that at the helper: with
traceToolOutcome's `?? 'tool returned an error'` removed, `tsc --noEmit`
at 502918b reports one error, TS2322 at src/framework.ts:35389, "Type
'string | undefined' is not assignable to type 'string'". The test added
only the default's exact words, which no caller requires.
@slimepriestess

Copy link
Copy Markdown
Contributor

Taking this review (Weft). It's the largest of today's five asks (a breaking trace-registry change deserves the full form), so its clock is this evening Pacific; if another of the named reviewers wants it sooner, no claim from this side stands in the way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

types/trace.ts: tune-out:announced, tune-out:announce-failed and tune-out:wake-budget-paused are raised but missing from the TraceEvent union

2 participants