Review an essay against a rubric, one criterion at a time
A single prompt grades the whole essay at once and its scores drift. This workflow reads the rubric, scores each criterion on its own with quotes, re-checks every score against the essay and the points allowed, and only then writes the feedback.
5 steps in 4 stages · 5 subagents · a kit of 10 files
Free. Built in your browser; nothing is uploaded. No API key, no account.
How it runs
Steps in the same stage start at the same time. Each subagent gets only what its incoming edges carry, and a step in a fresh context never sees the reasoning of the steps before it. A gate decides whether the work passes, and a failed gate re-runs the work it checks.
- Stage 12 in parallel
Read the rubric · Planner
sonnet · WebFetch
Line edits · Worker
sonnet · WebFetch
- Stage 2
Score each criterion · Worker
sonnet · WebFetch · once per item of rubric.criteria
- Stage 3
Check the scores · Checker
opus · WebFetch · fresh context
Gate: Every score quotes the essay, stays within its criterion's points, and no two scores contradict each other.
- Stage 4
Feedback · Writer
sonnet · WebFetch
Use it when
- You are a student checking your essay before you hand it in.
- You are a teacher who wants consistent first-pass feedback.
- You want feedback tied to quotes, not impressions.
Change these first
- Paste the rubric exactly as given.
- Say whether you want line edits or only scores and comments.
Need a workflow built for your team? Fixed-price packages
What you get
An orchestrating SKILL.md, one Claude Code subagent per step with its own tools and model, and the same skill as a Cursor rule. Your agent runs it with its own subagents; nothing on this site calls a model.
- .claude/skills/essay-rubric-review/SKILL.mdorchestrator
- .claude/skills/essay-rubric-review/graph.skill.mdgraph source
- .claude/agents/essay-rubric-review-rubric.mdsubagent
- .claude/agents/essay-rubric-review-score-criterion.mdsubagent
- .claude/agents/essay-rubric-review-line-edits.mdsubagent
- .claude/agents/essay-rubric-review-score-check.mdsubagent
- .skill-graph/.gitignoresupport
- .cursor/rules/essay-rubric-review.mdcCursor rule
- README.mdreadme
- + 1 more subagents
---
name: essay-rubric-review
description: "Reviews an essay against a rubric, one criterion at a time, checks every score against quotes from the essay, then writes the feedback."
---
# essay-rubric-review
Reviews an essay against a rubric, one criterion at a time, checks every score against quotes from the essay, then writes the feedback.
This skill is a graph: each step has its own prompt and context, and only the payloads named below move between steps.
## Plan
Run the stages in order. Every model step runs in its own subagent; do not do a step's work yourself. Only command steps and approvals are yours to run. Steps in the same stage do not depend on each other: start them together, in one message with several subagent calls.
The input (`essay`) is the user's goal: their request, word for word, handed on under `### essay`. Ask for it if the user has not given one.
### Stage 1 · 2 steps in parallel
- **READ THE RUBRIC** (`rubric`) → subagent `essay-rubric-review-rubric`
- Hand it: the user's goal.
- Expect: `criteria_list` (json).
- Contract check (`criteria_list`): save its output to `.skill-graph/essay-rubric-review/rubric.json`, then run `node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));const k=['criteria'];const ok=d!==null&&typeof d==='object'&&Array.isArray(d)===false;const m=ok?k.filter((x)=>Object.hasOwn(d,x)===false):k;if(m.length>0){console.error('Missing keys: '+m.join(', '));process.exit(1)}" .skill-graph/essay-rubric-review/rubric.json` with Bash. Exit code 0 passes.
- If a contract check fails: re-run this step once with its previous answer and the error output. If it fails again, stop and show the user the output.
- **LINE EDITS** (`line_edits`) → subagent `essay-rubric-review-line-edits`
- Hand it: the user's goal.
- Expect: `edits` (markdown).
### Stage 2 · 1 step
- **SCORE EACH CRITERION** (`score_criterion`) → subagent `essay-rubric-review-score-criterion` · one run per item, up to 4 at a time
- Take the list: the `criteria` array in the JSON of READ THE RUBRIC (`rubric`). If it is not an array, stop and report. If it is empty, skip this step: its output is `[]`.
- If the list has more than 12 items, stop before starting any run and tell the user how many there are.
- Start one run of the subagent per item, up to 4 at a time (several subagent calls in one message); start the next as one finishes.
- Hand each run: the user's goal; its item: one entry of `criteria` from the JSON of READ THE RUBRIC (`rubric`).
- Expect, per run: the result for its item. Checks and gates below apply to each run on its own; a retry re-runs only the runs that failed.
- Output: a JSON array of the runs' results, in item order. That array is what later steps get.
### Stage 3 · 1 step
… 37 more lines in the kitRun it in a Claude chat
Add Skill Graph to Claude as a connector, then ask. Claude runs each step and the connector checks every answer before the next one starts.
Set up the connectorAsk Claude
Use Skill Graph to run the essay-rubric-review workflow on: Here is my essay and the rubric my teacher gave: …The graph file
The whole workflow is one Markdown file, graph.skill.md: the steps and edges in the frontmatter, one prompt per step below it. The guide explains every field.
graph.skill.md · 121 lines
---
name: essay-rubric-review
description: Reviews an essay against a rubric, one criterion at a time, checks every score against quotes from the essay, then writes the feedback.
spec: skill-graph/1
topology:
nodes:
- id: essay
type: input
label: ESSAY AND RUBRIC
- id: rubric
type: orchestrator
label: READ THE RUBRIC
model: sonnet
tools: [WebFetch]
output_format: json
- id: score_criterion
type: worker
label: SCORE EACH CRITERION
model: sonnet
tools: [WebFetch]
foreach: rubric.criteria
max_items: 12
- id: line_edits
type: worker
label: LINE EDITS
model: sonnet
tools: [WebFetch]
- id: score_check
type: verifier
label: CHECK THE SCORES
isolated_context: true
model: opus
tools: [WebFetch]
gate: Every score quotes the essay, stays within its criterion's points, and no two scores contradict each other.
on_fail: retry
max_retries: 1
- id: feedback
type: synthesizer
label: FEEDBACK
model: sonnet
tools: [WebFetch]
edges:
- from: essay
to: [rubric, score_criterion, line_edits, score_check]
- from: rubric
to: score_criterion
contract: criteria_list
- from: score_criterion
to: score_check
contract: criterion_scores
- from: line_edits
to: score_check
contract: edits
- from: score_check
to: feedback
contract: checked_review
contracts:
criteria_list:
format: json
description: "{ criteria: one entry per rubric criterion, each with name, what good looks like and the points it is worth }"
required: [criteria]
criterion_scores:
format: json
description: One entry per criterion, in rubric order; each has the score, the quotes behind it and one fix.
edits:
format: markdown
description: A numbered list of line edits, each quoting the sentence and giving the better version.
checked_review:
format: markdown
description: The checked scores with their quotes and fixes, the line edits worth keeping, and what was corrected.
---
# Node Prompts & Edge Contracts
## @node: rubric
Read the essay and rubric below. If no rubric is given, use a standard one for this kind of essay: thesis, structure, evidence, style and mechanics.
{{essay}}
Return JSON: {"criteria": [{"name": "...", "good": "what full marks look like", "points": 10}]}, one entry per criterion, in the rubric's order.
## @node: score_criterion
Score the essay on this one criterion only:
{{item}}
The essay (with its rubric):
{{essay}}
Return a JSON object: {"criterion": the name, "score": a number within its points, "quotes": two or three short quotes from the essay that justify the score, "fix": the one change that would raise the score most}.
## @node: line_edits
Read this essay and pick the ten sentences that would gain most from an edit: unclear, wordy, or wrong.
{{essay}}
Return a numbered list. For each one quote the sentence, give a better version, and say why in a few words.
## @node: score_check
You are a second marker. You did not write these scores; check them as if a stranger did.
The essay and rubric:
{{essay}}
The scores, one per criterion:
{{score_criterion}}
The line edits:
{{line_edits}}
Check that every quote is really in the essay, that each score fits its quotes and its criterion, and that the line edits do not change the meaning. Correct what is wrong.
Return the checked scores with their quotes and fixes, the line edits worth keeping, and a short list of what you corrected.
## @node: feedback
Write the feedback for the essay's author from this checked review:
{{score_check}}
Return Markdown: the total score and a one-line verdict, a table of criteria with score and the fix for each, the three changes that matter most, then the line edits.
More workflows for a chat
- Research a topic and fact-check itSearch the web, check every claim against its source with fresh eyes, then a short answer with links
- Compare productsShortlist, research each one on the same criteria, check every price and spec, then a pick
- Plan a trip and check the budgetDay-by-day plan with prices, a budget check that sends it back while it is over, then a booking list