Approvals: the client's own dialog
Praxis changes things only through one flow: Action → Preview → Approval → Execute → Result. Reading, previews and diagnostics never need approval; protected actions do. Since 0.1.0-alpha.49.2 a protected action is approved only in the client's own approval dialog.
Read vs preview vs write
| Kind | Examples | Approval |
|---|---|---|
| READ | praxis_doctor, praxis_*_status, praxis_jira_epic | none |
| PREVIEW | praxis_ba_preview, praxis_architect_preview, praxis_workflow_preview, recovery preview | none, and must not write |
| LOCAL EXECUTION | praxis_quality_ensure, praxis_quality_review | starts a service or executes tests and persists evidence; no Jira apply |
| APPLY (protected) | praxis_project_init_apply, praxis_workflow_apply (human acts), praxis_ba_apply, praxis_architect_apply, praxis_developer_claim, praxis_developer_complete, praxis_quality_apply, the Git apply tools, praxis_review_apply (recording review decisions), praxis_approval_journal with amend=true | the client's own dialog, after confirmation=YES with the current previewFingerprint |
Saying "yes, do it" in chat is not an approval. confirmation=YES is the agent's request to apply what was previewed; the approval is the client's own dialog. If Jira or the project changed since the preview, generate a new preview; if an apply was partial, retry with the same fingerprint. Each role transition is a separate approval: completing Developer does not authorize Quality review.
Praxis changes things outside the agent's own notes only through the flow Action, Preview, Approval, Execute, Result. There is one way to approve, the client's own dialog, and no fallback: not the agent's word, not a page, not a setting.
The client's own dialog
When Claude calls a protected apply tool, Praxis asks in the MCP client's own approval dialog. The dialog shows the exact action.
- Approve runs exactly that action.
- Reject runs nothing (
APPROVAL_REJECTED). Closing the dialog without deciding runs nothing and is not a rejection (APPROVAL_DISMISSED). No answer in time givesAPPROVAL_TIMED_OUT. - The answer reaches Praxis as the client's response, not as a tool argument.
confirmation=YES, tool arguments, chat text, a file in the repository, a page in your browser and settings are not your approval. - If the preview changed since you were shown it, the call is refused before you are asked (
APPROVAL_FINGERPRINT_MISMATCH). - A client that cannot show the dialog gets
APPROVAL_CLIENT_UI_REQUIREDat once, with the preview andexecuted: false. Nothing ran, and nobody rejected it. Reads, previews, reviews, the doctor and agent-authored records keep working.
A dialog answer is a client response. Praxis knows that the client's dialog answered, not who or what answered it. It is recorded as such (assurance: client-ui, trust client-response, decidedBy: client-dialog, personVerified: false). It is never recorded as proof that a person approved.
An instant answer is the client's own. Any answer sooner than 1000 ms, accept or decline, is recorded as auto-answered, and nothing runs. Timing works in this direction only: it can show that an answer was automatic, never that a person answered.
A configured answer is refused. Claude Code can answer the dialog through an Elicitation hook. Praxis checks the client's configuration before it asks and again after the answer. It refuses protected actions when it finds:
- an
Elicitationhook in any settings scope, plugin, marketplace, or agent, skill or command definition; - a client started with settings, plugins or agents on its command line;
- a client whose environment points it at more configuration or loads code into it;
- a repository whose own Claude Code settings do more than plain preferences: any hook, any
enventry, a status line command, plugins or marketplaces, helper commands.
praxis_doctor names the file, flag or variable it found. If an ordinary setup of yours is refused, move hooks and env from the repository's settings into your user settings. The check is best effort: a configuration source Praxis does not know is not seen. Open a repository in Claude Code only after you have checked it, as its workspace trust prompt asks.
Claude Code CLI in an interactive terminal is where protected actions are approved. For 0.1.0-alpha.49.2 the interactive CLI 2.1.314 was driven with keys sent in a pseudo-terminal: Approve ran the action, and Reject and Esc were recorded as what they were. Earlier, on 2026-10-22 (alpha.48, CLI 2.1.304), a run with a person showed the real form and Approve after 402.5 seconds. These runs show that the CLI shows its dialog and passes the answer on. They do not show who answers it in your session.
Approval is a consent and workflow mechanism, not a security boundary against a program running as you. See Security model.
Claude Desktop: protected actions are not executed
Chat and Cowork show no approval dialog. The Code tab declares one but answers it by itself in milliseconds, without showing it (anthropics/claude-code#89858), and Desktop starts the Code tab with settings Praxis cannot check. So in all three:
- protected actions are not executed: commits, pushes, merge requests, Jira writes, writing
.project, confirming Stories and decisions, recording review decisions. Claude shows the exact preview, says it was not applied, and nobody is said to have rejected it (APPROVAL_CLIENT_UI_REQUIRED); - requirements, reviews, prototypes, plans, stories, architecture, implementation records, Quality checks, and Git and Jira previews work as before;
- apply protected actions from the Claude Code CLI in a terminal, in the same repository. The CLI shows the dialog.
A typical Desktop session:
- In Desktop, Claude does the work and previews the change (a commit, a Jira write,
.project). - In a review page, press Send for approval. In Desktop, Claude then says the decisions could not be recorded here.
- Open a terminal in the repository, start
claude, and ask it to apply the pending review or the previewed change. Approve it in the CLI's dialog.
Work items and previews are stored in the repository and in your Praxis state, so the CLI picks up where Desktop stopped. A review handed over in Desktop's page lives only as long as that Runtime process: if the CLI does not find it, render the review again in the CLI and send it from there.
Review pages: Send for approval
In an HTML review you mark, comment and edit, then press Send for approval. That records nothing by itself: it hands the decisions to Claude, which calls praxis_review_apply. The client's dialog then shows exactly those decisions and remarks, and only its approval records them. A page that is out of date says so at once.
Removed in 0.1.0-alpha.49.2
- The Trusted Client and host-permission modes.
PRAXIS_APPROVAL_MODEis ignored, and the doctor lists it. - The
planTextargument. It is still accepted, and ignored. - The Desktop extension's Approval mode setting. A value entered earlier is ignored.
- The approval page of the unreleased Desktop candidate.
Why: every place a setting can come from can be written by someone other than the person deciding. Claude Code applies the env block of a repository's .claude/settings.json to the MCP servers it starts, and an agent with a shell on your account writes your home folder and the Desktop extension settings like you do. In 0.1.0-alpha.48 to alpha.49.1, a repository's Claude Code settings could approve protected actions by themselves. Update every Praxis plugin (Update); the release notes carry the security advisory.
Earlier records keep their labels (trusted-client, host-prompt, review-page). To find decisions an affected version may have recorded without you, use praxis_workflow_inspect (view: approvals) and praxis_approval_journal.
Read praxis_doctor
Ask: "Run Praxis doctor". Look at approval:
mode:client-ui(the client's dialog can be asked) orno-dialog(protected actions are refused here).trust: how protected actions can be approved here, and that independently authenticated human approval is not available.client:name,version,protocolVersion,elicitation,dialog(true or false),flow, andreasonwhen there is no dialog.observedDialog: shown once Praxis has seen this client answer its own dialog.dialogAutomation: what Praxis found that could answer the dialog (the file, flag or variable).ignoredEnvironment: retired or test settings it found and ignored.note: what the mode means.hostGate: shown when there is no dialog; explains that a host prompt may hold the call before Praxis.decisionLog: your journal,~/.praxis/decisions/<repository id>.jsonl(PRAXIS_STATE_HOMEmoves it). It records time, action, digest, outcome, what answered and the channel.diagnosticsLog:~/.praxis/diagnostics/mcp.jsonl: per calltool_enter,tool_finished(with ms and code),input_required; no arguments. A call missing there never reached Praxis.nextStepsanddotenvRejected.
Results of protected calls carry approval[] (assurance: client-ui, personVerified: false) and decidedBy, which names what answered, never a person.
If something goes wrong
APPROVAL_CLIENT_UI_REQUIRED: Praxis could not ask here. There is no dialog (Desktop Chat, Cowork), the client answered its dialog by itself (the Desktop Code tab), or something configured could answer it (the doctor names it). Nothing ran, and nobody rejected it. Apply from the Claude Code CLI in a terminal.APPROVAL_REJECTED: Reject was chosen in the dialog. Nothing ran.APPROVAL_DISMISSED: the dialog was closed without deciding. Nothing ran; not a rejection.APPROVAL_WITHDRAWN: the agent passedconfirmation=NO. Nothing ran; not your decision.APPROVAL_TIMED_OUT: dialog shown, no answer. Nothing ran. Ask again.APPROVAL_EXPIRED: the answer is too old to use. Ask again.APPROVAL_FINGERPRINT_MISMATCH: stale preview. Preview again.APPROVAL_SCOPE_MISMATCH: the approval is for another action or for different content. Preview again.APPROVAL_TOO_LARGE: the action is too large to show in full, so Praxis did not ask. Nothing ran.APPROVAL_REQUIRED: a mutation outside an approved action.CONFIRMATION_REQUIRED: apply withoutconfirmation=YES.- A Claude Desktop Chat apply that seems to hang: look for Desktop's own permission prompt; check
diagnosticsLog. Allowing the call there lets it reach Praxis; it does not approve the action.