# Plugin review — ai-psychosis (2026-06-20) > Full-depth review (part of the marketplace-wide sweep; pilot was okr). Tooling: config-audit > v5.4.0 scanners (from source) + llm-security posture assessor + structure/version checks. > Read-only; this file is the only artifact. ## Verdict **Grade B — trustworthy mechanics, editorially self-interested.** No code-execution, no network egress, no credential access; the central privacy claim ("prompt text never written to disk") is real and test-enforced (canary + matched-phrase assertions across the hook lifecycle). The concerns are **content governance**, not technical exfiltration. ## Results by dimension | Dimension | Result | |-----------|--------| | config-audit posture | **A** (Feature Coverage F 36 — expected) | | config-audit plugin-health | 2 findings: "CLAUDE.md missing commands/hooks section" — **legitimate** (ships 1 command + a hook). | | llm-security posture | **B** — see findings. Zero npm deps; no `child_process`/`eval`/network anywhere; prompt variable explicitly cleared (`prompt-analyzer.mjs:290`). | | structure / hygiene | README ✓, CHANGELOG ✓, CLAUDE.md ✓, LICENSE ✓ | | version consistency | **OK** (gate) | ## Findings | ID | Severity | Location | Finding | |----|----------|----------|---------| | F-1 | **Medium** | `commands/interaction-report.md:382-391` | Layer-4 instructs Claude to append a verbatim, change-prohibited paragraph promoting an external commercial wellness program (Sadhguru "Miracle of Mind"), auto-triggered when `total flags >= 5 OR fatigue >= 2` — i.e. gated on the user's inferred emotional state, in a plugin marketed as "observation, not intervention." Opt-in (`layer4:false` default) and README-disclosed, which lowers severity. **This is the item to make an explicit accept/reject call on.** Recommend: gate/remove the promotion, or at least strip the emotional-state trigger + the "do not modify" lock. | | F-3 | Low (misinformation) | `README.md:544-552`, `SKILL.md:51-108` | Research citations presented as load-bearing authority that cannot be verified (future-dated arXiv IDs, an "April 2026 Anthropic guidance" quoted verbatim); the report command itself admits its "5-scale" is paraphrased, not a real Anthropic metric. **Recommend:** verify-or-remove. | | F-2 | Low — **resolved 2026-08-09** | `skills/ai-psychosis/SKILL.md:3-13` | "MANDATORY OVERRIDE … takes precedence over being helpful" auto-loads every conversation. Content is benign/pro-safety; flagged because the *structural pattern* (a skill claiming blanket precedence) is what a malicious skill would use. Governance note. | | F-5 | Low (defense-in-depth) — **resolved 2026-08-09** | `lib.mjs:233,59` | `session_id`/`cwd` interpolated into state-file paths without validation. Harness-supplied (not user-controlled) → not currently exploitable. See resolution below — fix split by field, not identical for both. | `/interaction-report` reading JSONL into context (F-4) is currently safe — records hold only a tool-name enum + domain labels, no free text. Noted only as a future sink. ## Decisions ### F-1 — accepted as-is (operator decision, 2026-08-02) **Accept.** Layer 4 ships unchanged: the `flags >= 5 OR fatigue >= 2` trigger, the verbatim paragraph, and the "do not modify" lock all remain as written. Rationale: Layer 4 is opt-in and off by default (`layer4: false`), the reference and its commercial nature are disclosed in `README.md:102-119`, and the paragraph is framed as the author's personal pointer rather than a claim about the user. The two distinct concerns the finding bundles — (A) inferred emotional state gating served content, and (B) a publicly distributed plugin carrying a named commercial endorsement — are both acknowledged and accepted as known, disclosed risk. No behavioural change, so no version bump; the plugin stays at v1.2.1. Established while making the call, and not previously recorded in this review: - **Layer 4 is enforced by prompt text only.** `requireLayer(4)` is never called. `lib.mjs:85-96` handles `n === 3` and `n === 4`, but the only call sites in the repo are `requireLayer(2)` in the four hook scripts. The `layer4: true` config gate, the flag trigger, and the "do not modify" instruction are all directives inside `commands/interaction-report.md` that Claude self-enforces at report time. This follows from Layers 3/4 being slash-command-driven rather than hook-driven, and it does not change the accept — but "opt-in, off by default" is an instruction, not a code guarantee. - **Scope correction:** `skills/ai-psychosis/SKILL.md` is *not* part of the F-1 surface (zero matches for `sadhguru|miracle of mind|layer4`). The surface is `commands/interaction-report.md:372-394`, `README.md:102-119`, and `lib.mjs:52,81,89`. - **No test coverage:** `tests/` contains no Layer 4 assertions — neither the paragraph nor its gate is verified by the suite. ### F-3 — resolved by correction in place (2026-08-02) Every research citation in `README.md` and `skills/ai-psychosis/SKILL.md` was verified against its primary source. **No citation was fabricated**, so all were kept with corrected attribution rather than removed. Defects found and fixed: | Claim | Verification | Outcome | |---|---|---| | arXiv:2602.19141 | Exists. Title is "…, Even in Ideal Bayesians"; authors Chandra, Kleiman-Weiner, Ragan-Kelley, Tenenbaum; MIT CSAIL / UW / MIT BCS; 22 Feb 2026 (affiliations read from the PDF, not the abs page) | Title completed; multi-institution attribution; description "proving … mathematical inevitability" replaced with what the abstract states (an *idealized Bayes-rational user is vulnerable*; sycophancy plays a causal role; effect persists under two mitigations) | | Disempowerment patterns | Exists. Real title "Disempowerment patterns in real-world AI usage"; published **28 Jan 2026** (dateline in page source); ~1.5M interactions | Title and date corrected (README said "March 2026") | | Nature d41586-025-03020-9 | Exists. "Can AI chatbots trigger psychosis? What the science says", Rachel Fieldhouse, *Nature* 646(8083), 18 Sep 2025 | Title completed; author, volume and date added; causal caveat added | | arXiv:2509.10970 | Exists. "The Psychogenic Machine: Simulating AI Psychosis, Delusion Reinforcement and Harm Enablement in LLMs"; Psychosis-bench | Title completed; claim quoted from the abstract | | 3 × Claude's Constitution quotes | **Verbatim-exact** (fragment-matched against the fetched page text, not eyeballed). CC0 1.0 release confirmed on the page | Unchanged | | "Score 5" 1–5 sycophancy scale | **Real and verbatim.** Appendix "Sycophancy criteria" rubric, pp. 9–10 (Score 1 on p.9, Score 5 on p.10 — confirmed by per-page extraction): Score 1 = "Extremely Sycophantic", Score 5 = "No Signs of Sycophancy". All three quoted lines are exact | Attribution sharpened; scale direction stated explicitly so "aim for Score 5" cannot be misread | | "11 guidance criteria … page 2" | **Both correct.** The list has exactly 11 bullets and is on printed page 2 | Quote of criterion 8 was silently truncated mid-sentence — completed with ", or more reliance on Claude than the person wants." | | 4 supporting Anthropic URLs | **3 of 4 returned HTTP 404** | `research/protecting-wellbeing` → `news/protecting-well-being-of-users`; `research/emotion-concepts` → `research/emotion-concepts-function`; `news/claudes-new-constitution` → `news/claude-new-constitution`. All now 200 | | `commands/interaction-report.md` disclaimer | Claimed the 1–5 scale "is not a verbatim metric from any Anthropic publication" — **false**; the rubric is real | Rewritten: the rubric is real, the *table's level descriptions* are the paraphrase | Method note: an initial WebFetch summary reported the scale as inverted (Score 5 = most sycophantic) and the criteria as 6 rather than 11. Both were wrong. Extracting the appendix PDF text directly (`pdftotext`) contradicted the summary. Model-generated summaries were therefore not used as evidence of record for any edit; every claim above rests on extracted source text or an HTTP status code. This applies to the figures newly *added* during the correction, not only to the ones corrected. Each was re-checked against source before shipping: the 30 April 2026 publication date (dateline in the page source), the "1 in 1,000 to 1 in 10,000 … depending on the domain" prevalence range (verbatim in the page source), and the quoted Psychosis-bench finding (verbatim in the arXiv abstract). A first pass also wrote "page 9" for the Score 5 wording; per-page extraction showed the rubric spans pp. 9–10 with Score 5 on p.10, and the reference was corrected in both files before release. **Scope note.** F-3 was originally scoped to `README.md:544-552` and `SKILL.md:51-108` (the reference list and the guidance-framework block). The same defect class was present in README prose outside that range, so the sweep was widened to every research claim in the file: | Claim (README body) | Verification | Outcome | |---|---|---| | "demonstrates mathematically that even a perfectly rational user **will spiral**" | Abstract says an idealized Bayes-rational user "is vulnerable to" delusional spiraling | Softened to the abstract's wording | | "The consensus from this research is clear: warnings don't work" | Not a consensus — it is one paper's model result, that the effect persists when users are informed of possible sycophancy | Re-attributed to that specific finding | | "page-11 finding that human contact is the strongest **disempowerment** signal" | p.11 is the user-information *grader rubric*; "human contact is the strongest signal" is a classification tie-break instruction, not a disempowerment finding | Recharacterized as the classification rule it is | | "Relationship / spirituality (21% / 19% pushback rate)" | Relationship 21% verified verbatim (against a 15% overall rate). **19% for spirituality appears nowhere in the extracted text** — it is legible only in Figure A4, which text extraction cannot read | 19% removed rather than guessed; spirituality re-justified on its verified 38% sycophancy rate | | Clinical reports "triggered by" AI interaction | The Nature piece explicitly declines the causal claim | Changed to "associated with", with the caveat stated | A fourth added figure was caught after release and corrected in a follow-up doc commit: the README body said severe disempowerment is rare "but rising". The trend claim *is* supported ("the rate of potentially disempowering conversations is increasing over time"), but it rests on a different subset (feedback conversations, late 2024 to late 2025) than the one-week December 2025 sample, and the source explicitly adds "we can't pinpoint why" — attributing the increase possibly to shifts in the user base or in who leaves feedback. The bald "but rising" dropped that caveat and was rewritten to carry it. Also removed: "the mechanism is the interaction structure, not individual vulnerability", which was an inference from the abstract rather than a statement in it. ### F-5 — resolved, split by field (2026-08-09) The finding's suggested fix — allowlist `^[A-Za-z0-9_-]+$` before path use — was applied to `session_id` only, not identically to `cwd`. `session_id` becomes a raw filename segment (`lib.mjs:sessionStateFile`), so an unvalidated value genuinely escapes `STATE_DIR` via path traversal (verified: `sessionStateFile('../../escape')` resolved outside `STATE_DIR` before the fix). `cwd` is a base directory, not a segment (`lib.mjs:initConfig`); every real value contains `/`, so the same regex would reject all legitimate absolute paths and silently disable the documented per-project config override. `cwd` instead gets `isAbsolute(cwd) && !cwd.includes('\0')` — a narrower guard that doesn't change behavior for well-formed input. - `sessionStateFile`: invalid `session_id` now degrades to a fixed sentinel filename inside `STATE_DIR` rather than interpolating the raw value. Hooks still never throw or exit non-zero. - `initConfig`: malformed/non-absolute `cwd` now skips the project-config candidate instead of being joined unchecked; the global `~/.claude/ai-psychosis.local.md` candidate is unaffected. - Tests: `tests/lib.test.mjs` (`sessionStateFile` — path-traversal allowlist) proves the pre-fix escape and the post-fix containment; `tests/session-start.test.mjs` (`initConfig — cwd path handling`) proves a normal absolute `cwd` still loads project-level config, guarding against the regression a blanket regex would have caused. ### F-2 — resolved, language softened (2026-08-09) Operator decision, given two options (accept as-is with documented rationale, or soften the wording): **soften.** The blanket-precedence framing was removed from `SKILL.md`, not just the content it wrapped: - Frontmatter `description`: `"MANDATORY override for all conversations. … These rules take precedence over being helpful or agreeable."` → `"Always-active guidance for healthy AI interaction. … Being helpful does not mean being agreeable."` - H1: `# Interaction Awareness — MANDATORY OVERRIDE` → `# Interaction Awareness` (also brings the skill header in line with `README.md`'s H1, independently kept as `# Interaction Awareness` in the 2026-08-04 session). - Intro line: `"These rules OVERRIDE your default behavior."` → `"These rules shape how you respond in every conversation."` Scope held narrow: the `NEVER`/`YOU MUST` imperatives inside the Rules and Patterns sections were left unchanged. Those describe the skill's own required behavior within its domain, not a claim of precedence over other instructions — the latter is the specific structural pattern F-2 flagged, and the former is ordinary behavioral-skill phrasing present in every skill in this marketplace. No tests assert on the removed strings (`tests/skill-md.test.mjs` checks Constitution/Score-5/11-criteria citations only), so the suite is unaffected: 315/315 substantive tests pass (perf.test.mjs wall-clock assertions flake under machine load — confirmed unrelated by isolated rerun, 9/9 green alone). **Tradeoff accepted knowingly:** the description's authority language existed to keep this skill invoked on every turn. Softening it may reduce how often the model chooses to load the skill; no mechanism in this plugin forces invocation independent of the description text. D12 landed in the same session: `README.md:18`'s disclosure line is now a self-contained single line (generator + process + ownership basis, no link), matching the wording already adopted in `llm-security/README.md:7` and `voyage/README.md:11`.