---
phase: quick-001
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/meshulash_guard/scanners/emotion.py
  - src/meshulash_guard/scanners/__init__.py
  - src/meshulash_guard/__init__.py
  - docs/scanners/emotion.md
  - mkdocs.yml
autonomous: true

must_haves:
  truths:
    - "EmotionScanner and EmotionLabel are importable from meshulash_guard and meshulash_guard.scanners"
    - "EmotionScanner.to_guardline_spec() produces a valid TC spec with the correct head name and label values"
    - "EmotionLabel.ALL expands to all non-ALL labels in the spec (matching ToxicityScanner/CyberScanner behavior)"
    - "docs/scanners/emotion.md exists with Quick Example, Labels table, Parameters table, scan_input and scan_output examples"
    - "mkdocs.yml nav includes EmotionScanner entry under Scanners"
  artifacts:
    - path: "src/meshulash_guard/scanners/emotion.py"
      provides: "EmotionScanner class and EmotionLabel enum"
      exports: ["EmotionScanner", "EmotionLabel"]
    - path: "docs/scanners/emotion.md"
      provides: "Developer-facing scanner documentation"
      contains: "EmotionScanner"
  key_links:
    - from: "src/meshulash_guard/scanners/__init__.py"
      to: "src/meshulash_guard/scanners/emotion.py"
      via: "from .emotion import EmotionScanner, EmotionLabel"
    - from: "src/meshulash_guard/__init__.py"
      to: "src/meshulash_guard/scanners/__init__.py"
      via: "EmotionScanner, EmotionLabel in import and __all__"
---

<objective>
Add EmotionScanner and EmotionLabel to the SDK, following the exact pattern of ToxicityScanner/CyberScanner, and create the matching docs page.

Purpose: Expose the server's emotion detection TC head to SDK users with the same ergonomics as all other scanners.
Output: emotion.py scanner module, updated exports in both __init__.py files, docs/scanners/emotion.md, updated mkdocs.yml nav.
</objective>

<execution_context>
@/Users/shalev/.claude/get-shit-done/workflows/execute-plan.md
@/Users/shalev/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/STATE.md

# Exact scanner template to mirror
@src/meshulash_guard/scanners/toxicity.py

# Files to update
@src/meshulash_guard/scanners/__init__.py
@src/meshulash_guard/__init__.py

# Docs template to mirror
@docs/scanners/toxicity.md

# Nav file to update
@mkdocs.yml
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create emotion.py scanner module</name>
  <files>src/meshulash_guard/scanners/emotion.py</files>
  <action>
    IMPORTANT: Before writing this file, you MUST verify the exact TC head name and label strings
    the server uses. Check if any of the following exist and contain emotion-related entries:
      - Any file named label_config.json, labels.json, or similar in the repo root or server/
      - Any existing test or demo file referencing an emotion head (grep for "emotion" in .py files)

    If no server config is found locally, use these defaults (which match standard Ekman-based emotion
    classification model naming conventions) and add a TODO comment at the top of the file noting
    that the head name and label strings must be verified against the live server's label_config.json:

      _TC_HEAD = "emotion"

      EmotionLabel members (exact server string values — verify before shipping):
        ANGER    = "anger"
        DISGUST  = "disgust"
        FEAR     = "fear"
        JOY      = "joy"
        NEUTRAL  = "neutral"
        SADNESS  = "sadness"
        SURPRISE = "surprise"
        ALL      = "__ALL__"

    Structure the file identically to toxicity.py:
    - Module docstring naming the TC head (or noting it needs verification)
    - `from __future__ import annotations`
    - Import Sequence from typing
    - Import Action, Condition, _StrValueMixin from ..enums
    - Import BaseScanner from .base
    - EmotionLabel(_StrValueMixin) class with docstring and all members above
    - EmotionScanner(BaseScanner) class with:
        _TC_HEAD = "emotion"
        __init__ accepting labels: Sequence[EmotionLabel], action=Action.BLOCK,
          condition=Condition.ANY, threshold=float|None=None, allowlist=list[str]|None=None
        Raises ValueError if labels is empty (same message pattern as ToxicityScanner)
        to_guardline_spec() method — copy logic exactly from toxicity.py, no changes
  </action>
  <verify>
    python -c "from meshulash_guard.scanners.emotion import EmotionScanner, EmotionLabel; s = EmotionScanner(labels=[EmotionLabel.JOY]); print(s.to_guardline_spec())"
    Output must be a dict with "name": "EmotionScanner", "required": {"tc": [["emotion", "joy"]]}.

    python -c "from meshulash_guard.scanners.emotion import EmotionScanner, EmotionLabel; s = EmotionScanner(labels=[EmotionLabel.ALL]); spec = s.to_guardline_spec(); pairs = spec['required']['tc']; assert all(p[0]=='emotion' for p in pairs); assert len(pairs)==7; print('ALL expansion OK:', pairs)"
    Must print 7 pairs (all labels except ALL).
  </verify>
  <done>
    emotion.py exists. EmotionScanner and EmotionLabel importable. Single-label spec correct.
    ALL expansion produces 7 tc pairs, all with head "emotion". ValueError raised for empty labels.
  </done>
</task>

<task type="auto">
  <name>Task 2: Wire exports into both __init__.py files</name>
  <files>
    src/meshulash_guard/scanners/__init__.py
    src/meshulash_guard/__init__.py
  </files>
  <action>
    In src/meshulash_guard/scanners/__init__.py:
    - Add after the existing cyber import line:
        from .emotion import EmotionScanner, EmotionLabel
    - Add "EmotionScanner" and "EmotionLabel" to __all__ (maintain alphabetical grouping with other scanners)

    In src/meshulash_guard/__init__.py:
    - Add EmotionScanner, EmotionLabel to the `from .scanners import (...)` block
    - Add "EmotionScanner" and "EmotionLabel" to __all__
  </action>
  <verify>
    python -c "from meshulash_guard import EmotionScanner, EmotionLabel; print('top-level import OK')"
    python -c "from meshulash_guard.scanners import EmotionScanner, EmotionLabel; print('scanners import OK')"
    python -c "import meshulash_guard; assert 'EmotionScanner' in dir(meshulash_guard); assert 'EmotionLabel' in dir(meshulash_guard); print('__all__ OK')"
  </verify>
  <done>
    EmotionScanner and EmotionLabel importable from both meshulash_guard and meshulash_guard.scanners.
    Both appear in __all__.
  </done>
</task>

<task type="auto">
  <name>Task 3: Write docs page and update mkdocs nav</name>
  <files>
    docs/scanners/emotion.md
    mkdocs.yml
  </files>
  <action>
    Write docs/scanners/emotion.md modeled on docs/scanners/toxicity.md. Adapt all content for emotion
    detection. Required sections in order:

    # EmotionScanner

    ## When to Use This
    Describe use cases: detecting angry or distressed user messages before LLM processing, routing
    sad/fearful messages to human support, auditing LLM responses for emotional tone, sentiment-aware
    moderation, mental health platforms detecting distress signals.

    ## Quick Example
    Show a scan_input call detecting EmotionLabel.ANGER and EmotionLabel.FEAR with Action.BLOCK.
    Include expected output block showing "blocked" and the input text.

    ## Labels
    Table with columns: Label | What It Detects
    One row per label (ANGER, DISGUST, FEAR, JOY, NEUTRAL, SADNESS, SURPRISE, ALL).
    ALL row: "Shorthand to include all seven emotion labels."

    ## Parameters
    Same parameter table as toxicity.md, substituting EmotionScanner/EmotionLabel names.

    ## Actions and Conditions
    Adapt toxicity.md prose for emotion context. Default is Action.BLOCK.
    Mention threshold tuning for emotion confidence. Mention Action.LOG for analytics.
    Link to concepts.md: See the [Concepts page](../concepts.md).

    ## scan_input Example
    Realistic scenario: routing distressed users to human support before passing to LLM.
    Use EmotionLabel.SADNESS and EmotionLabel.FEAR with Action.BLOCK and threshold=0.7.
    Show a list of 2 messages (one neutral, one distressed), loop with guard.scan_input,
    print [status] message[:60]. Include expected output block.

    ## scan_output Example
    Realistic scenario: auditing LLM responses for unintended angry or dismissive tone.
    Use EmotionLabel.ALL with Action.LOG.
    Show 2 simulated responses, loop with guard.scan_output, print based on status.
    Include expected output block.

    In mkdocs.yml, add this line to the Scanners nav section after JailbreakScanner:
        - EmotionScanner: scanners/emotion.md
  </action>
  <verify>
    grep -n "EmotionScanner" /Users/shalev/Desktop/meshulash_guard/docs/scanners/emotion.md | head -5
    grep "EmotionScanner" /Users/shalev/Desktop/meshulash_guard/mkdocs.yml
    # Confirm all 7 label rows present in the Labels table
    grep -c "EmotionLabel\." /Users/shalev/Desktop/meshulash_guard/docs/scanners/emotion.md
    # Should return at least 14 (labels appear in table + code examples)
  </verify>
  <done>
    docs/scanners/emotion.md exists with all 6 sections. mkdocs.yml nav includes EmotionScanner entry.
    All 7 EmotionLabel members documented in the Labels table. Both scan_input and scan_output examples present.
  </done>
</task>

</tasks>

<verification>
python -c "
from meshulash_guard import EmotionScanner, EmotionLabel
s = EmotionScanner(labels=[EmotionLabel.ANGER, EmotionLabel.SADNESS])
spec = s.to_guardline_spec()
assert spec['name'] == 'EmotionScanner'
assert spec['required']['tc'] == [['emotion', 'anger'], ['emotion', 'sadness']]
print('Integration check passed')
"

python -c "
from meshulash_guard import EmotionScanner, EmotionLabel
s = EmotionScanner(labels=[EmotionLabel.ALL])
pairs = s.to_guardline_spec()['required']['tc']
assert len(pairs) == 7, f'Expected 7 pairs, got {len(pairs)}'
assert all(p[0] == 'emotion' for p in pairs)
print('ALL expansion check passed')
"

grep "EmotionScanner" /Users/shalev/Desktop/meshulash_guard/mkdocs.yml
</verification>

<success_criteria>
- EmotionScanner and EmotionLabel importable from meshulash_guard and meshulash_guard.scanners
- to_guardline_spec() produces correct TC pairs with head name "emotion"
- EmotionLabel.ALL expands to all 7 non-ALL labels in the spec
- docs/scanners/emotion.md has all 6 sections with correct label table and working code examples
- mkdocs.yml nav lists EmotionScanner under Scanners
</success_criteria>

<output>
After completion, create `.planning/quick/001-add-emotionscanner-to-sdk-and-docs/001-SUMMARY.md`
</output>
