- scripts/agent.sh <name>: launches interactive pi TUI in the container with contracts + optional mission + agent identity + named session + optional workspace/tools; the Mosaic alternative to vanilla pi - pi adapter: MOSAIC_INTERACTIVE branch (clean TUI, no -p, no initial prompt); headless exec rebuilt via positional args (no word-splitting on the request); MOSAIC_AGENT_NAME optional in headless - loader: AGENT IDENTITY section when the launcher names the agent - compose: fixed command removed (request defaults live in run-agent.sh); MOSAIC_INTERACTIVE/MOSAIC_AGENT_NAME passthrough - docs/TOOLS.md: full on-demand tool reference; AGENTS.md routes to it - RELEASE -> 0.0.8 (container change); build verified Closes #35
84 lines
4.3 KiB
Markdown
84 lines
4.3 KiB
Markdown
# 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:<pattern id>` 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 <file> # CLI check
|
||
UNSLOP_LISTS=<path> node unslop-check.js <file> # 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=<path>` appends JSONL
|
||
events (loaded / flagged / notice_injected / checked / broken_lists /
|
||
skipped_broken) for headless evidence. `UNSLOP_LISTS=<path>` 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.
|