Three New Knobs on Snowflake's Managed Coding Agent: Effort, System Prompt, Stage Mounts
💡 Tool Tip: JSON Formatter, SQL Formatter, cURL to Code
The October 28, 2026 release note from Snowflake is four paragraphs long and easy to skim past. It adds three public controls to the Cortex Agents Coding Agent, which is the managed service that gives your application a sandboxed coding loop — bash, file read and write, grep, glob, web search, SQL execution and skills — without you hosting the harness. The three controls are thinking_effort, instructions.system and stage_mounts. All three sound like preferences. Two of them are actually cost and reproducibility decisions, and one of them quietly does less than its name suggests.
Server racks in a data centre, representing a managed agent sandbox
1. What Each Control Does, Exactly
thinking_effort sets the orchestration model's reasoning effort and accepts minimal, low, medium, high, xhigh and max. Each model uses a subset of those values, and — this is the sentence to reread — a request above that subset is lowered to the highest level the model accepts. Omitting the field keeps the model default. instructions.system appends extra system instructions to the managed coding-agent prompt, and the docs are explicit that it does not replace the sandbox, tool or skill instructions. stage_mounts, set on the code_toolset_all entry in tool_resources, mounts a Snowflake stage or a versioned stage into the sandbox; versioned stages cover notebooks, Streamlit apps, workspaces and agents. workspace_mounts is unchanged and still mounts a persistent writable workspace at version live. All three apply to requests that include the code_toolset_all tool.
POST /api/v2/cortex/agent:run
Content-Type: application/json
Authorization: Bearer $SNOWFLAKE_TOKEN
{
"messages": [
{ "role": "user", "content": [{ "type": "text",
"text": "Model weekly churn from RAW.EVENTS and write the result to churn_weekly.csv" }] }
],
"models": { "orchestration": "<your-orchestration-model>" },
"instructions": {
"system": "Dialect is Snowflake SQL. Never SELECT *. Prefer CREATE OR REPLACE TEMP TABLE. State assumptions before writing code."
},
"tools": [
{ "tool_spec": { "type": "code_toolset_all", "name": "code_toolset_all" } }
],
"tool_resources": {
"code_toolset_all": {
"stage_mounts": [
{ "stage": "@DB.SCHEMA.SKILL_STAGE", "version": "v3", "mount": "/mnt/skills" }
]
}
},
"thinking_effort": "medium"
}
// thinking_effort accepts minimal, low, medium, high, xhigh, max.
// Each model supports a subset; a value above that subset is silently
// lowered to the highest level the model accepts. Omit it for the default.2. thinking_effort Is a Ceiling, Not a Settings Slider
Because a too-high value is silently lowered, you cannot use thinking_effort to prove which level actually ran. If you ask for xhigh on a model whose ceiling is high, you get high and no error. This matters in two places: your cost model, where you assume the request bought more reasoning than it did, and your evaluation, where a level that appears not to help may simply never have been exercised. The fix is embarrassingly simple and almost nobody does it: sweep the levels on your own task, log wall time and output size, and treat a flat curve as evidence that the level is above the model's subset rather than evidence that effort does not matter.
import json, os, time, urllib.request
URL = "https://<account>.snowflakecomputing.com/api/v2/cortex/agent:run"
BODY = {
"messages": [{"role": "user", "content": [{"type": "text", "text": TASK}]}],
"models": {"orchestration": MODEL},
"tools": [{"tool_spec": {"type": "code_toolset_all", "name": "code_toolset_all"}}],
}
def run(effort=None):
body = dict(BODY)
if effort:
body["thinking_effort"] = effort
req = urllib.request.Request(
URL, data=json.dumps(body).encode(),
headers={"Content-Type": "application/json",
"Authorization": "Bearer " + os.environ["SNOWFLAKE_TOKEN"]},
method="POST")
t0 = time.time()
raw = urllib.request.urlopen(req, timeout=900).read().decode()
return {"effort": effort, "seconds": round(time.time() - t0, 1),
"chars": len(raw), "body": json.loads(raw)}
# Sweep the levels you care about on the same task, three times each.
# A level that never changes latency or output length is not doing anything.
for level in [None, "low", "medium", "high"]:
runs = [run(level) for _ in range(3)]
print(level, [r["seconds"] for r in runs], [r["chars"] for r in runs])3. instructions.system Adds; It Does Not Override
This is the most reassuring part of the release and the most misunderstood. Your system text is appended, so it cannot strip the sandbox or the tool contract — a good safety property. The flip side is that it also cannot override tool behaviour, and it sits in the same prompt as whatever untrusted content your agent pulls into context: a file in a mounted stage, a fetched page, a query result. Treat it as a place for style, dialect and verification habits ('state assumptions before writing code'), never for security boundaries, which belong in the tool allowlist and the mount permissions. If your user-supplied prompt text can contain 'ignore previous instructions', strip it before it becomes part of the conversation.
# Versioned stage vs live workspace: pick per artefact, not per habit.
# stage_mounts -> read-only view of a versioned stage (notebook, Streamlit app,
# workspace, agent). Same bytes every run. Use for skills,
# reference notebooks, and anything an audit may replay.
# workspace_mounts -> persistent, writable, at version "live". Use for scratch
# output the agent is expected to keep between runs.
tool_resources = {
"code_toolset_all": {
"stage_mounts": [
{"stage": "@ANALYTICS.GOV.APPROVED_SKILLS", "version": "v7",
"mount": "/mnt/skills"}, # read-only, pinned
{"stage": "@ANALYTICS.GOV.REF_NOTEBOOKS", "version": "v12",
"mount": "/mnt/ref"}, # read-only, pinned
],
"workspace_mounts": [
{"stage": "@ANALYTICS.SCRATCH.WS", "mount": "/mnt/work"} # live, writable
],
}
}
# Rule of thumb: if losing the mount would change an answer next month,
# it belongs in stage_mounts with an explicit version.4. stage_mounts Is Where Reproducibility Lives
Two mount types now coexist, and they answer different questions. workspace_mounts gives you a persistent writable workspace at version live: useful for the intermediate CSV an agent is iterating on, useless as evidence, because next month live is something else. stage_mounts with an explicit version gives you the same bytes on every run. So the split is simple: anything whose disappearance would change an answer — skills, reference notebooks, approved query patterns — goes in stage_mounts with a pinned version. Anything the agent is allowed to scribble on goes in workspace_mounts. If you cannot say which of the two a given artefact is, that is usually a sign it should be versioned.
from pydantic import BaseModel, field_validator
class RunRequest(BaseModel):
user_text: str
system_instructions: str
@field_validator("user_text")
@classmethod
def strip_control_claims(cls, v: str) -> str:
banned = [
"ignore previous instructions",
"you are now",
"new system prompt",
"disregard the sandbox",
]
low = v.lower()
hits = [b for b in banned if b in low]
if hits:
raise ValueError("untrusted text tries to edit the managed prompt: %r" % hits[0])
return v
# instructions.system is appended to the managed coding-agent prompt; it does not
# replace the sandbox, tool or skill instructions. That is good news: you cannot
# accidentally strip the guardrails. It is bad news only if you were counting on
# a system instruction to override a tool's behaviour -- it will not.5. Measure the Knobs Before You Ship Them
The temptation with a new control is to crank it. Do the opposite: run the same task three times per level, record wall time, output length and your own pass rate, and publish the median and p90 next to the agent's other service-level indicators. Two patterns are worth acting on. If raising effort changes nothing measurable, the level is above the model's supported subset and you are paying for a label. If it doubles p90 and the pass rate does not move, you are paying for latency with no evidence of quality. Only then decide a default — and write down the criteria you used, because the next model release will move the subset and your old assumption will not announce itself.
import statistics
def compare(rows, key="seconds"):
out = []
for level in sorted({r["effort"] or "default" for r in rows}):
vals = [r[key] for r in rows if (r["effort"] or "default") == level]
out.append({"effort": level, "n": len(vals),
"median": round(statistics.median(vals), 1),
"p90": round(sorted(vals)[int(len(vals) * 0.9) - 1], 1) if len(vals) > 1 else vals[0]})
return out
# Publish this table next to the agent's other SLIs, not in a wiki page.
# Two failure patterns to look for:
# 1. raising effort changes nothing -> the level is above the model's subset
# 2. raising effort doubles p90 and does not improve the pass rate -> stop paying
CRITERIA = {
"accept_new_default": lambda base, cand: (
cand["p90"] <= base["p90"] * 1.5 and cand["pass_rate"] >= base["pass_rate"]
),
}6. A Rollout That Survives the Next Model Release
Week one: pin every mount that feeds the agent, convert unversioned references to versioned stages, and record which artefacts cannot be versioned. Week two: build the sweep harness and get real numbers for two or three levels on your top task. Week three: move the numbers into a dashboard, set the default by the criteria you wrote down, and put the request body in version control so a reviewer can see the effort and the mount versions together. Week four: re-run the sweep when the orchestration model changes. None of this is glamorous, and it is the difference between an agent you can audit and an agent whose behaviour changed while nobody noticed.
A dashboard of latency and cost metrics
Abstract network of connected nodes
📌 Frequently Asked Questions
What are the three new controls in the Cortex Agents Coding Agent?
thinking_effort, instructions.system, and stage_mounts. They apply to agent:run requests that include the code_toolset_all tool.
Which values does thinking_effort accept?
minimal, low, medium, high, xhigh and max. Each model supports a subset, and a request above that subset is lowered to the highest level the model accepts. Omitting the field keeps the model default.
Does instructions.system replace the sandbox or tool instructions?
No. Snowflake's release note states it appends extra system instructions to the managed coding-agent prompt and does not replace the sandbox, tool or skill instructions.
What is the difference between stage_mounts and workspace_mounts?
stage_mounts mounts a stage or a versioned stage (notebooks, Streamlit apps, workspaces, agents) into the sandbox, giving you pinned, reproducible content. workspace_mounts is unchanged and mounts a persistent writable workspace at version live.
Why would raising thinking_effort have no visible effect?
Because the requested level may be above the subset the orchestration model supports, in which case it is silently lowered to the highest accepted level. Measuring latency and output length per level is the only way to know what actually ran.