ai-psychosis/docs/review-2026-06-20.md
Kjell Tore Guttormsen 0a5cdbd379 fix(skill): soften blanket-precedence framing in SKILL.md (F-2)
Review finding F-2 (docs/review-2026-06-20.md): "MANDATORY OVERRIDE …
takes precedence over being helpful" is benign content, but the
structural pattern — a skill claiming blanket precedence over other
instructions — is what a malicious skill would also use. Operator
chose to soften rather than accept as-is.

Removed from frontmatter description, H1, and the intro line only:
"MANDATORY", "OVERRIDE your default behavior", "take precedence over
being helpful or agreeable". Left the NEVER/YOU MUST imperatives in
the Rules and Patterns sections untouched — those describe the
skill's own required behavior, not a precedence claim over the
harness, and every skill in this marketplace phrases its rules that
way.

Known tradeoff, accepted knowingly: the removed language existed to
keep this skill invoked every turn; softening it may reduce how often
the model chooses to load it. No test asserts on the removed strings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5ZeVZ5bSAKZFcjpbFRWpQ
2026-08-09 10:19:20 +02:00

184 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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" 15 sycophancy scale | **Real and verbatim.** Appendix "Sycophancy criteria" rubric, pp. 910 (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 15 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. 910 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`.