skill-slide-critic
$
npx mdskill add benbrastmckie/nvim/skill-slide-criticInteractive critique feedback loop for academic presentations. Delegates to slide-critic-agent for initial material review, parses the structured critique report, presents findings to the user grouped by severity tier, collects accept/reject/modify decisions, loops until all issues are addressed, and produces a final filtered critique report consumable by `/plan`.
SKILL.md
.github/skills/skill-slide-criticView on GitHub ↗
---
name: skill-slide-critic
description: Interactive critique loop for slide presentations. Delegates to slide-critic-agent, presents findings to user, collects accept/reject/modify decisions, produces filtered critique report.
allowed-tools: Agent, Bash, Edit, Read, Write, AskUserQuestion
context: fork
agent: slide-critic-agent
---
# Slide Critic Skill
Interactive critique feedback loop for academic presentations. Delegates to slide-critic-agent for
initial material review, parses the structured critique report, presents findings to the user grouped
by severity tier, collects accept/reject/modify decisions, loops until all issues are addressed, and
produces a final filtered critique report consumable by `/plan`.
**IMPORTANT**: This skill implements the skill-internal postflight pattern. After the subagent returns
and the interactive loop completes, this skill handles all postflight operations (status update,
artifact linking, git commit) before returning.
## Context References
Reference (do not load eagerly):
- Path: `.claude/context/formats/return-metadata-file.md` - Metadata file schema
- Path: `.claude/context/patterns/postflight-control.md` - Marker file protocol
- Path: `.claude/context/patterns/file-metadata-exchange.md` - File I/O helpers
- Path: `.claude/context/patterns/jq-escaping-workarounds.md` - jq escaping patterns (Issue #1132)
Note: This skill runs delegation then interactive Q&A. Context is loaded by the delegated agent.
## Trigger Conditions
This skill activates when:
- `/critique` command with task number input
- `/research` on a task with `task_type: "present:slides"` and `workflow_type: "slides_critique"`
- Present extension is available
- Task has existing slide materials to review (research reports, plans, or assembled slides)
---
## Input Parameters
### Required Parameters
- `task_number` - Task number (must exist in state.json with task_type containing "slides")
- `session_id` - Session ID from orchestrator
### Optional Parameters
- `focus_categories` - Subset of 6 rubric categories to prioritize (e.g., ["Narrative Flow", "Timing Balance"])
- `audience_context` - Audience description for calibrating review
- `materials_to_review` - Override default material discovery (array of file paths)
---
## Execution Flow
### Stage 1: Input Validation
Validate required inputs:
- `task_number` - Must be provided and exist in state.json
- Verify task_type contains "slides" (supports "present:slides", "slides")
```bash
# Lookup task
task_data=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num)' \
specs/state.json)
# Validate exists
if [ -z "$task_data" ]; then
return error "Task $task_number not found"
fi
# Extract fields
task_type=$(echo "$task_data" | jq -r '.task_type // ""')
status=$(echo "$task_data" | jq -r '.status')
project_name=$(echo "$task_data" | jq -r '.project_name')
description=$(echo "$task_data" | jq -r '.description // ""')
forcing_data=$(echo "$task_data" | jq -r '.forcing_data // {}')
# Validate task_type (supports "present:slides" or legacy "slides")
if [ "$task_type" != "present:slides" ] && [ "$task_type" != "slides" ]; then
return error "Task $task_number is not a slides task (task_type=$task_type)"
fi
# Extract talk_type from forcing_data
talk_type=$(echo "$forcing_data" | jq -r '.talk_type // "CONFERENCE"')
```
---
### Stage 2: Preflight Status Update
Update task status to `researching` BEFORE invoking subagent.
```bash
padded_num=$(printf "%03d" "$task_number")
task_dir="specs/${padded_num}_${project_name}"
mkdir -p "$task_dir"
# Update state.json
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg sid "$session_id" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
status: "researching",
last_updated: $ts,
session_id: $sid
}' specs/state.json > specs/tmp/state.json && mv specs/tmp/state.json specs/state.json
# Update TODO.md marker to [RESEARCHING]
```
Create postflight marker:
```bash
cat > "${task_dir}/.postflight-pending" << EOF
{
"session_id": "${session_id}",
"skill": "skill-slide-critic",
"task_number": ${task_number},
"operation": "slides_critique",
"reason": "Postflight pending: status update, artifact linking, git commit",
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"stop_hook_active": false
}
EOF
```
---
### Stage 3: Prepare Delegation Context
Discover materials to review (if not provided via `materials_to_review`):
```bash
# Auto-discover materials in task directory
report_files=$(ls -1 "${task_dir}/reports/"*.md 2>/dev/null)
plan_files=$(ls -1 "${task_dir}/plans/"*.md 2>/dev/null)
# Check for assembled slides
talk_dir="talks/${task_number}_${project_name}"
slide_files=$(ls -1 "${talk_dir}/slides.md" 2>/dev/null)
```
Build the materials array from discovered files, or use the `materials_to_review` parameter if provided.
**Delegation context**:
```json
{
"session_id": "{session_id}",
"delegation_depth": 1,
"delegation_path": ["orchestrator", "critique", "skill-slide-critic", "slide-critic-agent"],
"timeout": 3600,
"task_context": {
"task_number": N,
"task_name": "{project_name}",
"description": "{description}",
"task_type": "present:slides"
},
"workflow_type": "slides_critique",
"forcing_data": {
"talk_type": "{talk_type}",
"materials_to_review": ["{discovered or provided paths}"],
"focus_categories": "{focus_categories or omitted}",
"audience_context": "{audience_context or omitted}"
},
"metadata_file_path": "specs/{NNN}_{SLUG}/.return-meta.json"
}
```
---
### Stage 4: Invoke Subagent
**CRITICAL**: Use the **Agent** tool to spawn the slide-critic-agent. Do NOT use `Skill(...)`.
```
Tool: Agent (NOT Skill, NOT Plan)
Parameters:
- subagent_type: "slide-critic-agent"
- prompt: [Include full delegation context from Stage 3]
- description: "Critique presentation materials for task {N}"
```
The slide-critic-agent will:
1. Load the critique rubric and priority matrix
2. Read all review materials
3. Evaluate against 6 rubric categories (or focus_categories)
4. Write a critique report to `specs/{NNN}_{SLUG}/reports/{MM}_slide-critique.md`
5. Write metadata to `specs/{NNN}_{SLUG}/.return-meta.json`
**DO NOT** use `Skill(...)` - this will FAIL. Always use `Task`.
---
### Stage 4b: Self-Execution Fallback
**CRITICAL**: If you performed the work above WITHOUT using the Agent tool (i.e., you read files,
wrote artifacts, or updated metadata directly instead of spawning a subagent), you MUST write a
`.return-meta.json` file now before proceeding. Use the schema from `return-metadata-file.md`
with status `researched`.
If you DID use the Agent tool, skip this stage -- the subagent already wrote the metadata.
---
## Interactive Critique Loop (Stages 5-7)
### Stage 5: Parse Critique Report
After the subagent returns, read the critique report and extract findings.
1. **Read metadata file** to get the critique report path:
```bash
metadata_file="${task_dir}/.return-meta.json"
meta_status=$(jq -r '.status' "$metadata_file")
critique_report_path=$(jq -r '.artifacts[0].path // ""' "$metadata_file")
findings_count=$(jq -r '.metadata.findings_count // {}' "$metadata_file")
```
2. **If agent failed or no findings**: Skip the interactive loop. Proceed directly to postflight with the agent's status.
3. **Read the critique report** and parse findings using these patterns:
**Per-slide heading**: `### Slide N ({slide_type})`
**Finding line**: `- [{severity}] {category}: {description}`
**Suggestion line**: `Suggested improvement: {text}` (indented under finding)
**General heading**: `### General (Cross-Cutting)`
**Recommendation tiers**: `### Must Fix`, `### Should Fix`, `### Nice to Fix`
4. **Build numbered issue list**:
```
issues = [
{ id: 1, slide: "Slide 3", severity: "Critical", category: "Narrative Flow",
description: "...", suggestion: "...", tier: "Must Fix" },
{ id: 2, slide: "Slide 7", severity: "Critical", category: "Audience Alignment",
description: "...", suggestion: "...", tier: "Must Fix" },
{ id: 3, slide: "General", severity: "Major", category: "Timing Balance",
description: "...", suggestion: "...", tier: "Should Fix" },
...
]
```
Assign tier based on severity:
- Critical -> "Must Fix"
- Major -> "Should Fix"
- Minor -> "Nice to Fix"
5. **If no findings found in report** (agent found no issues): Report success with zero findings. Skip interactive loop.
---
### Stage 6: Interactive Critique Loop
Present all findings grouped by severity tier in a single consolidated AskUserQuestion.
Collect user decisions and loop until all issues are addressed or user exits.
**AskUserQuestion format**:
```
Critique findings for your {talk_type} presentation ({N} total issues):
=== MUST FIX ({count}) ===
1. [Critical] {category} - {slide}: {description}
Suggested: {suggestion}
2. [Critical] {category} - {slide}: {description}
Suggested: {suggestion}
=== SHOULD FIX ({count}) ===
3. [Major] {category} - {slide}: {description}
Suggested: {suggestion}
4. [Major] {category} - {slide}: {description}
Suggested: {suggestion}
=== NICE TO FIX ({count}) ===
5. [Minor] {category} - {slide}: {description}
Suggested: {suggestion}
---
For each issue, respond with its number and action:
1: A (accept as-is)
3: R (reject/dismiss)
5: M add comparison to Smith 2024 (modify the suggestion)
Shortcuts: "accept all", "reject all minor", "done" (accept remaining)
```
**Response parsing grammar**:
Parse user response line by line. Each line matches one of:
| Pattern | Action | Effect |
|---------|--------|--------|
| `{N}: A` | Accept issue N | Mark as accepted, use original suggestion |
| `{N}: R` | Reject issue N | Mark as rejected/dismissed |
| `{N}: M {text}` | Modify issue N | Mark as modified, store user's text |
| `accept all` | Bulk accept | Accept all unaddressed issues |
| `reject all minor` | Bulk reject minor | Reject all Minor severity issues |
| `reject all` | Bulk reject | Reject all unaddressed issues |
| `done` | Finish | Accept all remaining unaddressed issues |
Track decisions per issue:
```
decisions = {
1: { action: "accepted", modification: null },
3: { action: "rejected", modification: null },
5: { action: "modified", modification: "add comparison to Smith 2024" },
...
}
```
**Loop continuation**:
After processing responses:
- If all issues are addressed (each has an accepted/rejected/modified decision): proceed to Stage 7
- If unaddressed issues remain AND user did not say "done": re-present ONLY unaddressed issues in a follow-up AskUserQuestion
- Maximum 3 loop iterations to prevent infinite cycles. After 3 iterations, auto-accept all remaining unaddressed issues.
---
### Stage 7: Generate Filtered Critique Report
Write the final filtered report incorporating user decisions.
Determine artifact number:
```bash
next_num=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num) | .next_artifact_number // 2' \
specs/state.json)
# Use next available number for the filtered report
filtered_num=$(printf "%02d" "$next_num")
```
Write to `specs/{NNN}_{SLUG}/reports/{MM}_filtered-critique.md`:
```markdown
# Filtered Critique Report: {title}
- **Task**: {N} - {description}
- **Talk Type**: {talk_type}
- **Original Findings**: {total} ({critical} critical, {major} major, {minor} minor)
- **Accepted**: {accepted_count} ({modified_count} with modifications)
- **Rejected**: {rejected_count}
## Accepted Issues
### Must Fix ({count})
- Slide {N}: [{severity}] {category}: {description}
Suggested: {original suggestion}
{If modified: "User modification: {user text}"}
### Should Fix ({count})
- Slide {N}: [{severity}] {category}: {description}
Suggested: {original suggestion}
### Nice to Fix ({count})
- Slide {N}: [{severity}] {category}: {description}
Suggested: {original suggestion}
## Rejected Issues ({count})
{Listed for reference but marked as dismissed}
- Slide {N}: [{severity}] {category}: {description} -- DISMISSED
## User Notes
{Any freeform feedback captured during the loop, or "None"}
## Source Critique Report
Original critique: {critique_report_path}
```
Update `.return-meta.json` with final artifact info:
```json
{
"status": "researched",
"artifacts": [
{
"type": "report",
"path": "specs/{NNN}_{SLUG}/reports/{MM}_filtered-critique.md",
"summary": "Filtered critique: {accepted_count} accepted, {rejected_count} rejected of {total} findings"
}
],
"metadata": {
"findings_count": {
"total": N,
"accepted": N,
"rejected": N,
"modified": N
}
}
}
```
---
## Postflight (ALWAYS EXECUTE)
The following stages MUST execute after work is complete, whether the work was done by a
subagent or inline. Do NOT skip these stages for any reason.
### Stage 8: Read Metadata File
```bash
metadata_file="${task_dir}/.return-meta.json"
if [ -f "$metadata_file" ] && jq empty "$metadata_file" 2>/dev/null; then
meta_status=$(jq -r '.status' "$metadata_file")
artifact_path=$(jq -r '.artifacts[0].path // ""' "$metadata_file")
artifact_type=$(jq -r '.artifacts[0].type // ""' "$metadata_file")
artifact_summary=$(jq -r '.artifacts[0].summary // ""' "$metadata_file")
else
echo "Error: Invalid or missing metadata file"
meta_status="failed"
fi
```
---
### Stage 9: Update Task Status (Postflight)
| Meta Status | Final state.json | Final TODO.md |
|-------------|-----------------|---------------|
| researched | researched | [RESEARCHED] |
| partial | researching | [RESEARCHING] |
| failed | (keep preflight) | (keep preflight marker) |
---
### Stage 10: Link Artifacts
Add artifact to state.json with summary. Use the two-step jq pattern to avoid Issue #1132.
**Update TODO.md**: Link artifact per `@.claude/context/patterns/artifact-linking-todo.md` with `field_name=**Report**`, `next_field=**Description**`.
---
### Stage 11: Git Commit
```bash
git add -A
git commit -m "task ${task_number}: complete slide critique
Session: ${session_id}"
```
---
### Stage 12: Cleanup
```bash
rm -f "${task_dir}/.postflight-pending"
rm -f "${task_dir}/.postflight-loop-guard"
rm -f "${task_dir}/.return-meta.json"
```
---
### Stage 13: Return Brief Summary
**Success**:
```
Slide critique completed for task {N}:
- Evaluated materials against {category_count} rubric categories
- Talk type: {talk_type}
- Original findings: {critical} critical, {major} major, {minor} minor
- User decisions: {accepted} accepted ({modified} modified), {rejected} rejected
- Filtered report: specs/{NNN}_{SLUG}/reports/{MM}_filtered-critique.md
- Status updated to [RESEARCHED]
- Changes committed with session {session_id}
```
**No findings**:
```
Slide critique completed for task {N}:
- No issues found by slide-critic-agent
- Materials appear well-structured for {talk_type} presentation
- Status updated to [RESEARCHED]
- Changes committed with session {session_id}
```
**Partial**:
```
Slide critique partially completed for task {N}:
- Agent produced findings but interactive loop was interrupted
- Run critique again to resume (existing report preserved)
```
---
## Error Handling
### Task not found
```
Slide critique error for task {N}:
- Task not found in state.json
- No status changes made
```
### Wrong task type
```
Slide critique error for task {N}:
- Task is not a slides task (task_type={task_type})
- No status changes made
```
### Metadata file missing after agent
Keep status at preflight level (researching) for resume.
### No findings from agent
Not an error. Report "no issues found" as success. Skip interactive loop. Write researched status.
### User abandons mid-loop
Accept all remaining unaddressed issues. Proceed to filtered report generation. Note in report that some issues were auto-accepted due to early exit.
### Git commit failure
Non-blocking. Log failure but continue.
---
## Return Format
This skill returns a **brief text summary** (NOT JSON). The JSON metadata is written to the file and processed internally.
More from benbrastmckie/nvim
- skill-analyzeCompetitive landscape research with positioning maps
- skill-budgetGrant budget spreadsheet generation with forcing questions. Invoke for budget tasks.
- skill-consultRoute design consultations to domain-specific design partner agents
- skill-deckGenerate YC-style investor pitch decks in Typst
- skill-deck-implementRoute deck implementation to deck-builder-agent for Slidev pitch deck generation
- skill-deck-planPitch deck planning with interactive template, content, and ordering selection
- skill-deck-researchPitch deck content research through material synthesis
- skill-docx-editIn-place DOCX editing routing to docx-edit-agent
- skill-epi-implementImplementation skill for R-based epidemiology analysis. Invoke for epi/epi:study implementation tasks.
- skill-epi-researchResearch skill for epidemiology study design and analysis planning. Invoke for epi/epi:study research tasks.