Force agents to act so weak models can't complete a no-op

The whole premise is cheap models + skills + a team — so the control plane,
not the model, must guarantee action. Every adapter hardcoded
tool_choice:"auto", letting a weak model answer the kickoff with prose and
"succeed" having hired no one, delegated nothing, and said nothing.

Now the agent loop compels a tool call on the opening turn (tool_choice
plumbed through every backend: OpenAI/Responses/Chat, Anthropic, Codex,
Copilot, Azure, Custom, Gemini ANY mode, Bedrock any). If the forced turn
still yields no tool call, the run fails loudly instead of recording a
deceptive success — surfacing the problem (let it crash) rather than a
silent dead company. After it has acted, the model chooses freely.

Tested: a stub that rambles on "auto" but acts when forced now posts a
kickoff message; a prose-only first turn fails the run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 4.8 committed 2026-06-10 21:43:49 -04:00
1 parent fa01aa682f
commit ef9ee463c9
9 files changed
+107 -35

No files matched your search

@@ -166,11 +166,18 @@ describe("autonomous bootstrap run", () => {
test("a goal created after onboarding reaches the CEO and runs autonomously", async () => {
const connection = new DatabaseConnection(join(tmpdir(), `ac-goal-${randomUUID()}.db`));
const providers = new ProviderService(connection.db, providerAdapters, testVault, undefined, async () => ({
text: "Reviewed the goal and posted the operating plan.",
toolCalls: [],
rawResponseJson: JSON.stringify({ id: "resp_goal" }),
}));
// The forced opening turn means each run must take a concrete action; it
// posts to the company channel, then stops on the next (free) turn.
const providers = new ProviderService(connection.db, providerAdapters, testVault, undefined, async (input) => {
if (input.toolChoice === "required") {
return {
text: "Reviewing the goal.",
toolCalls: [call("send_message", { channel: "general", body: "Reviewed the goal and posting the operating plan." })],
rawResponseJson: JSON.stringify({ id: "resp_goal_act" }),
};
}
return { text: "Done.", toolCalls: [], rawResponseJson: JSON.stringify({ id: "resp_goal_done" }) };
});
const onboarding = new OnboardingService({ db: connection.db, saveProviderCredential: (input) => providers.saveCredential(input) });
const organization = new OrganizationService(connection.db);
const taskExecution = new TaskExecutionService(connection.db, providers);