Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@ jobs:
with:
python-version: "3.12"
- run: python scripts/check-routing-drift.py --verbose

joy-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: pip install pytest pyyaml
- run: python -m pytest scripts/tests/test_joy_check_instruction_mode.py -v --tb=short
205 changes: 205 additions & 0 deletions private-skills/voice/skills/joy-check/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
---
name: joy-check
description: "Validate content framing on joy-grievance spectrum."
user-invocable: false
argument-hint: "[--fix] [--strict] [--mode writing|instruction] <file>"
command: /joy-check
allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
routing:
triggers:
- joy check
- check framing
- tone check
- negative framing
- joy validation
- too negative
- reframe positively
- positive framing check
- instruction framing
pairs_with:
- voice-writer
- anti-ai-editor
- voice-validator
- skill-creator
complexity: Simple
category: content
---

# Joy Check

Two modes:

- **writing** — Joy-grievance spectrum for human-facing content (blog posts, emails, articles). Evaluates curiosity/generosity vs. grievance/accusation framing.
- **instruction** — Positive framing for LLM-facing content (agents, skills, pipelines). Evaluates "what to do" vs. "what to avoid" (ADR-127).

Evaluates each paragraph/instruction independently, produces a score (0-100), suggests reframes without modifying content. Flags: `--fix` rewrites flagged items in place and re-verifies; `--strict` fails on any item below 100; `--mode writing|instruction` overrides auto-detection.

Checks *framing*, not *topic* or *voice*. Voice fidelity → voice-validator. AI pattern detection → anti-ai-editor.

## Reference Loading Table

| Signal | Load These Files | Why |
|---|---|---|
| tasks related to this reference | `instruction-rubric.md` | Loads detailed guidance from `instruction-rubric.md`. |
| tasks related to this reference | `writing-rubric.md` | Loads detailed guidance from `writing-rubric.md`. |

## Instructions

### Phase 0: DETECT MODE

Auto-detection (priority order):
1. Explicit `--mode` flag → use that
2. `agents/*.md` → **instruction**
3. `skills/*/SKILL.md` → **instruction**
4. `skills/workflow/references/*.md` → **instruction**
5. `CLAUDE.md` or `README.md` → **instruction**
6. Everything else → **writing**

Load `references/{mode}-rubric.md` for scoring criteria and examples.

**GATE**: Mode determined, rubric loaded.

### Phase 1: PRE-FILTER

Regex scanning as a fast gate before LLM semantic analysis.

**Writing mode**:
```bash
python3 ~/.claude/scripts/scan-negative-framing.py [file]
```

**Instruction mode**:
```bash
grep -nE 'NEVER|do NOT|must NOT|FORBIDDEN' [file]
grep -nE "^-?\s*Don't|^-?\s*Avoid|^#+.*Anti-[Pp]attern|^#+.*Avoid" [file]
```

Report findings with reframe suggestions from the rubric. If `--fix`, apply reframes and re-run.

**GATE**: Zero regex/grep hits. Resolve obvious patterns before Phase 2.

### Phase 2: ANALYZE

**Step 1: Read content**

Read full file. Skip frontmatter and code blocks.
- **Writing**: Identify paragraphs (blank-line separated). Skip blockquotes.
- **Instruction**: Identify instructional statements — bullets, table cells, imperatives, headings. Skip examples, code blocks, quoted dialogue, file paths.

**Step 2: Evaluate against rubric**

Apply scoring dimensions from `references/{mode}-rubric.md`.

For **writing**: Joy-grievance lens. Watch for subtle patterns in `references/writing-rubric.md` (defensive disclaimers, accumulative grievance, passive-aggressive factuality, reluctant generosity).

For **instruction**: Positive-negative lens. Check against patterns table in `references/instruction-rubric.md`. Contextual exceptions: subordinate negatives attached to positive instructions are PASS, as are negatives in code examples, writing samples, and technical terms.

**Step 3: Score each item**

Apply the rubric's scoring scale. For items scoring CAUTION/GRIEVANCE (writing) or NEGATIVE-LEANING/PROHIBITION-HEAVY (instruction), draft specific reframe suggestions preserving substance.

Flag subtle patterns with priority — they are the primary purpose of this LLM phase. The regex pre-filter catches explicit patterns; LLM analysis exists precisely to catch what regex misses.

**GATE**: All items scored. Reframe suggestions drafted for flagged items.

### Phase 3: REPORT

**Step 1: Calculate overall score**

Average all item scores. Pass criteria:
- **Writing**: Score == 100 AND zero GRIEVANCE paragraphs
- **Instruction**: Score == 100 AND zero primary negative patterns in instructional context

**Step 2: Output**

```
JOY CHECK: [file]
Mode: [writing|instruction]
Score: [0-100]
Status: PASS / FAIL

Items:
[writing mode]
P1 (L10-12): JOY [85] -- explorer framing, curiosity
P3 (L18-22): CAUTION [40] -- "confused" leans defensive
-> Reframe: Focus on what you learned from the confusion

[instruction mode]
L33: NEGATIVE [20] -- "NEVER edit code directly"
-> Rewrite: "Route all code modifications to domain agents"
L45: PASS [90] -- "Create feature branches for all changes"
L78: PASS [85] -- "Credentials stay in .env files, never in code" (subordinate negative OK)

Overall: [summary of framing arc]
```

**Step 3: Fix mode**

If `--fix`:
1. Rewrite flagged items using drafted suggestions
2. Preserve substance — change only framing
3. Re-run Phase 2 on rewrites to verify
4. Maximum 3 iterations if fixes introduce new flags

**GATE**: Report produced. If `--fix`, all rewrites applied and re-verified.

---

### Integration

**Writing pipeline**:
```
CONTENT --> voice-validator --> scan-ai-patterns --> joy-check --mode writing --> anti-ai-editor
```

**Instruction pipeline**:
```
SKILL.md --> joy-check --mode instruction --> fix flagged patterns --> re-verify
```

**Auto-invocation points**:
- `skill-creator`: after generating a new skill
- `agent-upgrade`: after modifying an agent
- `voice-writer`: during validation
- `doc-pipeline`: for toolkit documentation

Invoke standalone via `/joy-check [file]` (auto-detects mode) or with explicit `--mode`.

---

## Error Handling

### Error: "File Not Found"
Verify path with `ls -la`. Use glob to search: `Glob **/*.md`. Confirm working directory.

### Error: "Regex Scanner Fails or Not Found"
Verify `scripts/scan-negative-framing.py` exists. Requires Python 3.10+. If unavailable, skip to Phase 2 — the pre-filter is an optimization, not a requirement.

### Error: "All Paragraphs Score GRIEVANCE"
Content is fundamentally grievance-framed. Report scores honestly. Suggest full rewrite with different framing premise, not paragraph-level fixes.

### Error: "Fix Mode Fails After 3 Iterations"
Output best version with remaining concerns. Explain which rubric dimensions resist correction. The framing premise itself may need rethinking.

---

## References

### Rubric Files
- `references/writing-rubric.md` — Joy-grievance spectrum, subtle patterns, scoring, examples
- `references/instruction-rubric.md` — Positive framing rules, patterns, rewrite strategies, examples

### Scripts
- `scan-negative-framing.py` — Regex pre-filter for grievance patterns (writing mode, Phase 1)

### Complementary Skills
- `voice-validator` — Voice fidelity (different concern)
- `anti-ai-editor` — AI pattern detection (different concern)
- `voice-writer` — Invokes joy-check during validation
- `skill-creator` — Invokes joy-check in instruction mode
136 changes: 136 additions & 0 deletions private-skills/voice/skills/joy-check/references/instruction-rubric.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Instruction Rubric — Positive Framing for LLM Instructions

This rubric applies to agent, skill, and pipeline markdown files — instructions read by LLMs, not humans. The principle: state the desired action, not the forbidden one. An LLM needs to know what TO DO, not what to avoid.

## Positive Instruction Framing Rubric

Every instruction should tell the reader what action to take. Prohibitions define a boundary without specifying where to go; positive framing gives a clear action target.

| Dimension | Positive (PASS) | Negative (FAIL) |
|-----------|----------------|----------------|
| **Action framing** | "Route all code modifications to domain agents" | "NEVER edit code directly" |
| **Specific instruction** | "Stage files by name: `git add specific-file.py`" | "do NOT use git add -A" |
| **Table headings** | "Preferred Patterns", "Hard Gate Patterns" | "Anti-Patterns", "FORBIDDEN Patterns" |
| **Safety boundaries** | "Create feature branches for all changes" | "Never commit to main" |
| **Error handling** | "exit 0 on errors to keep tools available" | "must NEVER block tools" |
| **Double negatives** | "Run validation before marking complete" | "Don't skip validation" |
| **Section organization** | "What to do" tables showing correct approach | "What NOT to do" tables showing prohibited approach |

## Patterns to Flag

### Primary patterns (always flag when used as instructions)

| Pattern | Regex | Example |
|---------|-------|---------|
| NEVER (caps) | `\bNEVER\b` | "NEVER edit code directly" |
| do NOT / Do NOT | `\b[Dd]o NOT\b` | "Do NOT use git add -A" |
| must NOT | `\bmust NOT\b` | "must NOT block tools" |
| FORBIDDEN | `\bFORBIDDEN\b` | "FORBIDDEN Patterns" |
| Don't (instruction start) | `^-?\s*Don't\b` | "Don't mock the database" |
| Avoid (as heading/instruction) | `^\s*#{1,6}.*Avoid|^-?\s*Avoid\b` | "### Patterns to Avoid" |
| Anti-Pattern (in headings) | `^\s*#{1,6}.*[Aa]nti-[Pp]attern` | "### Common Anti-Patterns" |

### Contextual exceptions (allow these)

These are PASS even though they contain negative words:

- **Subordinate negatives attached to positive instructions**: "Credentials stay in .env files, never in code" — the primary instruction is positive ("stay in .env files"), the "never" is a subordinate boundary clarification
- **Code examples showing bad patterns**: `// NEVER` in a code comment demonstrating what SQL injection looks like — this is illustrative, not instructional
- **Writing samples and user dialogue**: "Don't do this!" in an example of how users speak — this is quoted content
- **Technical terms**: "Copula Avoidance" is a proper term for an AI writing pattern — the word "Avoidance" is part of the term, not a prohibition
- **File path references**: `references/preferred-patterns.md` — this is a filename, not an instruction
- **Descriptive text about behavior**: "tests do not cover edge cases" — this describes a state, not an instruction

## Rewrite Rules

When flagging a negative pattern, suggest a specific positive rewrite:

| Negative Pattern | Positive Rewrite Strategy |
|-----------------|--------------------------|
| Prohibition ("NEVER X") | State the action: "Do Y instead" |
| Warning ("do NOT use X") | Give the specific alternative: "Use Y: `example`" |
| Anti-pattern table | Invert to pattern table: show what to do, not what to avoid |
| Fear-based ("must NEVER block") | State the outcome: "exit 0 to keep available" |
| Double negative ("Don't skip") | Direct instruction: "Run before marking complete" |
| "Avoid" heading | Replace with "Preferred" or "Recommended" |
| "Anti-Pattern" heading | Replace with "Preferred Patterns" or "Patterns to Detect and Fix" |

## Scoring

| Score | Label | Meaning |
|-------|-------|---------|
| 80-100 | **POSITIVE** | Instructions frame through desired actions |
| 50-79 | **MIXED** | Some instructions are positive, some are prohibition-based |
| 30-49 | **NEGATIVE-LEANING** | Most instructions tell what to avoid rather than what to do |
| 0-29 | **PROHIBITION-HEAVY** | Instructions are primarily "don't do X" framing |

**Pass criteria**: Score == 100 AND zero primary negative patterns in instructional context.

## Principles

1. **State the desired action, not the forbidden one** — The LLM needs to know what TO DO
2. **Preserve safety intent** — "Never commit to main" becomes "Create feature branches for all changes" — same protection, positive framing
3. **Replace anti-pattern tables with pattern tables** — Show "What to do instead", not "What NOT to do"
4. **Keep the WHY** — "because X" explanations stay unchanged; only the framing changes
5. **Subordinate negatives are fine** — "Credentials stay in .env files, never in code" is PASS because the positive instruction leads

## Examples

### Example 1: Router Instructions

**NEGATIVE (FAIL):**
```markdown
**What the main thread NEVER does:** Read code files, edit files, run tests,
write docs, handle ANY Simple+ task directly.
```

**POSITIVE (PASS):**
```markdown
**The main thread delegates to agents:** code reading (Explore agent), file
edits (domain agents), test runs (agent with skill), documentation
(technical-documentation-engineer), all Simple+ tasks.
```

**Why the second works:** Tells the LLM exactly where each task type goes instead of listing what's forbidden.

### Example 2: Safety Boundaries

**NEGATIVE (FAIL):**
```markdown
Route to agents that create branches; never allow direct main/master commits,
because main branch commits affect everyone.
```

**POSITIVE (PASS):**
```markdown
Route to agents that create feature branches for all commits, because main
branch commits affect everyone.
```

**Why the second works:** Same safety boundary, but the instruction says what to create (feature branches) rather than what to prevent (main commits).

### Example 3: Section Headings

**NEGATIVE (FAIL):**
```markdown
## Anti-Patterns
### FORBIDDEN Patterns (HARD GATE)
| Pattern | Why FORBIDDEN |
```

**POSITIVE (PASS):**
```markdown
## Preferred Patterns
### Hard Gate Patterns
| Pattern | Why Blocked |
```

**Why the second works:** "Preferred Patterns" tells the reader what to aim for. "Hard Gate Patterns" preserves the enforcement without the fear framing.

### Example 4: Subordinate Negative (PASS)

```markdown
Credentials stay in .env files, never in code or logs.
```

This is PASS — the primary instruction is positive ("stay in .env files") and the "never" is a subordinate boundary that clarifies the positive instruction. The reader knows both what to do AND the boundary.
Loading
Loading