Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,42 @@ describe("dynamic-workflow parity", () => {
});
});

it("emits one warning event when the warnAgents threshold is crossed", async () => {
// Maps to the Claude dynamic workflow session-level advisory warning when many
// agents are scheduled; lets callers detect runaway fan-out before hitting maxAgents.
const worker = fakeAgent<number, number>("worker", (value) => value);
const events: WorkflowEvent[] = [];
const definition = workflow({
meta: { name: "warn", description: "warnAgents threshold" },
body: ({ call, pipeline }) => pipeline([1, 2, 3], (value) => call(worker, value)),
});

await runWorkflow(definition, {
limits: { warnAgents: 2 },
onEvent: (event) => events.push(event),
});
const warnings = events.filter((event) => event.type === "warning");
expect(warnings).toHaveLength(1);
expect(warnings[0]).toMatchObject({ type: "warning", message: expect.stringContaining("2") });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The stringContaining("2") assertion is weak — it would pass for any message containing the digit 2. The test should pin the exact message string emitted by the runtime to be a true executable spec.

💡 Suggested assertion
expect(warnings[0]).toMatchObject({
  type: "warning",
  message: `Workflow scheduled more than 2 agents.`,
});

This matches the literal string in rig.ts: `Workflow scheduled more than ${warnAgents} agents.`

});

it("call.json accepts a non-object schema (s.enum) — rig advantage over Claude dynamic workflows", async () => {
// Claude dynamic workflows only support object schemas in agent(prompt, { schema }).
// rig's call.json accepts any s.* schema: s.enum, s.array, s.string, etc.
configureAgent(() => ({

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] configureAgent sets a global factory but is not reset after the test. If vitest runs tests in the same process (default), subsequent tests that do not call configureAgent themselves will inherit this stub factory, causing confusing failures.

💡 Suggested fix

Add cleanup, matching the pattern used in workflow one-off agents:

it("call.json accepts a non-object schema...", async () => {
  const originalFactory = /* capture before */ undefined;
  configureAgent(() => ({ ask: async () => high, close: async () => {} }));

  // ... test body ...

  // reset at the end, or use afterEach in a describe block
});

Or wrap this test (and the one-off agents tests) in a shared describe with an afterEach(() => configureAgent(defaultAgentFactory())) to keep teardown in one place.

ask: async () => '"high"',
close: async () => {},
}));

const definition = workflow({
meta: { name: "enum-schema", description: "non-object schema in call.json" },
body: ({ call }) =>
call.json("Classify priority.", s.enum("high", "medium", "low")),
});

await expect(runWorkflow(definition)).resolves.toBe("high");
});

it("runs a nested workflow on the shared limiter, budget, and events", async () => {
let active = 0;
let peak = 0;
Expand Down