Skip to content

fix: ask before confirming, even in apps that cut the guide (#333) - #334

Merged
ifahimreza merged 3 commits into
mainfrom
fix/223-ask-before-confirm
Oct 5, 2026
Merged

ifahimreza merged 3 commits into
mainfrom
fix/223-ask-before-confirm

Conversation

@ifahimreza

@ifahimreza ifahimreza commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Closes #333
Refs #223

What

An AI app now shows the user a gated step's preview and waits for their yes before it confirms, including Claude Code, which reads only the start of Saddle's guide.

Why

Found in the #223 real-app pass. On the 1.5.1 zip, Claude Code 2.1.289 was asked to "Move the oldest draft to the trash". It got the preview and token and confirmed in the same turn without asking. The gate worked (two calls, single-use token, Activity with Undo), but the promise on the consent screen ("Deletions and overwrites will still show you a preview and ask for confirmation every time") was not kept.

Claude Code keeps the first 2,048 characters of a server's instructions (measured: its copy of Saddle's 6,912-character guide ends at character 2,048 with "… [truncated]"). The ask-first rule was at character 6,532. The preview's own text said only "To proceed, call this tool again… with confirm_token". The owner's instructions come last, so Claude Code never saw those either.

How

  • Saddle_Approval::gate(): the preview's instructions say to show the summary, ask, and call with the token only after the user agrees. Every app reads this, whatever its instructions limit.
  • Saddle_Context::system_context(): the write-tier line in "What you are allowed to do" carries the same rule, inside the first 2,048 characters.
  • Saddle_MCP::server_instructions(): the handshake's first line tells an app that cut the guide short to call saddle/get-instructions for the rest, including the owner's instructions. It is not in system_context(), because get-instructions serves that.
  • The withheld-tools line names the level as the owner sees it on AI apps ("Read only", "Edit content"), not the tier key ("read", "write"). Both test runs had Claude Code tell the user to set the app "back to write".
  • Owner instructions stay last, as the guide's order says. No tool, schema or name changes, so ChatGPT's frozen tool lists are unaffected.

Changelog line written under 1.5.2; the number is yours.

Testing

  • PHPUnit: Saddle_MCP_Transport_Test and Saddle_Approval_Test, 64/64, including two new tests: the first 2,048 characters of the handshake carry the ask-first rule and the pointer (with owner instructions set), and the preview tells the agent to ask. Full suite: see below.
  • phpcs clean on the three changed files.
  • saddle.pot regenerated: exactly the three changed strings.
  • Verified with the real app. Fresh Playground, WordPress 7.1.2, PHP 8.3, the 1.5.1 zip from WordPress.org with this branch's three files on top, Claude Code 2.1.289 by key at Edit content:
    • The first turn now calls saddle-get-instructions by itself.
    • "Move the oldest draft to the trash": it quotes the preview ("Nothing has changed yet") and asks "Should I go ahead?". The post is unchanged.
    • "Yes, go ahead": the post is in the trash.
    • Before the fix, on the same site: confirmed in the same turn, by key and by sign-in.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

ifahimreza and others added 3 commits October 5, 2026 18:47
Claude Code keeps only the first 2,048 characters of a server's
instructions. Saddle's are 6,912 on a fresh site, and the rule to show
the user a preview before confirming sat at character 6,532, so Claude
Code confirmed a gated delete in the same turn.

- The preview's own instructions say to show the summary and ask.
- "What you are allowed to do" carries the same rule.
- The handshake's first line says where the rest of the guide is,
  including the owner's instructions.
- The withheld-tools line names the level as the owner sees it.

Refs #333, #223

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #333

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #333

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ifahimreza
ifahimreza merged commit a0f8676 into main Oct 5, 2026
9 checks passed
@ifahimreza
ifahimreza deleted the fix/223-ask-before-confirm branch October 5, 2026 16:30
ifahimreza added a commit that referenced this pull request Oct 5, 2026
Refs #223

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ifahimreza added a commit that referenced this pull request Oct 5, 2026
* docs(clients): real-app results for Claude Code, 2026-10-05

Key and sign-in on the 1.5.1 zip: connect, read and the Read only
refusal pass; the gated write confirmed without asking (#333), fixed
by #334 and checked with the real app. Also records Claude Code's
2,048-character instructions limit and how to run the pass headless.

Refs #223

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(clients): #334 is on main

Refs #223

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude Code sees only the first 2,048 characters of Saddle's instructions, so it confirms gated steps without asking

1 participant