# unslop-hook Mechanical AI-tell enforcement for pi seats. Anti-drift gate for the writing standard in SYSTEM.md / ms-unslop: prose distribution alone decays over long sessions; this check cannot forget. - `lists.json`: committed machine source for every list the checker enforces: words, phrases, punct rules, regex patterns, density thresholds. Each entry carries provenance (`ms-unslop:` or `system-md`), the mention convention, and the documented divergence of the density gate from SYSTEM.md's outright em-dash ban. Edit lists here, not in code. - `unslop-check.js`: dependency-free checker (node CLI + module) driven by lists.json. Loads and schema-validates the lists on first use and hard-fails closed: empty, unparseable, or invalid lists throw. Detects banned vocabulary, chatbot/sycophancy phrases, filler phrases, em/en dashes, curly quotes, `not just X but Y`. Strips fenced and inline code first, so quoted code is never flagged. Exit 0 clean, 1 violations, 2 gate broken (lists unreadable, never a clean verdict). - `extension.ts`: pi extension. `message_end` checks finalized assistant text and notifies the operator (TUI/RPC). `before_agent_start` reads the most recent assistant reply from the session file and, if it carries tells, injects a correction notice the model sees on its next turn. `/unslop` reports session stats. Violation state lives in the session file, so the injection path survives restart, resume, fork, and reload (an in-memory pending flag was measured dead across print-mode turns, 2026-08-19). A broken lists.json fails closed: checks stop, `broken_lists` / `skipped_broken` events log the reason, operator notified once, seat keeps running. - `test-unslop-check.js`: unit tests with red and green controls. ## Use ```bash node test-unslop-check.js # suite node unslop-check.js # CLI check UNSLOP_LISTS= node unslop-check.js # alt lists location pi -e ~/.mosaic/tools/unslop-hook/extension.ts # ad-hoc load # deploy: copy dir to ~/.pi/agent/extensions/unslop-hook/ or seat .pi # equivalent, or list it in settings.json "extensions" ``` Env: `MOSAIC_UNSLOP_HOOK=0` disables. `MOSAIC_UNSLOP_LOG=` appends JSONL events (loaded / flagged / notice_injected / checked / broken_lists / skipped_broken) for headless evidence. `UNSLOP_LISTS=` overrides the lists.json location for both CLI and extension. ## Verified here (2026-08-19) - Unit suite 24/24 (8 behavioral, 11 loader/CLI, 5 review follow-up), red and green controls both exercised, including exit-2 on broken lists and on an unreadable input file (S3). - CLI: slop file exit 1, clean file exit 0, broken lists exit 2 with the fault named on stderr. - Extension, healthy path (print mode, zai/glm-5.3:low): startup probe loads lists.json, reply checked clean. - Extension, broken-lists path (print mode): `broken_lists` at startup, `skipped_broken` per turn, seat survives, reply still delivered. - Earlier live evidence (pre-C1, inline lists): forced-slop turn flagged; fresh-process follow-up injected the notice and the reply came back clean; full TUI trial (notify line, injection, /unslop stats) on session vision-unslop. - Log evidence in session scratchpad. ## Limits - `/unslop` command not tested headless (print mode has no command surface); it is a thin stats wrapper. - En dash flag fires on typographic ranges too (2–3); acceptable for fleet prose, revisit if it noisifies technical writing. - Notice injection is a nudger, not a blocker. Output already streamed to the user stays as-is; correction lands on the next turn. - A broken lists.json latches for the session: repairing the file mid-session does not revive checks until the seat restarts. Acceptable for an advisory gate (review S1). - The fail-closed operator notification requires a UI. Print-mode sessions log `skipped_broken` but notify nobody (review S2). - The word/phrase lists are the mechanical subset of ms-unslop only, keyed to pattern ids in lists.json. Style judgments (voice, rhythm, structure) stay in the skill, not the gate. ## Promotion path Stack issue (A4): checker shared as the single source for a matching Claude Code Stop-hook script; lists versioned beside SYSTEM.md contract text.