Skip to content

test(orchestration): backfill the tool-loop suite and raise its floors - #283

Merged
saadahmed95 merged 2 commits into
mainfrom
test/tool-loop-backfill
Oct 23, 2026
Merged

saadahmed95 merged 2 commits into
mainfrom
test/tool-loop-backfill

Conversation

@saadahmed95

@saadahmed95 saadahmed95 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Why

The tool loop is the engine's central feature and its suite was uneven: orchestratorHelpers.test.ts covered one of six exported helpers, every loop case drove exactly one tool call per round, and the loop's negative gates (no registry, an end_turn stop beside tool calls, a stream with no stop chunk) were never observed from outside. Closes #80.

What Changed

Test-only, plus the coverage floors; no src/ change outside *.test.ts.

  • src/orchestration/orchestratorHelpers.test.ts: all six exports, with the eight-row truth table of shouldContinueToolLoop in one assertion.
  • src/toolLoop.test.ts (root level, see below): the cases the package's exported createMockProvider can drive. A two-call round runs both executors and hands the next round one assistant message with two tool_use items and one user message with two matching tool_result items, in script order; one suppressing executor among two yields exactly one suppress_output after the tool_result; two entry_upsert messages take sequential indices from the session length at call time and are readable from the session afterwards; a round returning no client messages yields no tool_result; three rounds leave two assistant messages with one tool_use each, so no pending call leaks between rounds; a scripted tool_use turn with no registry ends after one provider call.
  • src/orchestration/chatOrchestrator.test.ts: the two gates the exported mock cannot express, because it derives the stop reason from whether a turn called a tool: an end_turn stop beside a tool call, and a stream ending without a stop chunk. Both reuse the file's existing scriptedProvider (a second scripted round that must never stream proves no second provider call), adding no new double, and their registry holds the tool TOOL_CALL names, lookup, with a recording executor, so "zero executions" is observed rather than assumed.
  • specs/hal-engine-architecture/spec.md, § Tool Execution Loop: a ### A round with more than one call subsection and extra gating and helper statements, fourteen in all, each citing its test; the line shifts in chatOrchestrator.test.ts repointed 33 existing anchors.
  • jest.config.js: the src/orchestration/, src/orchestration/tools/ and global floors raised to the re-measured numbers. src/orchestration/ is measured over production files alone (97.02 / 93.42 / 97.72 / 97.14). One thing worth knowing for the next ratchet: Jest holds global to the files no path floor claims, not to the "All files" row, so its numbers (95.05 / 90.7 / 93.75 / 95.74) are lower than that row; a comment on the line now says so.

Where this departs from #80's text, and why

  • Not every new case lives in chatOrchestrator.test.ts. layers.yaml (added after the issue) forbids orchestration from importing providers, so a test there cannot construct the exported createMockProvider the issue requires. The mock-driven cases live in a root-level file, where config.test.ts already composes both layers, with the two-tool registry helper local to it; the two scriptedProvider cases stay in place.
  • Two gates use the existing scriptedProvider, not the exported mock. MockTurn has no stop-reason field; it emits tool_use when a turn has calls and end_turn otherwise, and always emits a stop. The issue's "no further hand-rolled double" is kept: nothing new was written, the file's existing double is reused.

Nothing here asserts the round ceiling, the throwing-executor path or ToolContext's field list, so the three sibling issues merge in any order.

Alternatives Considered

  • Putting everything in chatOrchestrator.test.ts with scriptedProvider only: possible, but the issue asks for the exported mock on purpose, and the root-level file honours both that and the layering rule.
  • A per-case local double for the two stop-reason gates: more code for no more proof; the existing double already expresses them.

ADRs & Architecture

No architectural change. The tests pin specs/hal-engine-architecture/spec.md § Tool Execution Loop; the layering they respect is layers.yaml.

Testing

Fourteen new tests, each cited from the spec; the touched suites pass, scripts/check-changelog.sh origin/main passes (no non-test file under src/ changed), and npm run verify passes with the raised floors. The second commit answers the review: gatedTurn now observes the tool it names, and the helper that briefly lived under src/orchestration/ as a non-test file is folded into its only consumer, the floors re-measured without it.


Code Quality Checklist

  • Lint: npm run eslint passes with no warnings
  • Types: npm run typecheck passes (strict mode)
  • Tests: npm test passes (new tests added for new code)
  • No Secrets: No API keys, tokens, or credentials committed
  • Formatting: Code follows project style (npm run prettier:check or npm run prettier)
  • Documentation: Updated the relevant docs; a comment spans one line, so anything longer went to specs/, adrs/ or docs/
  • Layer Adherence: Changes respect the 5-layer architecture (types → providers → infrastructure → orchestration → transport)
  • No console.log: Uses log from shared/logger.js instead
  • Changelog: User-visible changes have a CHANGELOG.md entry under ## [Unreleased], or the PR carries the no-changelog label

Related Issues

Closes #80.

🤖 Generated with Claude Code

The loop's helpers were tested one in six, every loop case drove one
tool call per round, and the gates that end a turn were never observed
from outside. Fourteen tests now cover all six helpers with the whole
truth table of shouldContinueToolLoop; a two-call round running both
executors and pairing two tool_use with two tool_result items in order;
one suppressing executor among two yielding one suppress_output after
the tool_result; two upserts taking sequential indices and landing in
the session; a round returning no client messages yielding no
tool_result; no pending call leaking across three rounds; and the three
gates: no registry, end_turn beside a tool call, a stream with no stop.
The mock-driven cases live at the root, where the layering rule lets a
test reach both the orchestrator and the exported mock. The orchestration,
tools and global coverage floors ratchet to the measured numbers.

Closes #80.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lore-agent

lore-agent Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Lore is reviewing this PR — 2b23ab64-e382-4286-8aa0-4c9b27073472.

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🔍 Lore Spec Impact — advisory

This PR touches 43 statement(s) across 5 spec(s), and changes the validating tests alongside every one of them.

Architecture · 5 statement(s)

specs/hal-engine-architecture/spec.md

Whether a round continues
✓ this PR also changes the tests that validate it

A round continues only when the model stopped for tool_use, named at least one tool, and a ToolRegistry is configur…

validated by src/orchestration/orchestratorHelpers.test.ts:29

Whether a round continues
✓ this PR also changes the tests that validate it

With no registry configured the loop stops, whatever the model asked for, because nothing could execute the call ([vali…

validated by src/orchestration/orchestratorHelpers.test.ts:29

Whether a round continues
✓ this PR also changes the tests that validate it

A tool_use stop naming no tool stops the loop, rather than re-querying with nothing to add ([validated by: stops when…

validated by src/orchestration/orchestratorHelpers.test.ts:29

Whether a round continues
✓ this PR also changes the tests that validate it

Any other stop reason ends the loop even when tool calls are pending ([validated by: stops when the model finished for …

validated by src/orchestration/orchestratorHelpers.test.ts:29

Across rounds
✓ this PR also changes the tests that validate it

A tool call's signature, an opaque token its provider needs back, is copied onto its tool_use block for the next ro…

validated by src/orchestration/orchestratorHelpers.test.ts:29

Weaker signals (38) — linked by a spec, not proven by a test run

Architecture · 26 statement(s)

specs/hal-engine-architecture/spec.md

OrchestratorHooks
✓ this PR also changes the tests that validate it

Every hook is optional and receives the current session, and an orchestrator built with none behaves exactly as one bui…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

The order above holds for a successful pass ([validated by: calls all hooks in correct order](../../src/orchestration/c…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

When the stream fails, afterModelResponse is skipped and the tail becomes onError then afterSession ([validated b…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

beforeSession runs before any other hook ([validated by: called before anything else](../../src/orchestration/chatOrc…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

beforeUserInput receives the content of the last user message ([validated by: receives the last user message content]…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

beforeUserInput can modify that message by returning a different string ([validated by: modifies user message when re…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

afterUserInput receives the message as beforeUserInput left it, not as the client sent it ([validated by: receives …

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

beforeModelResponse can replace the system prompt, for example loading it from a PromptStore ([validated by: replac…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

afterModelResponse receives the collected response text and the usage the provider reported ([validated by: receives …

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

OrchestratorHooks
✓ this PR also changes the tests that validate it

afterModelResponse does not run when the stream throws, so it never reports a response that was not delivered ([valid…

validated by src/orchestration/chatOrchestrator.test.ts:638
via changed file src/orchestration/chatOrchestrator.test.ts

…and 28 more statement(s).

18 new statement(s) have no test link yet.

Deterministic · graph @ 909704b (projected 2026-10-23) · 5 file(s) skipped: changed since the graph last saw them, so their line numbers no longer line up · no tests run by this check

@lore-agent lore-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lore review — Changes suggested

gatedTurn registers first/second while TOOL_CALL invokes lookup, and src/orchestration/toolLoopTestSupport.ts fails check-changelog.sh in CI and inflates src/orchestration/ coverage thresholds

Must fix (2) · Consider (0) · Nits (0)

Reviewed by gemini-4-argon

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

Posted by floor, visit 98456201-e727-4c5b-bb1d-7430536ea06f.

Comment thread src/orchestration/chatOrchestrator.test.ts Outdated
Comment thread jest.config.js Outdated
…upport out of src

gatedTurn registered first and second while TOOL_CALL names lookup, so
its "zero executions" was unobservable; it now registers lookup with a
recording executor. The two-tool helper lives in its only consumer, the
root-level suite, so no non-test file sits under src/ for the changelog
check or the orchestration coverage floor to see; that floor is
re-measured without it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@saadahmed95

Copy link
Copy Markdown
Contributor Author

@lore review

@lore-agent

lore-agent Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Lore is reviewing this PR — 75a43a6d-487c-4179-b72d-9a1e6438dfd6.

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

@lore-agent lore-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lore review — Approved

No issues found.

Must fix (0) · Consider (0) · Nits (0)

Reviewed by gemini-4-argon

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

Posted by floor, visit 36e7f14b-c055-4f6e-a492-c4907df64ff2.

@saadahmed95
saadahmed95 merged commit 1d578af into main Oct 23, 2026
10 checks passed
@saadahmed95
saadahmed95 deleted the test/tool-loop-backfill branch October 23, 2026 08:49
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.

Backfill the orchestrator tool-loop tests

1 participant