[BUG] IS_DEMO still suppresses the workspace trust prompt in 2.1.263 — all hooks, statusLine and project permissions silently disabled, and /doctor has no trust check

Status Open
Reported on v2.1.150
Maintainer reply None cached
Activity 2 comments · opened Sep 8, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Why a new issue: this is the same defect as #37780, which was closed by the stale bot on 2026-06-26 (not_planned, after three stale labels — never triaged closed by a human) and whose closing message asks to open a new issue if it is still relevant. The two earlier reports of the same defect, #10409 and #34608, are locked. Happy to close this as a duplicate if #37780 is reopened instead — I have no preference about where it lives, only that it is tracked somewhere. Also related: #84728, #86857, #90220.

Setting IS_DEMO in the env block of ~/.claude/settings.json makes the workspace trust prompt never appear, so hasTrustDialogAccepted stays at its default false indefinitely. In that state Claude Code silently skips every hook, the statusLine, all project-scoped permissions.allow entries, and plugin installation — with no error, no warning, and nothing surfaced by /doctor.

This was reported in #37780 on 2.1.150 and again on 2.1.233. It still reproduces on 2.1.263, and identically on 2.1.261 and 2.1.260.

Worth being precise about the scope: what gets disabled is the user's own global configuration, not just the folder's. In a freshly git init-ed directory containing only a README.md and no .claude/ directory at all, every hook defined in ~/.claude/settings.json was still skipped (SessionStart, UserPromptSubmit ×2, Stop ×2, SessionEnd ×2), and no Dropped N project-scoped permissions line appeared because there were none to drop. And the trust prompt itself never mentions hooks or the statusLine. I captured it in both cases (screenshots attached below; the username in the path is redacted).

In a repo that has project settings, it adds a warning line about that folder's own configuration:

Quick safety check: Is this a project you created or one you trust? ...
Claude Code'll be able to read, edit, and execute files here.

⚠ This folder pre-approves 97 tool permissions in .claude/settings.json and .claude/settings.local.json:
  Edit, Write, NotebookEdit, WebSearch, WebFetch, mcp__blender__*, mcp__drawio__*, mcp__ide__*, and 83 more
These will apply without asking. Only proceed if you trust this configuration.

In the git init-ed folder with no .claude/ directory, that line is absent and the entire prompt is:

Quick safety check: Is this a project you created or one you trust? ...
Claude Code'll be able to read, edit, and execute files here.

Security guide
❯ No, exit
  Yes, I trust this folder

Every conditional line the prompt can add describes what that folder contributes (project permissions, added directories, a headersHelper). Neither wording says anything about the user's own ~/.claude/settings.json hooks or statusLine — which are exactly what stops running. A user who reads this prompt carefully still has no way to know.

Note also that the default focus is on No, exit, so Enter and Esc both decline — landing the user in the same silently-degraded state as never being asked, except that No, exit at least quits rather than continuing.

I set IS_DEMO to keep my account email off the screen while doing educational live-coding sessions — which is the documented, recommended use of the variable. The environment variables reference describes it as:

Set to any non-empty value, such as 1, to enable demo mode: hides your email and organization name from the header and /status output, and skips onboarding. Setting it to 0 or false still enables demo mode, unlike most on/off variables; unset the variable to turn it off. Useful when streaming or recording a session

So this is not an internal flag being used out of scope, and it is not marked deprecated. The documented effects are hiding the email/organization name and skipping onboarding. Suppressing the workspace trust prompt — and with it every hook, the statusLine, and all project-scoped permissions — is not among them. A user following the documentation for its recommended purpose has no way to know their guardrails stopped running. (See also #84728: it is currently the only way to keep account info off the screen.)

Two things are worth adding to what #37780 already documented.

1. git init silently drops inherited trust, so this recurs for every new repository. Trust is resolved by walking ancestors, but the walk stops at the git root:

function AB(e,t){ let r=zn(lt(t)); let o=k0(r);          // o = git root
                  let d = o!==null ? y1(lt(o)) : null;
                  return yB(e, r, d); }
function yB(e,t,r){ let o=y1(t);
  while(!0){
    if (!(r===null || o===r || o.startsWith(r.endsWith("/")?r:r+"/"))) return !1;
    if (e.projects?.[o]?.hasTrustDialogAccepted) return !0;
    if (o===r) return !1;                                 // stops at the git root
    let p=y1(lt(o,"..")); if (p===o) return !1; o=p;
  } }

A non-git directory under a trusted parent inherits trust and behaves normally. The moment it becomes a git repository it no longer inherits — and with IS_DEMO set, nobody is ever asked. So this does not present as a one-time setup mistake that a user makes and fixes; it silently recurs every time they start a new repo. In my case hooks had been dead for three days across a repo I use daily. (Adjacent to #90220 and #84402, from the opposite direction.)

2. /doctor has no workspace-trust check, so the built-in diagnostic reports a healthy setup. Run in a directory where every hook is disabled by this bug, /doctor produced:

## Summary
Setup is largely healthy. Install (2.1.263, native) is current, all config files parse,
and hooks are all fast (slowest 521ms).

### Check 5 — slow hooks
No findings. 347 hook executions recorded in the window, zero timeouts.

The /doctor instructions contain zero references to workspace trust (workspace trust: 0 occurrences, hasTrustDialogAccepted: 0; the three trust matches are all about untrusted transcript data). Check 5 measures hook speed, and its aggregation window is 50 sessions across 6 projects over 12 days — so it reported hook activity from other projects while the current workspace ran none. The instructions even warn in the safe direction ("zero recorded runs does not mean the hook rarely fires"), but nothing checks whether hooks are enabled here at all.

claude doctor (the CLI diagnostic) in the same directory prints No installation issues found.

Workspace trust being unaccepted is plausibly the highest-impact setup problem /doctor could surface, and it is the one thing it does not look at. I am filing this together with the trust bug rather than separately because it is the reason the trust bug stays invisible — a user who suspects something is wrong and runs the built-in health check is told everything is fine.

What Should Happen?

  1. IS_DEMO should not suppress the trust prompt. Either it should not gate the trust flow, or suppressing the prompt should leave the user something actionable. This is the same pattern as #10409 (--dangerously-skip-permissions skipping the trust check without granting trust), which was fixed.
  2. /doctor should check workspace trust and report it as a finding when the current workspace is untrusted, listing what is consequently disabled (hooks, statusLine, project-scoped permissions, plugin installs).
  3. Hook and statusLine skips should be visible at the default log level, not only under --debug. Today the only signal is the absence of whatever the hooks did.

Error Messages/Logs

With `CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose claude --debug-file /tmp/cc.log` in an untrusted workspace:


[DEBUG] Dropped 97 project-scoped permissions.allow entries — workspace not yet trusted
[DEBUG] Skipping SessionStart:startup hook execution - workspace trust not accepted
[WARN]  Status line command skipped: workspace trust not accepted
[DEBUG] Trust not accepted for current directory - skipping plugin installations
[DEBUG] Skipping StatusLine command execution - workspace trust not accepted
[DEBUG] Skipping UserPromptSubmit hook execution - workspace trust not accepted
[DEBUG] Skipping Stop hook execution - workspace trust not accepted
[DEBUG] Skipping SubagentStop hook execution - workspace trust not accepted
[DEBUG] Skipping SessionEnd:prompt_input_exit hook execution - workspace trust not accepted


Relevant implementation in 2.1.263 — `IS_DEMO` short-circuits to `"skip"`, so `"ask"` (the dialog) is unreachable:


function S(){
  if (Ie(!1) || Boolean(a.IS_DEMO) || a.CLAUBBIT) return "skip";
  return Bo() && !Nae() ? "trusted" : "ask";
}
async function T(r,i,o){ switch(i){
  case "skip":    if(!a.CLAUBBIT && Bo()){ Dx(!0); ... } ... return;   // grants only if already trusted
  case "trusted": { Dx(!0); ... return }
  case "ask":     { ... TrustDialog ... Dx(!0); ... return }
}}

Steps to Reproduce

<ol>
<li>Put <code>IS_DEMO</code> in <code>env</code> in <code>~/.claude/settings.json</code>, plus any hook with an observable side effect:
<pre><code class="language-json">{
"env": { "IS_DEMO": "true" },
"hooks": {
"Stop": [ { "hooks": [ { "type": "command", "command": "echo hi >> /tmp/hook-canary" } ] } ]
}
}
</code></pre>
</li>
<li><code>mkdir ~/repro &#x26;&#x26; cd ~/repro &#x26;&#x26; git init</code> — a git repository, so trust is not inherited from a trusted parent</li>
<li><code>CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose claude --debug-file /tmp/cc.log</code></li>
<li><strong>No trust prompt appears.</strong> Send two short messages, then exit.</li>
<li>Observe:
<ul>
<li><code>/tmp/hook-canary</code> does not exist</li>
<li><code>grep -c 'workspace trust not accepted' /tmp/cc.log</code> → non-zero</li>
<li><code>grep -c 'Getting matching hook commands' /tmp/cc.log</code> → <code>0</code></li>
</ul>
</li>
<li>Run <code>/doctor</code> in the same directory — it reports a healthy setup and never mentions trust</li>
<li>Remove <code>IS_DEMO</code>, restart → the trust prompt appears → accept it → hooks work. Restore <code>IS_DEMO</code> → hooks keep working, because the acceptance persists in <code>~/.claude.json</code></li>
</ol>
<p><strong>Controlled A/B</strong> — two freshly <code>git init</code>-ed directories, <code>IS_DEMO</code> set in both, differing only in <code>hasTrustDialogAccepted</code>:</p>

  | hasTrustDialogAccepted | trust prompt | trust not accepted lines | hook dispatches
-- | -- | -- | -- | --
A | false (default) | not shown | 10 | 0
B | true (written directly to ~/.claude.json) | not shown | 0 | 6

<h3 data-heading="Impact">Impact</h3>
<p>Hook-based guardrails fail open. In my setup a <code>PreToolUse</code> hook blocks commits and pushes to <code>main</code>, and another warns on destructive shell commands. Both scripts pass their own unit tests (10/10 and 21/21) and were simply never invoked for three days. A guard that is silently not running is worse than no guard, because the user believes they are protected.</p>
<p>A <code>UserPromptSubmit</code> hook that warns at 80% context was dead for the same reason, and sessions grew to 965k tokens (96% of a 1M window) with no warning.</p>

Claude Model

Opus

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.263 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

  • Reproduced in two different terminals, so it is not terminal-specific. Neither is in the Terminal/Shell dropdown, hence "Other".
  • Installation method: native (~/.local/bin/claude → ~/.local/share/claude/versions/<version>).
  • The /doctor figures above come from its own output in the untrusted directory; the full report and both debug logs are available if useful.
  • Screenshots: the trust prompt in a repo that has project settings (the pre-approves 97 tool permissions line is present), and in the git init-ed folder with no .claude/ directory (that line is absent — the whole prompt is the generic file-access sentence). Neither mentions hooks or the statusLine. The username in the paths is redacted.

<img width="1171" height="425" alt="Image" src="https://github.com/user-attachments/assets/67394973-3144-4776-a388-e3c7bea4190a" />
<img width="800" height="340" alt="Image" src="https://github.com/user-attachments/assets/cfe53dbe-2163-4a31-b9f4-b36a40d6fda4" />

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗