anthropics / anthropics/claude-code

[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

Đang mở
#92,911 1 bình luận 3 reaction 0 người được giao Xem trên GitHub
area:hooks area:statusline bug has repro platform:macos
Ngôn ngữ chính
Python
Star
145k
Fork
23.1k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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](https://code.claude.com/docs/en/env-vars) 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:

```js
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

```shell
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


  1. Put IS_DEMO in env in ~/.claude/settings.json, plus any hook with an observable side effect:
    {
    
    "env": { "IS_DEMO": "true" },
    "hooks": {
    "Stop": [ { "hooks": [ { "type": "command", "command": "echo hi >> /tmp/hook-canary" } ] } ]
    }
    }


  2. mkdir ~/repro && cd ~/repro && git init — a git repository, so trust is not inherited from a trusted parent

  3. CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose claude --debug-file /tmp/cc.log

  4. No trust prompt appears. Send two short messages, then exit.

  5. Observe:

    • /tmp/hook-canary does not exist

    • grep -c 'workspace trust not accepted' /tmp/cc.log → non-zero

    • grep -c 'Getting matching hook commands' /tmp/cc.log0



  6. Run /doctor in the same directory — it reports a healthy setup and never mentions trust

  7. Remove IS_DEMO, restart → the trust prompt appears → accept it → hooks work. Restore IS_DEMO → hooks keep working, because the acceptance persists in ~/.claude.json


Controlled A/B — two freshly git init-ed directories, IS_DEMO set in both, differing only in hasTrustDialogAccepted:

  | 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

Impact


Hook-based guardrails fail open. In my setup a PreToolUse hook blocks commits and pushes to main, 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.


A UserPromptSubmit 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.

### 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/`).
- 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.

Image
Image

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

Start with the reproduction using ~/.claude/settings.json, a fresh git init repository, and CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose claude --debug-file /tmp/cc.log; compare the false/true hasTrustDialogAccepted cases in ~/.claude.json and inspect the trust decision and dispatch paths shown for S and T. Use /doctor and the debug log as checks; done means IS_DEMO no longer suppresses an actionable trust flow, untrusted-workspace consequences are reported, and skipped hooks/statusLine are visible.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
git, javascript, shell
Lĩnh vực
cli, developer-experience, security
Loại issue
Lỗi
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.