Summary
When embedding graff --json in a product UI, prompt-only tool budgets are not enough. The harness should provide hard tool-call policy controls so applications can prevent duplicate searches/reads, cap runaway loops, and surface a deterministic reason when a tool call is rejected.
This came up while integrating CodeGraff into Lawplain's sandboxed /ask flow.
Related Lawplain issue:
Related broader CodeGraff JSON/embedded UI issue:
Why this is a harness issue
Different models behave very differently with the same system prompt:
| Model |
Behavior observed |
mimo-v2.5-pro |
relatively good, but still repeated one search and one judgment read |
glm-5.2 |
manageable, but repeated some searches/reads |
deepseek-v4-pro |
over-verified and repeated detail reads |
minimax-m3 |
repeatedly read the same judgment and leaked <think> reasoning |
kimi-k2.6 |
very slow and expensive, repeated searches/details |
The system prompt included strict instructions:
- hard limit of 6 tool calls
- fast path: one search + one detail fetch for broad doctrine questions
- never repeat the same URL/citation
- stop once a usable result is found
But those are soft constraints. The model can still emit repeated tool calls, and the harness currently executes them.
For a CLI, this is annoying. For an embedded web app, it becomes:
- duplicated UI chips
- high latency
- higher cost
- unpredictable completion time
- worse user trust
The model is responsible for proposing bad calls, but the harness is the only layer that can reliably enforce execution policy.
Concrete example
Question:
What must a plaintiff prove in a defamation claim?
Observed duplicate calls in Lawplain testing:
search: defamation ×2 or ×3
/v1/judgments/2019_SGHC_27 ×3
/v1/judgments/2020_SGHC_278 ×9 in one MiniMax run
Each repeat caused another model turn and grew context/cost.
Requested feature
Add harness-level tool call controls, ideally available in both CLI flags and SDK options.
Possible API:
runAgent({
model: "mimo-v2.5-pro",
maxToolCalls: 6,
dedupeToolCalls: true,
toolCallKey: "normalized", // or callback in SDK
onDuplicateToolCall: "reject", // "reject" | "warn" | "allow"
})
CLI equivalent:
graff --json --model mimo-v2.5-pro --max-tool-calls 6 --dedupe-tool-calls
Desired behavior
Max tool calls
When the limit is reached, the harness should not execute further tools. It should return a structured result to the model, e.g.:
{
"type": "tool_result",
"name": "bash",
"error": true,
"content": "Tool-call budget exhausted: 6/6. Write the best answer from gathered sources."
}
or emit an explicit policy event:
{
"type": "tool_rejected",
"reason": "max_tool_calls",
"limit": 6,
"tool_call_count": 6
}
Duplicate suppression
For repeated normalized tool inputs, the harness should either:
- reject the duplicate with a structured message, or
- return the cached previous tool result.
For example:
{
"type": "tool_rejected",
"reason": "duplicate_tool_call",
"duplicate_of": 3,
"message": "Duplicate tool call suppressed; use the previous result."
}
Normalization suggestions
For the built-in bash tool, exact command matching is too brittle. A useful first pass could normalize common URL/curl calls:
- strip shell quoting noise
- extract URL path + query
- sort query params
- normalize repeated whitespace
- ignore harmless output formatting differences like
| jq ...
Even a simpler initial implementation — exact JSON input hash — would still be useful.
Acceptance criteria
Why not solve this only in the app?
Lawplain can visually group duplicate chips, but it cannot reliably prevent the cost/latency because graff owns tool execution internally. Application-level UI grouping is a presentation workaround, not a control plane.
A harness-level tool policy would benefit any embedded/product use of CodeGraff, especially remote/sandboxed deployments.
Summary
When embedding
graff --jsonin a product UI, prompt-only tool budgets are not enough. The harness should provide hard tool-call policy controls so applications can prevent duplicate searches/reads, cap runaway loops, and surface a deterministic reason when a tool call is rejected.This came up while integrating CodeGraff into Lawplain's sandboxed
/askflow.Related Lawplain issue:
Related broader CodeGraff JSON/embedded UI issue:
Why this is a harness issue
Different models behave very differently with the same system prompt:
mimo-v2.5-proglm-5.2deepseek-v4-prominimax-m3<think>reasoningkimi-k2.6The system prompt included strict instructions:
But those are soft constraints. The model can still emit repeated tool calls, and the harness currently executes them.
For a CLI, this is annoying. For an embedded web app, it becomes:
The model is responsible for proposing bad calls, but the harness is the only layer that can reliably enforce execution policy.
Concrete example
Question:
Observed duplicate calls in Lawplain testing:
Each repeat caused another model turn and grew context/cost.
Requested feature
Add harness-level tool call controls, ideally available in both CLI flags and SDK options.
Possible API:
CLI equivalent:
Desired behavior
Max tool calls
When the limit is reached, the harness should not execute further tools. It should return a structured result to the model, e.g.:
{ "type": "tool_result", "name": "bash", "error": true, "content": "Tool-call budget exhausted: 6/6. Write the best answer from gathered sources." }or emit an explicit policy event:
{ "type": "tool_rejected", "reason": "max_tool_calls", "limit": 6, "tool_call_count": 6 }Duplicate suppression
For repeated normalized tool inputs, the harness should either:
For example:
{ "type": "tool_rejected", "reason": "duplicate_tool_call", "duplicate_of": 3, "message": "Duplicate tool call suppressed; use the previous result." }Normalization suggestions
For the built-in
bashtool, exact command matching is too brittle. A useful first pass could normalize common URL/curl calls:| jq ...Even a simpler initial implementation — exact JSON input hash — would still be useful.
Acceptance criteria
turnstill emits normally after budget exhaustion if the model answers.Why not solve this only in the app?
Lawplain can visually group duplicate chips, but it cannot reliably prevent the cost/latency because
graffowns tool execution internally. Application-level UI grouping is a presentation workaround, not a control plane.A harness-level tool policy would benefit any embedded/product use of CodeGraff, especially remote/sandboxed deployments.