goose: who decides what
How to read this page
A seat is a judgment the agent makes over and over: which provider runs a session, what the model sees, may this tool call run, is the work done. Each card records the question, how it is answered, and who or what holds it, with the code that implements or exposes that decision. Seat ids (A1, D12 …) are links.
Each answer describes what goose does by default at the commit named above. Exceptions named are the main ones, not a complete list.
The chip on each card has two words. The first is about the question: a RULE has one right answer given its inputs, so a run that answers differently is a bug; a RULING is one that competent people could answer differently. Enforcing a limit someone set is a RULE; choosing what to keep, what to trust or when the work is done is a RULING, even when code holds it.
The second word is who holds it: llm = a language-model call; code = code in their repository; human = a person; external = something outside the repository that the agent hands the decision to.
Paths are relative to the repository root. A bare :120-140 means lines in the file named just before it.
Counts
102 seats across 9 stages: A 14, B 14, C 11, D 18, E 13, F 10, G 11, H 10, I 1.
| Kind of question · holder | Seats |
|---|---|
| RULE · code | 52 |
| RULING · code | 18 |
| RULING · llm | 18 |
| RULING · human | 8 |
| RULING · external | 5 |
| RULE · external | 1 |
What this edition does not have
There is no list of gaps. No coverage figure: this edition does not say how many of goose's model calls and decision points the seats leave out. Each card shows the question, the answer and its holder; for most seats we also recorded what checks the judgment and how it has failed, but those notes are not shown here.
What is left out of this edition
15 of the 102 cards are left out of this edition. The counts above include them.
A. Intake and routing
A1 When a person resumes without naming a session, which session continues? RULE · code
- Answered how
For
goose session --resumeandgoose run --resume, code takes the given session id as is, or the first session whose name or id equals the given name, or the id from a given path's file stem; with no identifier it takes the firstSessionType::Usersession from the store's list, ordered by the latest message timestamp (else the session'supdated_at), newest first, so hidden and scheduled sessions are never picked.build_sessionthen re-checks the id exists and has its own copy of the no-identifier rule.- Held by
crates/goose-cli/src/cli.rs:417-447(get_or_create_session_id); second copycrates/goose-cli/src/session/builder.rs:470-493; orderingcrates/goose/src/session/session_manager.rs:2317-2318,:2350
A2 Which provider and model run a CLI session? RULE · code
- Answered how
Code picks the provider as CLI
--provider, else the saved session's provider, else the recipe'ssettings.goose_provider, else the configuredGOOSE_PROVIDER. The model is the CLI--model, else (only with a--providerflag that equals the configured provider, on resume)GOOSE_MODELfrom the environment, else the saved model when the provider is unchanged, else, only with a--providerflag: the recipe model (if the recipe names no provider or the same one),GOOSE_MODELenv and the configured model (if that provider is the configured one), the provider entry's saved model, the registry default; without a flag: the recipe model, then the configured model. Nothing found exits.- Held by
crates/goose-cli/src/session/builder.rs:289-445(resolve_provider_and_model; provider:301-310, model:366-405)
A3 Which provider and model run a desktop/ACP session? RULE · code
- Answered how
On
session/new, code takes the recipe'ssettings.goose_provider, else the client's_meta.provider; the model is the recipe'sgoose_model, else the chosen provider's registry default model. When neither recipe nor client names a provider, it uses the configured provider with the recipe model if there is one, else the configured provider and model.- Held by
crates/goose/src/model_config.rs:23-60(resolve_provider_and_model_config), called fromcrates/goose/src/acp/server/new_session.rs:139-150
A4 Which provider and model run a scheduled recipe job? RULE · code
- Answered how
Code resolves the provider and model at run time through
resolve_provider_and_model_configwith no client-requested provider: the recipe'ssettings.goose_provider, else the globalGOOSE_PROVIDER; the recipe'sgoose_model, else the recipe provider's registry default model when the recipe names a provider, else the globalGOOSE_MODEL; a recipetemperatureis applied. It then clamps the cache TTL and switches the job's session to that provider and model.- Held by
crates/goose/src/scheduler/full.rs:874-876(calls resolve_provider_and_model_config with the recipe's settings and no requested provider); rulecrates/goose/src/model_config.rs:23-60; appliedcrates/goose/src/scheduler/full.rs:902-904
A5 When a resumed CLI session's original provider cannot start, what runs instead? RULE · code
- Answered how
Only on resume without a
--providerflag, and only when the provider error's text contains "is not set", "not configured" or "Configuration value not found", code switches to the globally configured provider and model and prints a yellow warning on stderr; any other provider error, or a failure of the fallback, exits.- Held by
crates/goose-cli/src/session/builder.rs:753-798(fallback branch); test of the errorcrates/goose-cli/src/session/builder.rs:908-913(string match)
A6 Which approval mode does an interactive (CLI or desktop) session run under? RULE · code
- Answered how
Code reads
GOOSE_MODEthrough the lenientget_goose_mode()(get_param) and turns any error, missing or unparseable alike, into the enum defaultAuto. The CLI applies it to the session on every build, new or resumed; ACP stamps it on the session row atsession/new. The person can change it later: CLI/modesets the session and also writes the global config, ACPsession/set_modesets only that session.- Held by
crates/goose-cli/src/session/builder.rs:831-836(CLI);crates/goose/src/acp/server/new_session.rs:52(ACP); readercrates/goose/src/config/base.rs:243-244,:1277; defaultcrates/goose-provider-types/src/goose_mode.rs:23-25; person's changescrates/goose-cli/src/session/mod.rs:892-910,crates/goose/src/acp/server.rs:2431-2445
A7 Which approval mode does a scheduled job run under? RULE · code
- Answered how
Code creates every scheduled job's session with
GooseMode::Autoand sets the agent's mode to Auto again before the run; no config value, recipe field or person changes it.- Held by
crates/goose/src/scheduler/full.rs:884(session created Auto, in thecreate_sessioncall:878-886);crates/goose/src/scheduler/full.rs:905-907(forced again)
A8 Is this kickoff message a slash command, and which handler takes it (built-in, recipe, skill, MCP prompt)? RULE · code
- Answered how
Unless
GOOSE_SLASH_COMMANDS_ENABLEDis false (default true), code looks only at a turn whose kickoff is a single message, trims its agent-visible text, maps/compact,/summarizeand "Please compact this conversation" to/compact, and treats anything starting with/as a command word plus parameters. It offers the command to each operation in pipeline order (skills before recipes before tool execution, compaction, doctor, retry, status); the first that applies wins. Name collisions are settled inside the resolvers: a skill resolves only if no built-in or recipe has that name, a recipe only if no built-in does. An unclaimed/wordgoes to the model as text.- Held by
crates/goose/src/agents/state_machine/ops_slash_command.rs:19-83(parse and dispatch); ordercrates/goose/src/agents/state_machine_builder.rs:72-139; collision rulescrates/goose/src/slash_commands/skill_slash_command.rs:50-52,crates/goose/src/slash_commands/recipe_slash_command.rs:158-160; switchcrates/goose/src/agents/execute_commands.rs:3-7
A9 Should a kickoff starting with ! run directly as a shell command? RULE · code
- Answered how
When the turn's kickoff message, as the user sees it, starts with
!(after leading whitespace) and has non-empty text after it, code emits an assistant tool request toshellwith that text ascommand, without a model call; once the tool result is the last message, the operation returnsyielded(its effect on the rest of the turn not traced here). No setting turns it off.- Held by
crates/goose/src/agents/state_machine/ops_bang_shell.rs:18-24(bang_shell_command);crates/goose/src/agents/state_machine/ops_bang_shell.rs:40-74(emits theshellrequest)
A10 Are /compact and /clear handled by goose or passed through to the provider? RULE · code
- Answered how
When the pipeline is built, code asks the provider
manages_own_context(); if it does not, the compaction operation is added and handles/compactand/clear; if it does, no operation claims them and the text goes to the provider. Separately, the CLI's own/clearand/compacthandlers refuse with an error for such providers before anything reaches the agent (the ACP path was not read here).- Held by
crates/goose/src/agents/state_machine_builder.rs:70,crates/goose/src/agents/state_machine_builder.rs:99-106(compaction op only when the provider does not manage its own context); CLI refusalcrates/goose-cli/src/session/mod.rs:1088-1096,:1298-1306
A11 When a resumed CLI session's working directory differs from the current one, which directory does it run in? RULING · human
- Answered how
Only on resume and only when the current directory differs from the session's stored one: in an interactive session the person answers a yes/no prompt whose preselected answer is "switch back"; yes changes the process directory to the original (or keeps the current one with an error if it no longer exists). A non-interactive session stays in the current directory with a yellow warning, so code holds it there.
- Held by
crates/goose-cli/src/session/builder.rs:522-549(interactive confirm,.initial_value(true));crates/goose-cli/src/session/builder.rs:550-560(non-interactive: stay, warn); calledcrates/goose-cli/src/session/builder.rs:722-724
A12 Should this recipe be allowed to start a desktop session? RULING · human
- Answered how
Before
session/newis sent for a desktop session started from a saved recipe id or a recipe deeplink, code first checks whether this recipe's hash was accepted before and, if so, starts without asking. Otherwise it scans the recipe and the person accepts or declines in a consent request that carries the scan'shas_security_warnings; acceptance records the hash, decline throws and no session is created.- Held by
ui/desktop/src/sessions.ts:90-105(ensureRecipeConsent, calledui/desktop/src/sessions.ts:111); requestui/desktop/src/recipe/consent.ts:37-45
A14 What text does a scheduled job start from, and in which directory? RULE · code
- Answered how
Code sends the recipe's
promptas the first user message, or itsinstructionswhen the prompt is empty or whitespace, and refuses the job when both are empty. The session's working directory is the scheduler process's current directory at run time, which is also where plugin MCP servers are looked up when the recipe lists no extensions.- Held by
crates/goose/src/scheduler/full.rs:952-964(prompt, else instructions);crates/goose/src/scheduler/full.rs:881(working dir =std::env::current_dir());crates/goose/src/scheduler/full.rs:889-893(plugin MCP servers looked up there when the recipe lists no extensions)
B. Context: what the model sees
B6 Which skills are listed to the model as available? RULE · code
- Answered how
On every inference step code scans skill roots in a fixed order: the working directory's
.agents/skills,.goose/skills,.claude/skills, then project-scope plugin skill dirs, then~/.agents/skills, the goose config dir'sskills,~/.claude/skills,~/.config/agents/skills, user-scope plugins, then built-ins. The first skill parsed with a given name wins (exact, case-sensitive; within one root the winner is unspecified), and the model is shown each surviving skill's name and description, sorted by name.- Held by
crates/goose/src/agents/state_machine/ops_skills.rs:62-85(the listed skills); rootscrates/goose/src/skills/mod.rs:317-389, built-inscrates/goose/src/skills/mod.rs:442-469; first name winscrates/goose-agent/src/skills/discovery.rs:230-247
B7 Which skill (or skill supporting file) should be loaded into context now? RULING · llm
- Answered how
The session's model decides, by calling the
load_skilltool with a name. Code then runs pre-tool hooks (a denial stops it), looks the name up among discovered skills on each call: an exact name returns the rendered skill (or an error if its arguments fail to parse),skill/relative-pathreturns that skill's supporting file (or an error if it is missing or unreadable), anything else returns an error naming up to three near matches. In Chat mode the call is answered with a skipped message and nothing loads.- Held by
the session's model, via the
load_skilltool (crates/goose/src/agents/state_machine/ops_skills.rs:47-60); executedcrates/goose/src/agents/state_machine/ops_skills.rs:87-149,crates/goose/src/agents/state_machine/ops_skills.rs:321-396
B9 Does the tool list reach the model as native tool schemas or as text inside the system prompt? RULE · code
- Answered how
On every model call code checks the session's model config flag
toolshim: if true, the tool list is written into the system prompt and no tools are sent natively; otherwise they go as native schemas. The flag is set fromGOOSE_TOOLSHIM(default false) when a model config is built from user config; a resumed CLI session that reuses its saved model config keeps the saved flag.- Held by
crates/goose/src/agents/reply_parts.rs:213-224(prepare_tools_for_provider), called fromcrates/goose/src/agents/state_machine/ops_llm.rs:156-161; flagcrates/goose/src/model_config.rs:174,crates/goose/src/model_config.rs:226-240
B10 What per-turn context block (time, directory, turn budget, extension state) is added this turn? RULE · code
- Answered how
On every inference step code builds an agent-only user message tagged as turn context: the turn's start time (to the minute), the working directory, then the non-empty parts the pipeline operations contribute (platform extensions' context, and
<turn-budget>once at least half the max turns are used). It is not added when the context limit is under 32,000 tokens, when themoim::SKIPflag is set, or when its text equals the most recent turn-context message already in this turn.- Held by
crates/goose/src/agents/state_machine/inference_preparation.rs:64-84(dedupe within the turn);crates/goose/src/agents/moim.rs:92-134(compose; under 32,000 tokens none:6,:109-111); partscrates/goose/src/agents/extension_manager/lease.rs:577-591(platform extensions),crates/goose/src/agents/state_machine/ops_maxturns.rs:20-28,:42-51(turn budget)
B11 Which part of the conversation history reaches the model? RULE · code
- Answered how
On each model call code sends every agent-visible message of the stored conversation, reduced to its agent-visible content, dropping tool requests from before this turn that never got a response and messages left empty, then appends this step's turn-context message, drops signed and redacted thinking from messages whose inference metadata names a different provider, repairs the sequence (
fix_conversation) and merges consecutive messages. If the most recent message carrying inference metadata came from this same provider with a provider session id, code first asks the provider to resume that session and continues with a warning if it cannot. What a provider then forwards is its own business (see notes).- Held by
crates/goose-agent/src/inference.rs:259-287(messages_for_provider);crates/goose-agent/src/inference.rs:482-504(turn context appended, other providers' signed thinking dropped:290-305, provider resume, fix and merge)
B12 Which project description and notes enter the system prompt? RULE · code
- Answered how
On every inference step, only when the session has a
project_id, code reads that project and adds aprojectprompt part: "# Project: <name>", then its description and its content when non-empty. ACP setsproject_idatsession/newfrom the client's_meta.projectId; if the project cannot be read, nothing is added and nothing is logged.- Held by
crates/goose/src/agents/state_machine/ops_project.rs:19-38(prompt part);project_idfromcrates/goose/src/acp/server/new_session.rs:205-207(_meta.projectId,crates/goose/src/acp/server/new_session.rs:276)
B13 Which recipe instructions and final-output rules enter the system prompt? RULE · code
- Answered how
On every inference step, when the session record carries a recipe, code adds its
instructionsas therecipeprompt part and, when the recipe has aresponse, the final-output tool's own system-prompt text as thefinal_outputpart. Aresponsethat fails to build a final-output tool raises and stops the step.- Held by
crates/goose/src/agents/state_machine/ops_recipe.rs:73-82(recipe_prompt_parts); calledcrates/goose/src/agents/state_machine/ops_recipe.rs:240-246
C. Choosing the next action
C2 Is it the model's turn now (call the provider, or not)? RULE · code
- Answered how
Code calls the model only when no earlier step applied and three checks pass: the conversation has a kickoff user message, its last message carries no error, and the conversation as the provider would see it (unanswered tool requests from earlier turns dropped) ends on a non-empty user or tool message. Otherwise the inference step is not applicable and the run yields.
- Held by
crates/goose-agent/src/inference.rs:329-338(should_infer) andcrates/goose-agent/src/inference.rs:437-442(applies).
C3 Which pipeline step runs next (pending tool, approval, compaction, retry, stop, or the model)? RULE · code
- Answered how
Code walks a fixed list on every step and runs the first operation that applies: slash commands, tool approval, skills, recipe, tool execution, remaining tool calls, subagents, steer, bang shell, context compaction (only when the provider does not manage its own context), tool-pair compaction, doctor, project, retry, empty response, stop hook, exit on error, max turns, then an automatic effort-selection step only when that feature is enabled and available (
crates/goose/src/agents/services.rs:162-205), and last the model call. If none applies the run yields; an applied step's status decides whether the loop continues.- Held by
fixed step order
crates/goose/src/agents/state_machine_builder.rs:72-144, walked first-applies-wins atcrates/goose-agent/src/machine.rs:167-219.
C4 Is the model's tool name one we know, and if it is garbled, which tool did it mean? RULING · code
- Answered how
Only when the model's tool name is not exactly an advertised name, code trims it, strips a
functions.orfunctions:prefix, and compares it with each advertised tool as itself, asext.toolforext__tool, and asowner.nameorowner__namefrom owner metadata; exactly one match renames the call, zero or two-plus matches leave it unchanged. The same recovery runs again at dispatch against the extension catalog when an exact lookup fails.- Held by
crates/goose/src/agents/extension_manager/mod.rs:527-567(recover_mangled_tool_name), called fromcrates/goose/src/agents/state_machine/ops_llm.rs:92-104against the advertised set and again at dispatch (crates/goose/src/agents/extension_manager/lease.rs:605-622).
C5 Do the tool-call arguments fit the tool (coerce or reject)? RULE · code
- Answered how
For a request whose (possibly recovered) name matches an advertised tool, code walks the top-level arguments and, where the value is a string and the schema property's
typeis number/integer or boolean, converts it if it parses; everything else passes through unchanged. Arguments the provider parser could not read arrive as anErrand are answered with a parse-error text instead of running; nothing validates required fields or nested types before dispatch.- Held by
crates/goose/src/agents/reply_parts.rs:105-128(coerce_tool_arguments), called atcrates/goose/src/agents/state_machine/ops_llm.rs:112-114; parse failures becomeToolDisposition::ParseErroratcrates/goose/src/agents/state_machine/ops_toolcalling.rs:696-700.
C6 Native tool-call parsing: which tool calls does the model's reply contain (provider format)? RULE · code
- Answered how
When toolshim is off, the provider's own format parser turns its structured tool-use blocks into tool requests; in the Anthropic stream, for example, the accumulated argument string of each finished block is parsed, an empty string becomes
{}, and an unreadable one becomes anErrrequest carrying a truncation or 'Could not parse tool arguments' message. The argument parser shared by the Anthropic, OpenAI and Databricks formats is tolerant: it unwraps double-encoded objects and a JSON wrapper and attempts a repair unless the text looks truncated. The OpenAI Responses format has its own parser, which turns malformed arguments into{}.- Held by
the provider's format parser, e.g. Anthropic streaming at
crates/goose-provider-types/src/formats/anthropic.rs:1195-1233, with the shared argument parsercrates/goose-provider-types/src/json.rs:270-297(OpenAI Responses: its own atcrates/goose-provider-types/src/formats/openai_responses.rs:945-952); the other formats (openai, openai_responses, google, databricks, ollama, snowflake) sit in the samecrates/goose-provider-types/src/formats/directory. Reached throughcrates/goose/src/agents/reply_parts.rs:446-489whenmodel_config.toolshimis false (crates/goose/src/agents/reply_parts.rs:213-224).
C7 Tool shim: which tool calls did a model WITHOUT native tool calling mean (interpreter model)? RULING · llm
- Answered how
Only in toolshim mode (
model_config.toolshimtrue), and only when the code parsers of C8 found no call and the reply carries no native tool request, code sends the main model's joined reply text to a second local model and adds each{name, arguments}it returns (exceptnoop) as a tool request. The backend is Ollama unlessGOOSE_TOOLSHIM_BACKENDnames local/llama.cpp; the Ollama model isGOOSE_TOOLSHIM_OLLAMA_MODEL, elsemistral-nemo; the local model isGOOSE_TOOLSHIM_MODEL, else configLOCAL_LLM_MODEL, else an error.- Held by
a second, local model: Ollama mistral-nemo by default at
crates/goose/src/providers/toolshim.rs:1143-1150(constantcrates/goose/src/providers/toolshim.rs:55), or a llama.cpp model whenGOOSE_TOOLSHIM_BACKENDis local (crates/goose/src/providers/toolshim.rs:74-116,crates/goose/src/providers/toolshim.rs:1160-1199). Selected atcrates/goose/src/providers/toolshim.rs:1371-1385; invoked throughtoolshim_postprocess(crates/goose/src/agents/reply_parts.rs:130-144) fromcrates/goose/src/agents/reply_parts.rs:382-445only whenmodel_config.toolshimis true.
C8 Tool shim, code pass: does the text already contain tokenized or inline-JSON tool calls? RULING · code
- Answered how
In toolshim mode, on the joined reply text, code first looks for
<|tool_call_begin|> name [<|tool_call_argument_begin|>] {json} <|tool_call_end|>blocks, then for inline{"name":..., "arguments":{...}}objects, keeping a call only when the name resolves to exactly one known tool (exact, without index orfunctions.prefix, dots as__, the last dotted segment, or a unique__suffixmatch). A code-modeexecute/execute_codecall is converted to a shell call when its code contains exactly oneDeveloper.shell({command: "..."})(tree-sitter feature only, the rest of the code dropped); otherwise an unresolved execute alias inside a tokenized block rejects the whole text (the inline-JSON pass has no such check): markup stripped, no call added from that text (tool requests already in the message stay), and the interpreter of C7 is skipped.- Held by
crates/goose/src/providers/toolshim.rs:1333-1352(parse_tokenized_tool_calls_with_statuscrates/goose/src/providers/toolshim.rs:551-658,parse_inline_json_tool_callscrates/goose/src/providers/toolshim.rs:706-733,resolve_tool_namecrates/goose/src/providers/toolshim.rs:118-164), execute-alias conversion or rejection atcrates/goose/src/providers/toolshim.rs:183-350andcrates/goose/src/providers/toolshim.rs:443-493.
C9 Should a failed model request be retried, and after how long? RULE · code
- Answered how
When the provider does not manage its own context and the stream opened, code retries an error that arrives before the stream's first item if it is a rate limit, server or network error, up to the provider's
retry_config().max_retries(default 3), sleeping the server's rate-limit delay or 1 s doubling to a 30 s cap with 0.8-1.2 jitter (no sleep whenGOOSE_PROVIDER_SKIP_BACKOFFis true). An error opening the stream, or one after the first item, is not retried at this level. A separate generic helper that providers may call retries 4xx too (except permanent markers) and refreshes credentials once on an auth error.- Held by
crates/goose/src/agents/reply_parts.rs:322-380usingcrates/goose-provider-types/src/retry.rs:108-117(should_retry),crates/goose-provider-types/src/retry.rs:71-87(backoff) andcrates/goose-provider-types/src/retry.rs:14-17(defaults); generic helpercrates/goose-provider-types/src/retry.rs:197-271.
C10 The model answered with nothing: retry, or give up and tell the person? RULE · code
- Answered how
Code calls an assistant reply empty when every block is whitespace text or unsigned empty thinking and no chunk hit the output-token limit. An empty reply is re-requested up to 3 more times (4 calls in all), except when the conversation ends on an all-successful tool result or the turn is cancelled; after the last empty reply code stores a hidden fallback message, which the empty-response step then reveals unless the session's recipe has a
retryblock.- Held by
crates/goose-agent/src/inference.rs:507-602(emptinesscrates/goose-agent/src/inference.rs:179-187,MAX_EMPTY_RESPONSE_RETRIEScrates/goose-agent/src/inference.rs:78), revealed bycrates/goose/src/agents/state_machine/ops_empty_response.rs:23-54.
C11 Which of the model's repeated or duplicated stream pieces are kept? RULE · code
- Answered how
While streaming one attempt, code keeps the first tool request for each id and silently drops any later request with the same id; when a chunk carries a tool request, any thinking block in it equal to one already accumulated from the same role is removed. A chunk left empty (and not flagged as hitting the output-token limit) is accumulated without being shown.
- Held by
crates/goose-agent/src/inference.rs:540-568(id de-duplicationcrates/goose-agent/src/inference.rs:556-561; repeated thinkingcrates/goose-agent/src/inference.rs:99-116).
D. Gatekeeping: may this action run?
D1 Given the session's mode and the tool's saved permission, does this tool call run, wait for a person, or get refused? RULE · code
- Answered how
Code switches on the session's mode. Chat gives no verdict (and the approval pass does not run at all in Chat); Auto allows every call before any saved permission is read; Approve and SmartApprove first use the tool name's saved user permission (AlwaysAllow / NeverAllow / AskBefore), then in SmartApprove a read-only annotation allows (D3), then
manage_extensionsalways asks, then SmartApprove hands uncached tools to the LLM judge (D2), and anything left asks the person.- Held by
crates/goose/src/permission/permission_inspector.rs:144-227(PermissionInspector::inspect); mode read fromsession.goose_modeatcrates/goose/src/agents/state_machine/ops_tool_approval.rs:49.
D2 Is this tool call read-only, so it can run in Smart Approve mode without asking the person? RULING · llm
- Answered how
Only in SmartApprove, for a tool with no saved user permission, no read-only annotation and no cached AskBefore: one LLM call (the session's provider and the session's model config, else the global GOOSE_PROVIDER/GOOSE_MODEL) is shown the batch of calls as untrusted JSON and asked to return the ids of the read-only ones through the offered tool. A call whose id comes back runs without asking; every other candidate asks the person and its tool name is cached as AskBefore.
- Held by
crates/goose/src/permission/permission_judge.rs:145-185(detect_read_only_requests), called fromcrates/goose/src/permission/permission_inspector.rs:229-246. Model: the session'smodel_config, else global provider/model (crates/goose/src/permission/permission_judge.rs:13-33); provider: the session's (crates/goose/src/permission/permission_inspector.rs:231-234). No separate judge-model setting exists.
D3 Does the tool's author declare it read-only, so Smart Approve may run it without asking? RULING · external
- Answered how
In SmartApprove only, each turn's tool list is scanned and every tool whose MCP definition carries
read_only_hint == Some(true)goes into a per-agent set; a call to a tool in that set runs without asking, unless the person has a saved permission for that tool name, which is checked first. A tool markedread_only_hint == Some(false)is written to the smart_approve cache as AskBefore; a tool with no annotation falls through to the LLM judge (D2).- Held by
crates/goose/src/permission/permission_inspector.rs:173-176honours the MCP server'sannotations.read_only_hint, collected atcrates/goose/src/permission/permission_inspector.rs:52-64and refreshed each turn in SmartApprove bycrates/goose/src/agents/state_machine/inference_preparation.rs:56-59. A write annotation is cached as AskBefore (crates/goose/src/config/permission.rs:91-107).
D4 May this tool call run? (the person's Allow once / Always allow / Deny) RULING · human
- Answered how
When a call needs approval, the desktop app shows Allow Once, Always Allow and Deny; Always Allow is hidden when the request carries a security message. The click resolves the pending ACP permission request; back in the agent, Allow Once or Always Allow marks the call executable, anything else does not, and Always Allow (or an Always Deny from another ACP client) is also saved for the tool name.
- Held by
ui/desktop/src/components/ToolApprovalButtons.tsx:95-143(buttons; Always Allow hidden when a security prompt is attached,:132), resolved byresolveAcpPermissionRequest(ui/desktop/src/acp/permissionRequests.ts:33-52); request built atcrates/goose/src/agents/state_machine/ops_tool_approval.rs:107-127, answer applied atcrates/goose/src/agents/state_machine/ops_tool_approval.rs:57-76.
D5 May this tool call run? (the person's Allow / Always Allow / Deny / Cancel in the terminal) RULING · human
- Answered how
In an interactive CLI session, when the agent emits a tool confirmation the terminal shows the tool and its arguments and a select of Allow / Always Allow / Deny / Cancel; Always Allow is left out when a security message is attached. The choice is submitted to the agent as the confirmation; Cancel also stops the turn and records the call as cancelled.
- Held by
crates/goose-cli/src/session/mod.rs:2297-2337(prompt_tool_confirmation), called atcrates/goose-cli/src/session/mod.rs:1400-1408wheninteractiveis true; Always Allow only when no security prompt is attached (crates/goose-cli/src/session/mod.rs:2312-2336).
D6 May this tool call run? (the paired chat user's reply: approve, approve always, deny, deny always) RULING · human
- Answered how
When the agent asks for a confirmation during a gateway turn, the paired chat user is sent the tool name, pretty-printed arguments, any security message and the four reply options, and the request id is queued for that user. Their next message is matched exactly (case-insensitive, trimmed): approve/yes/y, approve always, deny/no/n, deny always; the answer is applied to the oldest queued request. Anything else re-asks.
- Held by
crates/goose/src/gateway/handler.rs:215-245(reply parsing), prompt sent atcrates/goose/src/gateway/handler.rs:623-683, answer submitted atcrates/goose/src/gateway/handler.rs:247-265. Reached only by a paired user, and only a listed one when a user allowlist is configured (crates/goose/src/gateway/handler.rs:126-134,:192-199; no allowlist admits everyone,:829-833).
D7 In a goose run with no terminal attached, what happens to a tool call that is waiting for approval? RULE · code
- Answered how
When the CLI is not interactive and a confirmation arrives, code reads the mode from the global config (not from the session): Approve or SmartApprove cancels the run and returns an error; any other mode answers AllowOnce automatically, recorded only by a WARN log line.
- Held by
crates/goose-cli/src/session/mod.rs:1409-1428: Approve or SmartApprove stops the run with an error (crates/goose-cli/src/session/mod.rs:1415-1422); otherwise AllowOnce with a warning log (crates/goose-cli/src/session/mod.rs:1424-1427).
D8 Should any tool call run in this session? RULE · code
- Answered how
Code: when the session's mode is Chat, the approval pass does not run, and each tool-handling operation answers every executable call with the fixed
CHAT_MODE_TOOL_SKIPPED_RESPONSEtext instead of running it (a call that failed to parse still gets its parse error, and one for a tool its turn did not advertise gets "Tool '<name>' is not available.").- Held by
crates/goose/src/agents/state_machine/ops_tool_approval.rs:50-52(no approval pass) andcrates/goose/src/agents/state_machine/ops_remaining_tool_calls.rs:148-153(call answered withCHAT_MODE_TOOL_SKIPPED_RESPONSE); same check incrates/goose/src/agents/state_machine/ops_toolcalling.rs:998,crates/goose/src/agents/state_machine/ops_skills.rs:345,crates/goose/src/agents/state_machine/ops_recipe.rs:277.
D9 When a scheduled recipe runs with nobody watching, which approval regime governs its tool calls? RULING · code
- Answered how
Code fixes it to Auto for every scheduled job: the scheduler creates the job's session in Auto and sets Auto again after switching the provider, ignoring the configured GOOSE_MODE. So D1 allows every call; only an inspector override (scanner, adversary) or a PreToolUse hook can still stop one, and an override that asks for approval is never answered.
- Held by
crates/goose/src/scheduler/full.rs:878-907: session created withGooseMode::Auto(crates/goose/src/scheduler/full.rs:884) and set to Auto again after the provider switch (crates/goose/src/scheduler/full.rs:905-907); the in-file test setsGOOSE_MODE=chat(crates/goose/src/scheduler/full.rs:1367) and asserts Auto (crates/goose/src/scheduler/full.rs:1423).
D10 Is this shell command dangerous enough to stop and ask the person first? RULING · code
- Answered how
Only when
SECURITY_PROMPT_ENABLEDis true (default false; envSECURITY_PROMPT_ENABLED_OVERRIDEwins) and only for a tool named exactlyshell: code scores the command against fixed patterns, takes the max with an optional command-classifier score, may blend in an optional prompt-classifier score of recent user messages (never below the pattern score), and if the result is strictly aboveSECURITY_PROMPT_THRESHOLD(default 0.8) turns the call into RequireApproval with a Security Alert message. Every other call gets no verdict from this seat.- Held by
crates/goose/src/security/security_inspector.rs:59-87->crates/goose/src/security/mod.rs:77-224(analyze_tool_requests) ->crates/goose/src/security/scanner.rs:132-201. Pattern rules incrates/goose/src/security/patterns.rs; optional classifier viacrates/goose/src/security/classification_client.rs:141(a classifier model). ThresholdSECURITY_PROMPT_THRESHOLD, default 0.8 (crates/goose/src/security/scanner.rs:126-130); a finding above it becomes RequireApproval with a Security Alert (crates/goose/src/security/security_inspector.rs:27-36).
D11 Given the user's task and their written rules, is this tool call safe to run? RULING · llm
- Answered how
Only when
adversary.mdexists in the goose config dir, and only for tools named in itstools:line (default justshell), in every mode: one LLM call per such tool call, with the session's own provider and model, sees the first user message, up to four recent user messages, the call as JSON and the rules, and is asked for ALLOW or BLOCK. A reply that starts with BLOCK or has a line starting BLOCK becomes a Deny that overrides every other verdict; anything else is an Allow, which changes nothing in the combination (D12).- Held by
crates/goose/src/security/adversary_inspector.rs:249-347(consult_llm) viacrates/goose/src/security/adversary_inspector.rs:364-469(inspect); the session's own provider and model (crates/goose/src/security/adversary_inspector.rs:257-262,:302).
D12 When the inspectors disagree about a call, which verdict wins? RULE · code
- Answered how
Code: the permission inspector's verdicts form the baseline (a call with none needs approval); then every other inspector's result is applied in order: a Deny moves the call to denied, a RequireApproval moves an approved call to needs-approval unless it is already denied, and an Allow changes nothing. So Deny beats RequireApproval beats Allow, whichever inspector said it.
- Held by
crates/goose/src/tool_inspection.rs:170-261(apply_inspection_results_to_permissions) after the permission baseline atcrates/goose/src/permission/permission_inspector.rs:73-131. Deny beats RequireApproval beats Allow; an Allow never upgrades another verdict (crates/goose/src/tool_inspection.rs:253-256).
D13 Does the installed policy hook allow this tool call? RULING · external
- Answered how
Only when an enabled plugin ships
hooks/hooks.jsonwith a PreToolUse rule whose matcher fits the tool: just before the call executes, code runs each matching command with the call's context as JSON; the first one that exits 2 or prints{"decision":"block"}denies the call and the model gets a policy-denial error. With no matching rule the call proceeds.- Held by
crates/goose/src/hooks/mod.rs:688-760(emit_blocking_with_outcome) runs the plugin's command (an external program from<plugin-root>/hooks/hooks.json,crates/goose/src/hooks/mod.rs:4,:438); called before execution atcrates/goose/src/agents/state_machine/ops_toolcalling.rs:120-161andcrates/goose/src/agents/state_machine/ops_remaining_tool_calls.rs:161-171. Deny = exit status 2 or{"decision":"block"}(crates/goose/src/hooks/mod.rs:681-683).
D16 May a tool called from inside Code Mode's JavaScript run? RULE · code
- Answered how
Only when Code Mode is compiled in (feature
code-mode) and enabled (off by default): code lets the script call any tool whose name is in the catalog built at the start of the outerexecute_javascriptcall (the current extension lease's tools, minus MCP-app tools, tools hidden from the model, Code Mode's own and summon'sdelegate), and runs it straight through the extension lease. No permission check, inspector, approval or PreToolUse hook is consulted for these nested calls; only the outer call went through D1-D13. This is deliberate: a maintainer's answer on issue #12376, which asked for one policy boundary, was "if you select code mode, you opt out of the rest of the machinery."- Held by
crates/goose/src/agents/platform_extensions/code_execution.rs:116-136(callback; catalog filter atcrates/goose/src/agents/platform_extensions/code_execution.rs:57-67) ->dispatch_tool(crates/goose/src/agents/platform_extensions/code_execution.rs:172-199) ->ExtensionLease::call(crates/goose/src/agents/extension_manager/lease.rs:657-667), which resolves and calls the tool directly.
D17 May an MCP app's UI call its extension's tool directly, without the model? RULE · code
- Answered how
Code: when an ACP client asks to call a tool on an app's behalf, the tool must exist under that extension and be visible to apps, the arguments must be an object or null, and the session must be in Auto mode; if all hold, the tool runs at once through the extension lease with no permission inspector, scanner, adversary, approval or PreToolUse hook. Any failed check returns an error and nothing runs.
- Held by
crates/goose/src/acp/server/tools.rs:68-160(on_call_tool): tool owned by the app's extension and app-visible (crates/goose/src/acp/server/tools.rs:80-89), session in Auto (crates/goose/src/acp/server/tools.rs:117-120); thencall_for_appruns it directly (crates/goose/src/agents/extension_manager/lease.rs:669-682).
D18 When goose drives another agent over ACP (Claude, Codex, Copilot, Amp, Pi) and that agent asks to run a tool, may it? RULING · code
- Answered how
Code answers from the goose mode the ACP provider holds, read once at the start of each streamed request: Auto replies AllowOnce at once, Chat replies RejectOnce (and records the call as rejected), and Approve or SmartApprove forward the request to the person as an ActionRequired carrying the agent's tool title, raw input and first text content, then relay their answer back to the delegated agent.
- Held by
crates/goose/src/acp/provider.rs:1028-1037withpermission_decision_from_mode(crates/goose/src/acp/provider.rs:2388-2394): Auto -> AllowOnce, Chat -> RejectOnce, Approve/SmartApprove -> ask the person (crates/goose/src/acp/provider.rs:1039-1062; ActionRequired built atcrates/goose/src/acp/provider.rs:2120-2161; answered in D4).
E. Execution and observation
E1 When the model asks for several tools at once, do they run together or one after another? RULING · code
- Answered how
Always together: code walks the reply's pending
Executerequests in order, awaiting each one's PreToolUse hooks and tool resolution, which returns a not-yet-started call future; it then polls all those futures at once through oneselect_all, with no cap, and records each result as it completes. Only subagent calls have a concurrency limit (a separate operation).- Held by
crates/goose/src/agents/state_machine/ops_toolcalling.rs:1021-1055(sequential dispatch, thenselect_all), polled atcrates/goose/src/agents/state_machine/ops_toolcalling.rs:1082-1111; pre-hooks per call atcrates/goose/src/agents/state_machine/ops_toolcalling.rs:419-439; the lazy call future atcrates/goose/src/agents/extension_manager/lease.rs:719-749.
E2 Which extension and server tool does a tool name run on? RULE · code
- Answered how
Before dispatch, the tool-execution step keeps only requests whose name is in the lease's current tool list (minus the skills extension); the rest fall to the remaining-tool-calls step. At dispatch, code looks the name up exactly in the lease's tool catalog, else repairs it with C4's recovery against the catalog, and runs it on the catalog entry's extension client and server tool name; for a call from an MCP app the search is limited to that app's extension and the tool must be app-visible.
- Held by
crates/goose/src/agents/extension_manager/lease.rs:593-655(resolve_tool), called fromcrates/goose/src/agents/extension_manager/lease.rs:657-682(call,call_for_app); known-set filter atcrates/goose/src/agents/state_machine/ops_toolcalling.rs:981-992.
E3 How long may an extension tool call run before goose gives up on it? RULING · code
- Answered how
When an MCP extension is connected, code fixes its timeout: the extension config's
timeout, else theGOOSE_DEFAULT_EXTENSION_TIMEOUTsetting, else 300 s. Each tool call to that client then races the server's reply against that timer and the turn's cancel token; on the timer, code sends an MCP cancel notification and the call fails with a timeout error.- Held by
crates/goose/src/agents/mcp_client.rs:616-641(await_response), duration fromcrates/goose/src/agents/extension_manager/mod.rs:123-129(resolve_timeout, applied at connect,crates/goose/src/agents/extension_manager/mod.rs:889-890) andcrates/goose/src/config/extensions.rs:10.
E4 How long may a shell command run? RULING · llm
- Answered how
The model may pass
timeout_secswith eachshellcall; if it does not, code uses theGOOSE_DEFAULT_EXTENSION_TIMEOUTsetting, else 300 s. A resolved value of 0 (from the model or the setting) means no deadline at all; otherwise the deadline covers building the command and running it, and a command past it is reported as timed out.- Held by
the model's timeout_secs argument to the developer shell tool (
crates/goose/src/agents/platform_extensions/developer/shell.rs:182-189), applied atcrates/goose/src/agents/platform_extensions/developer/shell.rs:416-440; defaultcrates/goose/src/agents/platform_extensions/developer/shell.rs:408-414.
E5 How much of a shell command's output does the model see, and where does the rest go? RULING · code
- Answered how
Code checks stdout, stderr and the interleaved display text separately: one that is over 2000 lines or 50 000 bytes is written whole to a per-tool temp file (one of 8 rotating slots) and replaced by its last 50 lines, trimmed to the last 10 000 bytes, with a separate notice naming the file and how to read it. Anything under both limits passes in full.
- Held by
crates/goose/src/agents/platform_extensions/developer/shell.rs:747-787(truncate_output), limitscrates/goose/src/agents/platform_extensions/developer/shell.rs:161-166, applied atcrates/goose/src/agents/platform_extensions/developer/shell.rs:286-337; noticecrates/goose/src/agents/platform_extensions/developer/shell.rs:717-731.
E7 What does the model read back when its tool request cannot run (unknown, not advertised, unparsed, declined, chat mode, unhandled)? RULE · code
- Answered how
Code answers each request no earlier step ran with a fixed text by case: unparsed arguments get the parse-error text; a request for a name the producing turn did not advertise gets "Tool '<name>' is not available." (marked unclaimed, so the next model call also lists the available tools); in chat mode every other executable request gets the skip text as a success; a declined one gets
DECLINED_RESPONSE. An advertised request nobody executed first runs the PreToolUse hooks (a denial's message becomes the answer), then gets "not available" or, forrecipe__final_outputwithout a recipe response, "Final output tool not defined"; a request still left over gets "This tool call was not handled.". With no cached extension lease, and unless every execute request was generated by an operation and has no approval history (then a lease is resolved), the tool-execution step answers execute requests with the expired-approval text.- Held by
crates/goose/src/agents/state_machine/ops_remaining_tool_calls.rs:81-247, withcrates/goose/src/agents/state_machine/ops_toolcalling.rs:955-979(no lease) andcrates/goose/src/agents/state_machine/ops_toolcalling.rs:998-1019(chat mode); texts incrates/goose/src/agents/tool_execution.rs:151-162.
E9 Which parts of an extension's reply are trusted to change goose itself (enable or disable extensions, app metadata)? RULE · code
- Answered how
On every tool result, code deletes the trusted-tool-update key and
goose.mcpAppmetadata, and, unless the extension is a platform (in-process) one, removes anygoose_extension_mutationrequest. On the tool-execution path, a mutation still present (so only from a platform extension) is then parsed as enable/disable of a named extension and applied to the session before the result reaches the model.- Held by
crates/goose/src/agents/extension_manager/lease.rs:732-736, usingcrates/goose/src/agents/extension_manager/mod.rs:583-607(remove_untrusted_mcp_app_meta) andcrates/goose/src/agents/extension_manager/mod.rs:62-69(ExtensionMutation::take); applied bycrates/goose/src/agents/extension_manager/mod.rs:1107-1130(applying_mutation), called atcrates/goose/src/agents/state_machine/ops_toolcalling.rs:456-458.
E11 An MCP server asks the person for input mid-tool (elicitation): who answers? RULING · human
- Answered how
If the client capabilities carry an
elicitation_handler, that function answers first and the person is never asked. Otherwise code resolves the session and tool call, turns the server's form or URL request into an action-required prompt, and waits up to 300 s for the person's accept (with data), decline or cancel, which is passed back to the server.- Held by
the person, through ActionRequiredManager::request_and_wait at
crates/goose/src/agents/mcp_client.rs:418-426, unless an elicitation_handler atcrates/goose/src/agents/mcp_client.rs:381-383answers first; answers mapped atcrates/goose/src/acp/server/elicitation.rs:216-230(ACP) andcrates/goose/src/agents/services.rs:868-873.
E12 Did the developer edit find exactly the text the model meant to replace? RULE · code
- Answered how
Code reads the file and counts exact, non-overlapping occurrences of the model's
beforetext: exactly one is replaced byafterand the file is written; zero or more than one returns an error and writes nothing. There is no fuzzy or whitespace-tolerant matching; an emptybeforematches at every character boundary, so it is refused as many matches unless the file is empty, in which caseafterbecomes the content.- Held by
crates/goose/src/agents/platform_extensions/developer/edit.rs:129-168(string_replace), called atcrates/goose/src/agents/platform_extensions/developer/edit.rs:83-120.
F. Limits and memory
F1 Has the conversation grown big enough that goose should summarise it before the next model call? RULING · code
- Answered how
Before a model call (never when the turn is about to end on an assistant reply), code counts the session's reported total tokens plus tool-result tokens not yet reported (or estimates all agent-visible messages when no usage is recorded) and compacts when tokens / context limit exceeds the threshold. The threshold is
GOOSE_AUTO_COMPACT_THRESHOLD(default 0.8), lowered toGOOSE_AUTO_COMPACT_TOKEN_LIMIT(default 225,000) / context limit when that is smaller; a threshold of 0 or less, or 1 or more, switches the trigger off. The operation is not built at all when the provider manages its own context.- Held by
crates/goose/src/agents/state_machine/ops_compaction.rs:218-227(proactive branch ofCompactionOperation::run; ratio testover_thresholdatcrates/goose/src/agents/state_machine/ops_compaction.rs:66-71); threshold read byTurnSettings::from_config(GOOSE_AUTO_COMPACT_THRESHOLDandGOOSE_AUTO_COMPACT_TOKEN_LIMIT,crates/goose/src/agents/services.rs:150-155) and capped bycapped_compaction_threshold(crates/goose/src/context_mgmt/mod.rs:44-53, called atcrates/goose/src/agents/services.rs:761-765; default 0.8 atcrates/goose-context-management/src/lib.rs:32; token limit 225,000 atcrates/goose/src/context_mgmt/mod.rs:25).auto_compact_threshold(crates/goose/src/context_mgmt/mod.rs:30-41) applies the same rule but a Grep overcrates/found it called only fromcrates/goose/tests/auto_compact_token_limit.rs.
F2 The provider refused the request as too long: compact and try again, or stop trying? RULE · code
- Answered how
When the conversation ends in a provider error already classified as
ContextLengthExceeded, code counts the agent-hidden context-length errors since the kickoff; while that count is at mostMAX_CONTEXT_ERROR_COMPACTIONS(2) it hides the error from the model and compacts, then the turn retries. Past the cap it declines, and the exit-on-error step ends the turn with the error left visible (the lifecycle test pins exactly 2 compactions).- Held by
crates/goose/src/agents/state_machine/ops_compaction.rs:202-217(capMAX_CONTEXT_ERROR_COMPACTIONS = 2atcrates/goose/src/agents/state_machine/ops_compaction.rs:25; error hidden before compacting atcrates/goose/src/agents/state_machine/ops_compaction.rs:229-240).
F3 When the conversation is compacted, what from the work so far is carried forward in the summary? RULING · llm
- Answered how
The session's own provider and model get one call (re-sent on a context overflow with a rising share of tool responses requested for removal,
crates/goose-context-management/src/summarize.rs:133-174): thecompaction.mdtemplate (a user copy under<config>/prompts/wins) rendered with every agent-visible message except turn-context events, plus a fixed user line asking for the summary. If the reply parses as the structured JSON summary andcompaction_summary.mdrenders it to non-empty text, that rendering replaces it; otherwise the raw reply text is kept as the summary unchanged.- Held by
crates/goose-context-management/src/summarize.rs:124-182(summarize), called bycrates/goose/src/context_mgmt/mod.rs:381-410(do_compact) throughGooseCompactionModel::complete(crates/goose/src/context_mgmt/mod.rs:331-347, which callsmodel_config::complete_one_shot). Model: the session's own provider and model.
F4 When the history is too long even for the summariser, which tool results are thrown away first? RULING · code
- Answered how
Only when the summariser call itself fails with a context-length error, code retries with 10%, 20%, 50% and then 100% of the messages carrying tool responses removed (asking for at least one each time; with an odd number of tool responses the 100% pass leaves the last one, so a single tool response is never removed), taking them alternately from just before and just after the middle of the list of tool-response messages, so the earliest and latest survive longest. With no tool responses at all it fails on the first overflow.
- Held by
crates/goose-context-management/src/summarize.rs:38-75(filter_tool_responses); percentagesREMOVAL_PERCENTAGES = [0,10,20,50,100]atcrates/goose-context-management/src/summarize.rs:14; retry loopcrates/goose-context-management/src/summarize.rs:133-177.
F5 After compaction, which original messages does the model still see word for word, and is it told to keep working or just continue the chat? RULING · code
- Answered how
Code makes every original message invisible to the model while keeping whatever visibility it already had for the person, and gives the model the summary, then one continuation line. On automatic compaction it also re-appends, text only, the most recent agent-visible user message that has text and no tool content, plus that turn's latest turn-context event; manual
/compactkeeps no message. The continuation line is the manual text for/compact, 'continue the conversation naturally' when the preserved message was the last one (only turn-context after it), and 'continue calling tools' otherwise.- Held by
crates/goose/src/context_mgmt/mod.rs:143-239(compact_messages: preserved messagecrates/goose/src/context_mgmt/mod.rs:143-176; originals made agent-invisiblecrates/goose/src/context_mgmt/mod.rs:189-193; continuation choicecrates/goose/src/context_mgmt/mod.rs:200-206; turn-context carrycrates/goose/src/context_mgmt/mod.rs:222-239).
F6 Which earlier tool calls and their results are replaced by a one-line summary to save context? RULING · code
- Answered how
Only when
GOOSE_TOOL_PAIR_SUMMARIZATIONis set true (default false) and the provider does not manage its own context: code counts agent-visible tool calls, excludes the ones made since the current kickoff, and once more than cutoff + 10 remain it takes the oldest 10. The cutoff isGOOSE_TOOL_CALL_CUTOFF, else 3 x (context limit x compaction threshold) / 20,000 clamped to 10..500. A pair is skipped if it is not exactly two messages, shares a message with a pair already summarised, or has sibling calls answered elsewhere.- Held by
crates/goose/src/agents/state_machine/ops_tool_pair_compaction.rs:65-183(ToolPairCompactionOperation::run); selectioncrates/goose/src/context_mgmt/mod.rs:424-455(tool_ids_to_summarize); cutoffcrates/goose/src/context_mgmt/mod.rs:414-422(compute_tool_call_cutoff, applied atcrates/goose/src/agents/services.rs:766-768) orGOOSE_TOOL_CALL_CUTOFF(crates/goose/src/agents/services.rs:156).
F7 What does the one-line replacement for an old tool call and its result say? RULING · llm
- Answered how
For each pair F6 selects, the session's provider and model get one call with a fixed inline system prompt and the pair's agent-visible messages formatted as text; whatever comes back is kept as the replacement, re-roled as a user message, visible to the model only, dated to the pair's last message. Present only when F6 is on.
- Held by
crates/goose/src/context_mgmt/mod.rs:492-538(summarize_tool_call;complete_one_shotatcrates/goose/src/context_mgmt/mod.rs:523-531), session model.
F8 Has the agent taken as many steps as it may without coming back to the person? RULING · code
- Answered how
Code counts assistant turns since the person's kickoff message; once the count reaches the budget it ends the turn with a fixed 'Would you like me to continue?' message. The budget is the session's
max_turns(e.g. CLI--max-turns), elseGOOSE_MAX_TURNS, else 1000. From half the budget on, the model is shown a<turn-budget>n/max used</turn-budget>part each turn.- Held by
crates/goose/src/agents/state_machine/ops_maxturns.rs:53-67(MAX_TURNS_MESSAGEatcrates/goose/src/agents/state_machine/ops_maxturns.rs:14); budgetcrates/goose/src/agents/services.rs:760(session_config.max_turns, elseTurnSettings.max_turnsfromconfig.default_max_turns()atcrates/goose/src/agents/services.rs:135, which isGOOSE_MAX_TURNSelse 1000 atcrates/goose/src/config/base.rs:1339-1341); budget hintcrates/goose/src/agents/state_machine/ops_maxturns.rs:20-28,42-51.
F9 Has the agent called the same tool with the same arguments too many times in a row? RULING · code
- Answered how
At this pin it is never answered 'too many': the agent registers the inspector with no limit, and with no limit
check_tool_callalways allows. Even with a limit,inspectchecks each call on a temporary copy of the inspector's state that is never written back, so repeats never accumulate. The CLI--max-tool-repetitionsvalue is stored and never read.- Held by
crates/goose/src/tool_monitor.rs:51-80,99-134(RepetitionInspector), registered withNoneatcrates/goose/src/agents/services.rs:572.
F10 After how many stop-hook refusals since the kickoff does goose override the hook and end the turn anyway? RULE · code
- Answered how
When a stop hook denies, code counts the denial notes already in the conversation since the kickoff, plus this one; if that exceeds the cap (
GOOSE_STOP_HOOK_BLOCK_CAP, default 8) it ends the turn anyway with a visible override warning, otherwise it adds the denial to the model's context and the agent keeps going. Every denial since the kickoff counts, whatever work happened between them.- Held by
crates/goose/src/agents/state_machine/ops_stop_hook.rs:101-111; capGOOSE_STOP_HOOK_BLOCK_CAP, default 8 (crates/goose/src/agents/services.rs:52,146-148).
G. Judging the result
G1 The person set a goal with /goal; has the agent fully met it before it stops? RULING · llm
- Answered how
Only when the person has set a goal with
/goal <text>: the first time the turn would end after the kickoff, code adds one hidden user message asking the session model to check whether the goal is fully met and continue if not. The model's own next actions are the answer; the next time the turn would end (unless a/grindgoal is also set), code clears the goal without reading any verdict from the reply.- Held by
crates/goose/src/agents/state_machine/ops_retry.rs:183-201(one hidden nudge per kickoff, markedNUDGED); goal cleared atcrates/goose/src/agents/state_machine/ops_retry.rs:219-221. The model that answers is the session model.
G2 With /grind, is the work finished, or must the agent keep going? RULING · code
- Answered how
Only when the person has set
/grind <text>: every time the turn would end, code answers 'not done' and adds a hidden 'Keep working' message, without reading the model's reply. The work stops when the turn budget (F8) runs out or the person clears the grind (/grind off|clear|none); if a/goalis also set and not yet nudged, the goal nudge goes first.- Held by
crates/goose/src/agents/state_machine/ops_retry.rs:203-217(grind nudge on every turn end); cleared by the person atcrates/goose/src/agents/state_machine/ops_retry.rs:110-117.
G3 When a recipe with a retry block finishes, did the work actually succeed? RULING · external
- Answered how
Only for a recipe with a
retryblock, and only when the turn ends with no/goalor/grindpending: code runs each of the recipe author's shell commands in order (sh -c, orcmd /Con Windows), and the run counts as successful only if every one exits 0; the first non-zero exit means 'failed'. Each command gets the recipe'stimeout_seconds, elseGOOSE_RECIPE_RETRY_TIMEOUT_SECONDS, else 300 s; an empty check list passes.- Held by
the recipe author's shell commands, run by
crates/goose/src/agents/retry.rs:14-39(execute_success_checks_with_timeout), called atcrates/goose/src/agents/state_machine/ops_retry.rs:223-242; timeout default 300 atcrates/goose/src/agents/types.rs:4, read atcrates/goose/src/agents/services.rs:136-140.
G4 The success checks failed: wipe the attempt and run the recipe again, or stop and report failure? RULE · code
- Answered how
When G3 says 'failed', code reads the attempt count stored on the kickoff message; if it has reached the recipe's
max_retriesit stops with a 'Maximum retry attempts exceeded' error. Otherwise it runs the recipe's optionalon_failurecommand (timeouton_failure_timeout_seconds, elseGOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS, else 600 s; if that command fails, its error is shown and no retry follows), cuts the conversation back to the kickoff, increments the count and runs again. So a recipe gets at mostmax_retriesretries after the first run, andon_failureis not run after the last failure.- Held by
crates/goose/src/agents/state_machine/ops_retry.rs:244-277(attempt countercrates/goose/src/agents/state_machine/ops_retry.rs:63-72;on_failurecrates/goose/src/agents/state_machine/ops_retry.rs:262-271, timeout default 600 atcrates/goose/src/agents/types.rs:7; resetcrates/goose/src/agents/state_machine/ops_retry.rs:49-55).
G5 Does a configured policy hook accept that the agent's turn is finished? RULING · external
- Answered how
Only when a plugin has configured a Stop hook: when the turn would end, code runs each matching hook command in order with a JSON payload carrying the trailing user-visible assistant text and the working directory. The first hook that denies (exit status 2 with the reason on stderr, or
{"decision":"block"}on stdout) blocks the end and its reason goes to the model (capped by F10); a hook that fails to run or errors is skipped as if it allowed, even withon_failure: block, because blocking on failure applies only to PreToolUse.- Held by
plugin hook commands, run by
crates/goose/src/hooks/mod.rs:684-784(emit_blocking->emit_blocking_with_outcome), called bycrates/goose/src/agents/state_machine/ops_stop_hook.rs:80-119with the trailing user-visible assistant text and working dir (crates/goose/src/agents/state_machine/ops_stop_hook.rs:90-94).
G6 Is the structured answer the recipe promised valid against the recipe's JSON schema? RULE · code
- Answered how
Only for a recipe that declares a
responseJSON schema: when the model callsrecipe__final_outputon its own (not alongside other tools, not in Chat mode, and not denied by a PreToolUse hook), code validates the call's arguments against the schema with thejsonschemacrate. Valid output is accepted and echoed as one line; any validation error is returned to the model as an error tool result listing the errors and the expected schema.- Held by
crates/goose/src/agents/final_output_tool.rs:154-175(validate_json_output), executed bycrates/goose/src/agents/state_machine/ops_recipe.rs:304-353; schema sanity at recipe loadcrates/goose/src/agents/final_output_tool.rs:36-53.
G7 The model stopped; has it delivered the recipe's final output, or must it be told to? RULE · code
- Answered how
Only for a recipe with a
responseschema: when the turn would end, code looks since the kickoff for arecipe__final_outputcall whose tool result is a non-error 'Final output successfully collected.'; if one exists the turn may end, otherwise it adds a model-only 'You MUST call thefinal_outputtool NOW' message and the agent continues. A provider that cannot receive goose's built-in tools gets an error up front instead.- Held by
crates/goose/src/agents/state_machine/ops_recipe.rs:380-390(FinalOutputTool::successful_outputatcrates/goose/src/agents/final_output_tool.rs:55-95; continuationFINAL_OUTPUT_CONTINUATION_MESSAGEatcrates/goose/src/agents/final_output_tool.rs:14-15).
G8 In this changed file, which lines have real problems, and how serious is each? RULING · llm
- Answered how
When a person runs
goose review(default orchestrated mode, not--checks-only): code splits the diff per file and, up to 4 at a time, runs onegoose run --no-session --quiet --no-profilesubprocess per file with the base review prompt (default or--prompt), the file's diff and a JSON-only output demand. That model's reply is the answer: the first balanced{...}is parsed as{findings:[...]}, missing severity becomes 'medium' and missing path becomes the file. Model is--modelor the provider default; turn limit--turn-limit, else 25.- Held by
crates/goose-cli/src/commands/review/orchestrator.rs:307-394(run_main_pass_in_parallel; subprocesscrates/goose-cli/src/commands/review/orchestrator.rs:228-290; turn limitDEFAULT_CHECK_TURN_LIMIT = 25atcrates/goose/src/checks/mod.rs:17).
G9 Does the change break this review check (an .agents/checks/*.md file in the repo or a sub-tree, a global check under ~/.config/goose/checks or ~/.config/agents/checks, or the rules of an .agents/REVIEW.md), and how badly? RULING · llm
- Answered how
On the default orchestrated
goose reviewpath (not--dry-run,--summary-onlyor--no-orchestrate, except that--no-orchestrate --checks-onlyuses the same check runner): code discovers the checks (closer scopes shadow global ones of the same name;--check-filternarrows them) and, up to 4 at a time, runs onegoose run --no-profilesubprocess per check with the check's body and the WHOLE diff, asking for JSON findings on changed lines only. The model's reply is the verdict, parsed as in G8; a finding with no severity takes the check's frontmatter default, else 'medium'. Model:--override-model, else, when--provideris given,--model(or the provider default when it is absent, skipping the check's own model), else the check's ownmodel:, else--model/default; turns: the check's limit, else--turn-limit, else 25.- Held by
crates/goose-cli/src/commands/review/orchestrator.rs:86-154(run_checks_in_parallel,MAX_WORKERS = 4atcrates/goose-cli/src/commands/review/orchestrator.rs:46) ->crates/goose-cli/src/commands/review/orchestrator.rs:192-222(run_single_check_subprocess); discoverycrates/goose/src/checks/mod.rs:318-392(discover); model precedencecrates/goose-cli/src/commands/review/orchestrator.rs:169-180.
G10 If a review pass or check crashed, ran out of turns or returned garbage, does the review say so or report clean? RULING · code
- Answered how
Code answers 'report clean': a check or per-file pass whose subprocess failed, panicked, or returned unparseable output contributes an empty findings list and one stderr warning line; the review prints only the findings that pass the severity floor (
--severity, default medium) as JSONL and exits successfully, and unless--quietit also prints a stderr summary line that counts findings but not failures. A failed check therefore looks the same on stdout and in the exit status as a check that found nothing.- Held by
crates/goose-cli/src/commands/review/orchestrator.rs:133-150andcrates/goose-cli/src/commands/review/orchestrator.rs:384-389(failure -> empty findings);crates/goose-cli/src/commands/review/handler.rs:246-272(summary andOk(())); severity floorcrates/goose-cli/src/commands/review/handler.rs:84-91applied byemit_findings(crates/goose-cli/src/commands/review/orchestrator.rs:774-787).
G11 (legacy path) Same as G8+G9, answered by one full agent session over the whole diff. RULING · llm
- Answered how
Only with
goose review --no-orchestrateand without--checks-only(that combination falls back to the G9 runner), and refused outright if any discovered check declares atools:allowlist: code builds one in-process session with no profile and thedeveloperandsummonbuiltins, and hands it a single prompt carrying the base review prompt, the discovered check bodies and the whole diff. Whatever the model streams as text is the review; nothing parses it.- Held by
crates/goose-cli/src/commands/review/handler.rs:189-229(build_sessionwithdeveloper+summonbuiltins,session.headless(prompt)).
H. Delegation
H1 Should part of the task go to a subagent, and with what instructions, source (sub-recipe, recipe or agent file), extensions, model and turn budget? RULING · llm
- Answered how
The session model answers by choosing to call the
delegatetool (offered by thesummonextension, on by default, and only to non-subagent sessions) with the instructions, source and overrides it picks, or withresumeand the id of a failed or cancelled delegate to continue. For a new delegation, code first checks that instructions or a source is present, that parameters come only with a source, and that an explicit max_turns is at least 1; building the delegate then validates further, and extension names that match nothing are logged and dropped, not refused. A resume accepts onlyinstructions, and is refused for an id that is not a subagent of this session or whose subagent already produced its final output.- Held by
the session model's call of the
delegatetool,crates/goose/src/agents/platform_extensions/summon.rs:475-546(tool), handled atcrates/goose/src/agents/platform_extensions/summon.rs:849-884(handle_delegate).
H2 Which provider, model, extensions, working directory, approval mode and turn budget does the subagent run with? RULE · code
- Answered how
Code resolves each setting by a fixed precedence. Provider: delegate recipe's
goose_provider> envGOOSE_SUBAGENT_PROVIDER> the model'sprovider> config-fileGOOSE_SUBAGENT_PROVIDER> the parent's. Model (each candidate only if its paired provider is unset or matches): recipe > envGOOSE_SUBAGENT_MODEL> the model'smodel> config > parent > provider default. Turns: the model'smax_turns> delegate recipe > env, then configGOOSE_SUBAGENT_MAX_TURNS>GOOSE_MAX_TURNS> 1000. Extensions: the source recipe's, else the parent's, which the model may only narrow. The approval mode is copied from the parent session, and a requested working dir must resolve inside the parent's.- Held by
crates/goose/src/agents/platform_extensions/summon.rs:1238-1299(resolve_subagent_config), withcrates/goose/src/agents/platform_extensions/summon.rs:925-1013(create_subagent),crates/goose/src/agents/platform_extensions/summon.rs:1301-1393(model),crates/goose/src/agents/platform_extensions/summon.rs:1395-1429(provider),crates/goose/src/agents/platform_extensions/summon.rs:1431-1437(turns,resolve_max_turns); approval modecrates/goose/src/agents/platform_extensions/summon.rs:416-425; working dircrates/goose/src/agents/platform_extensions/summon.rs:1520-1543(resolve_working_dir).
H3 Can a subagent start subagents of its own? RULE · code
- Answered how
Always no: code does not list the
delegatetool to a session whose type is SubAgent, refuses adelegatecall from one with 'Delegated tasks cannot spawn further delegations', and the subagent runner returns no children for a SubAgent parent. Delegation depth is fixed at one.- Held by
crates/goose/src/agents/platform_extensions/summon.rs:874-876(refuses); tool hidden atcrates/goose/src/agents/platform_extensions/summon.rs:1448-1460; runner never starts children of a subagent session atcrates/goose/src/agents/state_machine/ops_subagent.rs:94-96.
H4 When a subagent stops, did it complete, fail or get cancelled? RULE · code
- Answered how
After the child's run ends, code reloads its session and checks in a fixed order: a successful
recipe__final_outputcall anywhere in it means Completed (even if the run was cancelled or errored afterwards); else a cancelled run is Cancelled; else a run error, then a trailing error message, then the last message's text ('max turns reached; last response: ...' when it was the max-turns notice, 'stopped without final output' when empty) make it Failed.- Held by
crates/goose/src/agents/subagent_handler.rs:140-176(ForegroundSubagentRunner::run; final-output testcrates/goose/src/agents/final_output_tool.rs:55-95;failure_reasoncrates/goose/src/agents/subagent_handler.rs:265-287).
H5 Should the parent trust and build on what the subagent returned? RULING · llm
- Answered how
The parent's session model decides, with no code check in between: each finished child's outcome arrives as one user-role message hidden from the person, 'Subagent <id> completed: <final-output JSON>' or '... failed: <reason>' (a cancelled child gets a 'Subagent <id> was cancelled before it finished.' line), and the model uses it however it chooses.
- Held by
the parent's session model, which receives the outcome as a hidden user message (
crates/goose-agent/src/subagent.rs:82-101), delivered bySubagentOperation::run(crates/goose-agent/src/subagent.rs:252-295).
H6 While a subagent runs, which of its messages are passed up to the parent session (and so to the person)? RULE · code
- Answered how
Code passes up only the child's messages that carry an action-required tool confirmation or elicitation, after re-addressing each request id through the parent session's action-required table; the parent emits them as its own messages. Every other child message (text, tool calls, results) stays in the child session; the person also gets the start and finish notices built separately (see H5).
- Held by
crates/goose/src/agents/subagent_handler.rs:125-136(filterasks_for_a_decisionatcrates/goose/src/agents/subagent_handler.rs:251-263; re-addressingforwardedatcrates/goose/src/agents/subagent_handler.rs:231-249).
H7 With several delegations pending, which start now and which wait? RULE · code
- Answered how
Code walks the conversation in order and keeps as pending each delegated (or resumed) subagent that has no delivered or cancelled note after its latest delegation, then starts each pending one not already running until the number running reaches the cap (
GOOSE_MAX_CONCURRENT_SUBAGENTS, default 5); the rest wait. It then waits for the next finished child and delivers that one result per pass, so later passes start the waiting ones.- Held by
crates/goose-agent/src/subagent.rs:252-295(SubagentOperation::run; pending list frompending_subagents,crates/goose-agent/src/subagent.rs:173-187); capGOOSE_MAX_CONCURRENT_SUBAGENTS, default 5 (crates/goose/src/agents/state_machine_builder.rs:89-95).
H8 Asked a question about a set of files, what does a one-shot model call answer? RULING · llm
- Answered how
Only when the
summarizeextension has been enabled (off by default) and the session model calls its tool: code resolves each named relative path and refuses one that escapes the working directory. A named file is read as given; a named directory is walked, honouring the working directory's top-level .gitignore and skipping symlink entries. Files over 100 KB and unreadable files are skipped, more than 1 MB in total is an error. Then the session's provider and model get one call with a fixed system line and the file contents plus question. Whatever text comes back is the answer, returned with an 'Analyzed N files (L lines)' footer.- Held by
crates/goose/src/agents/platform_extensions/summarize.rs:158-212(execute_summarize,provider.completeatcrates/goose/src/agents/platform_extensions/summarize.rs:183-188), session model; extension off by default atcrates/goose/src/agents/platform_extensions/mod.rs:141-152.
H9 Given the requested app, what name, description, HTML and window size does a separate model call produce? RULING · llm
- Answered how
When the session model asks the apps extension for a new app: the session's provider and (registry-normalised) model get one call with the
apps_create.mdsystem prompt (a user copy wins), a user prompt carrying the request and the names of existing apps, andcreate_app_contentas the only tool. The arguments of that tool call, coerced toward its schema and deserialised, must give the name, description and HTML and may give width, height and resizable, each on its own; after the call, code fills any it left out with 800, 600 and resizable.- Held by
crates/goose/src/agents/platform_extensions/apps.rs:305-352(generate_new_app_content); window defaults applied atcrates/goose/src/agents/platform_extensions/apps.rs:473-477(values:61-65); apps extensiondefault_enabled: trueatcrates/goose/src/agents/platform_extensions/mod.rs:66-79, compiled in only with theplatform-appscargo feature.
H10 Given the current app and feedback, what updated HTML and description does a separate model call produce? RULING · llm
- Answered how
When the session model asks the apps extension to change an app with some feedback text: the session's provider and model get one call with the
apps_iterate.mdsystem prompt, a user prompt carrying the original PRD, the current HTML and the feedback, andupdate_app_contentas the only tool. That tool call's arguments are the new description, HTML, PRD and optional window size; a reply whose reported output tokens reach the configured max_tokens (when both are known), or that made no such call, is an error.- Held by
crates/goose/src/agents/platform_extensions/apps.rs:354-401(generate_updated_app_content).
I. Learning and feedback
I1 Should something the person said be remembered for later sessions, and for this project or everywhere? RULING · llm
- Answered how
Only when the memory builtin extension is enabled: the session model decides, by calling
remember_memorywith a category, data, tags andis_globalof its choosing, guided by instructions that say both 'Save proactively' and 'Always confirm with the user before saving'. Code appends the entry to<category>.txtunder the working directory's.goose/memory/(local) or the config dir'smemory/(global); no code asks the person, beyond whatever the normal tool-approval seat does.- Held by
the session model's call of the memory extension's tools (instructions
crates/goose-mcp/src/memory/mod.rs:116-131; toolremember_memoryatcrates/goose-mcp/src/memory/mod.rs:391-424->remembercrates/goose-mcp/src/memory/mod.rs:260-285; local vs global dircrates/goose-mcp/src/memory/mod.rs:133-135,205-213). A builtin MCP extension named atcrates/goose-mcp/src/mcp_server_runner.rs:21, not a platform extension.
How this map was made
Four readers each took a part of the agent and wrote seat cards from the source at an earlier commit. The map's author re-read every seat and corrected what the source did not support. For this edition every shown card was carried to the commit named above: its citations whose lines moved were renumbered, and every shown card citing code that changed was re-read at that commit. Reviews by a model from another family (read-only, at the commit named above) checked the answers against the code, and each of their findings was checked against the code before anything changed. Nothing on this page was produced by running their code.