Rules, Commands, and Reusable Prompts
Read21 min · Plan → Implement → Verify → Evolve · Updated 2026-09-13
You end this lesson with: Three reusable commands and a rule file, checked into the repository.
Chapters
- Three Artifact Types, Three Different Jobs01
- Rule Granularity and Scoping02
- Writing a Command as a Checklist, Not an Essay03
- Prompt Templates: Parameterized Task Briefs04
- Versioning and Review of the Rule Set05
- How to Test That a Rule Is Actually Being Obeyed06
Lecture 16. Rules, Commands, and Reusable Prompts
Your instruction file has three kinds of entries jammed into one list. One line says "always validate input at API boundaries": that should apply to every task, unprompted, forever. Another is twelve numbered steps for cutting a release, buried in the middle of a paragraph, and last week's release skipped step 7 because nobody scanning that paragraph noticed it was a step at all. A third is a block of text you retype, slightly differently, every time you ask for a security review, this time it asked for "auth flows," last time it asked for "authentication," and the agent treated them as different requests.
None of these three failed because the instruction was wrong. They failed because they were the wrong kind of artifact for the job they were doing. A constraint that should fire on every task was competing for attention with a twelve-step procedure that should only fire when invoked, which was competing with a task brief that should have had named blanks instead of being retyped from memory. Lecture 4 told you to split instructions across files so an entry file stays a router, not an encyclopedia. This lecture tells you what to split them into.
Three Artifact Types, Three Different Jobs
A harness needs exactly three kinds of reusable instruction artifact, and they answer three different questions:
| Type | Answers | Fires | Lives at |
|---|---|---|---|
| Rule | What must always or never be true? | Automatically, whenever its scope is active, unprompted | rules/*.md, scoped by path or topic |
| Command | What are the exact steps for this named procedure? | Only when explicitly invoked | commands/*.md |
| Prompt template | What's the brief for this kind of task, with the blanks filled in? | Only when instantiated with specific parameters | prompts/*.md |
The test that tells you which one you're writing: would a human need to recall this on every single edit (a rule), only when running a specific named procedure (a command), or only when kicking off a specific kind of task (a template)? If the answer is "every edit," it's a rule, and it belongs where the agent reads it without being asked. If the answer is "only when I run X," it's a command, and burying it inside prose guarantees a step gets skipped under pressure, see "step 7" above. If the answer is "only when I'm about to start a task shaped like this," it's a template, and free-form retyping is exactly how "auth flows" and "authentication" end up meaning different things to the agent on different days.
Mixing the three into one undifferentiated list is what lecture 4 called instruction bloat's finishing move: even after you've split content into topic files, a topic file that mixes an always-on constraint with a twelve-step checklist with a task brief still fails, because the three items need different triggers and none of them are getting the trigger they need.
Rule Granularity and Scoping
A rule that's too broad competes for attention with rules that don't apply to the current task, the same "lost in the middle" cost lecture 4 already covered for entry files. A rule that's too narrow is a rule nobody remembers exists, because it never fires often enough to matter. The right grain sits between those two failures.
One rule per failure mode. Each rule should trace back to a specific incident it exists to prevent, the same discipline Evolve uses to generate rules in the first place. "Use good error handling" is not a rule; it's a mood. "Every API boundary must validate input with the project's schema library before touching it, not after" is a rule, because you can point to the exact bug it prevents and check a diff against it directly.
Path-scoped rules. A rule about SQL migrations doesn't need to load for a task that touches only the frontend. Scope rule files to the paths they govern — db/migrations/RULES.md for anything under db/migrations/, a frontend/RULES.md for anything under frontend/ — so the agent's context budget is only spent on rules relevant to the files it's actually touching. Real tools support this natively: Claude Code loads nested CLAUDE.md files by directory; Cursor's .cursorrules can be scoped with glob patterns. Whatever tool you're on, the principle is the same: a rule's scope should match the blast radius of the failure it prevents, not the whole repository by default.
Naming. Name a rule file after the failure mode it prevents, not the general topic it lives under. no-raw-sql-string-interpolation.md tells you, from the filename alone, what incident this rule traces back to and what a violation looks like. database-notes.md tells you nothing: it could be one rule or twelve, and nobody auditing the rule set later can tell which line came from which incident without opening the file and reading closely.
Writing a Command as a Checklist, Not an Essay
A command is invoked under exactly the conditions where steps get skipped: time pressure, context already half-full from the task that led to needing the command, an agent (or a human) that's confident it remembers the procedure and doesn't need to actually read it carefully. Prose paragraphs get skimmed under those conditions. A numbered checklist with an explicit pass/fail condition on each step does not get skimmed the same way: there's no sentence to skip past, only a box that is or isn't checked.
Contrast the two shapes for the same procedure:
Essay style (bad): "Before deploying, make sure you've run the test suite and checked that the build works, and also don't forget to update the changelog and check that there aren't any pending migrations that need to run first, and once you're confident everything's good you can push to the release branch."
Checklist style (good):
1. Run `bun test` — all green, or stop.
2. Run `bun run build` — exits 0, or stop.
3. Check `bun run migrate:status` — no pending migrations, or stop and resolve them first.
4. Update `CHANGELOG.md` with this release's entries.
5. Push to `release` branch only after 1-4 all pass.
The essay version buried the migration check in the middle of a sentence about the changelog: exactly the shape of the "step 7" failure from this lecture's opening. The checklist version makes each step a gate: fail one, stop, don't proceed to the next. That's the same gating discipline PIVE uses between phases, applied inside a single procedure instead of across four.
Prompt Templates: Parameterized Task Briefs
A prompt template is neither a standing rule nor an invoked procedure. It's a fill-in-the-blank brief for a kind of task you start often enough that retyping it from memory each time introduces drift. A bug-fix brief, a new-endpoint brief, a security-review brief: each one has a fixed shape (what's being asked, what context to gather, what "done" looks like) and a small set of parameters that change per instance.
Two things make a template worth having instead of just remembering the shape:
- Named parameters, not free text.
{{component}},{{failure_description}},{{repro_steps}}: filled in the same way every time, instead of retyped in whatever words come to mind that day. "Auth flows" and "authentication" stop being two different requests once they're both just the value of{{scope}}. - An explicit definition of done baked into the template. Every instance of the template should carry the same verification bar, so "done" doesn't get renegotiated per instance depending on how the brief happened to be phrased that day.
A template is versioned like code, not typed fresh each time, specifically because it gets instantiated many times downstream: a bad edit to a template compounds across every task that uses it afterward, where a bad edit to a one-off chat message only affects that one message.
Versioning and Review of the Rule Set
Treat rules/, commands/, and prompts/ the same way you treat source code: changes go through a diff, and the diff gets reviewed before it merges. A rule added by silently appending a line to a file, with no review, is exactly the mechanism lecture 4 identified as the root cause of instruction bloat: "add a rule" felt free in the moment and the file only ever grew.
Three habits keep the rule set from decaying the same way:
- Every new rule cites the incident it came from. If you can't name the failure it prevents, it isn't ready to be a rule yet: write down the incident first, generalize into a rule second.
- Review deletions as seriously as additions. A rule that hasn't fired in months, or that a refactor made moot, is now a competing-for-attention cost with no offsetting benefit. Delete it in a reviewed diff, don't just leave it because deleting feels riskier than leaving it alone.
- Audit on a schedule, not just when something breaks. A quarterly pass through
rules/(does each entry still trace to a real incident, is the scope still correct, does it contradict anything added since) catches decay before it becomes a 600-line file again.
How to Test That a Rule Is Actually Being Obeyed
A rule is invisible until it's violated, which means you can ship a rule that looks fine on paper and never find out it doesn't actually change behavior. Test it the same way you'd test code:
- Write a task that sits squarely inside the rule's failure mode: for a "no raw SQL string interpolation" rule, that's a task that asks for a new query built from user input.
- Run the task with the rule in place and check the diff, not the agent's summary of the diff. Did it actually use parameterized queries, or did it interpolate a string and describe that as "using the database safely"?
- Keep a short, standing list of these test tasks, one or two per rule that matters most, and re-run them whenever a neighboring rule changes or the rule file itself gets edited, a rule that passed its test six months ago can silently stop firing if a later edit narrowed its scope by accident.
- Fold the check into Verify, where it's cheap to automate. A rule like "no raw SQL string interpolation" can often be checked directly with a grep over the diff for the pattern it forbids, run as part of the same Verify step that already runs tests. A rule you can check mechanically is worth automating; a rule you can only check by careful reading is worth keeping on the standing test-task list instead.
The common failure here isn't "the rule was wrong." It's "the rule was never actually exercised after it was written, so nobody noticed it had stopped working." Testing a rule once, at the moment you write it, and never again, gives you exactly one data point about a file that's supposed to hold for the life of the project.
A Full Directory Layout
.agent/
rules/
no-raw-sql-string-interpolation.md
validate-input-at-api-boundaries.md
commands/
add-endpoint.md
security-review.md
release-checklist.md
prompts/
bug-fix-brief.md
Real tools use their own equivalents of the same three-way split (Claude Code's .claude/rules/ and .claude/commands/, Cursor's .cursor/rules/), but the shape is the same regardless of which tool reads it: rules that fire unprompted, commands that fire on invocation, templates that fire on instantiation, each in their own directory so the type of an artifact is visible from its path alone.
Key Takeaways
- Rules, commands, and prompt templates answer three different questions and fire under three different conditions. Putting all three in one undifferentiated list means none of them gets the trigger it needs.
- Scope rules to the paths and failure modes they govern. A rule that fires everywhere competes for attention with rules that don't apply to the current task.
- One rule per failure mode, named after the incident it prevents (not the general topic) so it stays traceable and auditable.
- Write commands as numbered checklists with pass/fail gates per step, not prose. Prose gets skimmed exactly when a command is invoked under pressure.
- Prompt templates need named parameters and a baked-in definition of done, or "the same kind of task" quietly drifts into several different requests depending on how it was phrased that day.
- Version the rule set like code: every addition cites an incident, deletions get reviewed as seriously as additions, and the whole set gets audited on a schedule.
- A rule is invisible until violated. Test it against a task built to sit inside its failure mode, and re-test after neighboring rules change. Don't assume a rule that was correct on day one is still firing on day two hundred.
What You Have Now
Three reusable commands and a rule file, checked into the repository.
Save the rule as .agent/rules/no-raw-sql-string-interpolation.md, and the commands as .agent/commands/add-endpoint.md, .agent/commands/security-review.md, and .agent/commands/release-checklist.md; the lab's checker looks for all four at those paths.
Example Files
Copy-ready course examples referenced in this lecture. Together these cover the deliverable: one rule file and three commands, plus a prompt template for reference.
rules/no-raw-sql-string-interpolation.md
The rule file: traces to a specific incident, states the constraint, and gives the check a Verify step can automate.
# Rule: No Raw SQL String Interpolation
## Incident
2026-03-14: an agent built a search query by interpolating a user-supplied
term directly into a SQL string. It worked in testing and was a SQL
injection vector in review. Caught before merge, not after.
## Scope
`**/*queries*.py`, `**/*repository*.py`, any file executing raw SQL.
## Rule
Every SQL statement built from external input must use parameterized
queries (`cursor.execute(query, params)`), never Python string
interpolation or f-strings, into the SQL text itself.
## Check
`grep -rnE 'execute\(f"|execute\(".*%s.*"\s*%' src/ --include=*.py`
Any match is a violation — fold this into the Verify step for any task
that touches a queries/ or repository/ file.
## Expiry
Revisit if the project migrates to an ORM that makes raw SQL impossible to
write (in which case this rule is no longer reachable and can be deleted).
commands/add-endpoint.md
The first of three commands: a checklist, not an essay, for the recurring "add an API endpoint" procedure.
# Command: Add Endpoint
1. Confirm the route, method, and auth requirement are decided — if not,
stop and write a plan file first (see the PIVE checklist).
2. Add the route handler in the appropriate module.
3. Add input validation at the boundary using the project's schema
library — never trust the request body directly.
4. Add or update tests covering the success path, the auth-denied path,
and at least one invalid-input case.
5. Run `bun test` — all green, or stop and fix before continuing.
6. Run `bun run typecheck` — exits 0, or stop and fix before continuing.
7. Update the API reference doc for this route.
commands/security-review.md
The second command: same checklist discipline, different procedure.
# Command: Security Review
1. List every new or changed file that touches auth, user input, or
external data in this diff.
2. For each file, check: is input validated at the boundary, not deep
inside business logic?
3. For each file touching SQL: run the check from
`rules/no-raw-sql-string-interpolation.md` against it directly.
4. For each new endpoint: confirm the auth requirement matches the
plan's stated requirement — not what "seems right."
5. Check secrets: confirm no credential, token, or key appears in the
diff itself (`grep -rnE '(api[_-]?key|secret|token)\s*=\s*"' <diff>`).
6. Report each finding as `file:line — issue — fix`, not as a prose
paragraph. Findings without a file:line are not actionable.
commands/release-checklist.md
The third command: the checklist version of the essay-style example earlier in this lecture.
# Command: Release Checklist
1. Run `bun test` — all green, or stop.
2. Run `bun run build` — exits 0, or stop.
3. Check `bun run migrate:status` — no pending migrations, or stop and
resolve them first.
4. Update `CHANGELOG.md` with this release's entries.
5. Push to `release` branch only after 1-4 all pass.
prompts/bug-fix-brief.md
A parameterized prompt template, named blanks instead of a paragraph retyped from memory each time.
# Template: Bug Fix Brief
Fill in every field below before starting. Do not begin implementation
with a blank left unfilled.
- **Component:** {{component}}
- **Observed behavior:** {{observed_behavior}}
- **Expected behavior:** {{expected_behavior}}
- **Repro steps:** {{repro_steps}}
- **Scope:** fix only the described defect. Do not refactor adjacent code
in the same change.
- **Definition of done:**
- A regression test exists that fails before the fix and passes after.
- `bun test` passes in full, not just the new test.
- The fix is scoped to {{component}} unless the plan explicitly states
the defect requires a change elsewhere.
Exercises
-
Sort your current instruction file into three piles. Go through every entry in your entry file and any topic docs it links to. For each one, decide: rule, command, or prompt template, using the "when would a human need to recall this" test from this lecture. Move each into the matching directory.
-
Rewrite one essay-style command as a checklist. Find a procedure in your repository currently written as prose (a deploy process, a review process, an onboarding step) and rewrite it as a numbered checklist with an explicit pass/fail condition on each step.
-
Test one rule against a task built to violate it. Pick your most important existing rule, write a task that sits squarely inside its failure mode, run it, and check the actual diff (not the agent's summary) against the rule. If it fails, fix the rule's wording or scope, not just the one output.
-
Write one prompt template for a task shape you start often. Identify a kind of task you kick off at least weekly in roughly the same shape (a bug fix, a new component, a data migration) and turn its usual phrasing into a template with named parameters and a baked-in definition of done. Use it for the next three instances of that task and check whether "done" stayed consistent across all three.
Further Reading
- OpenAI: Harness Engineering: Leveraging Codex in an Agent-First World
- Anthropic: Effective Harnesses for Long-Running Agents
- HumanLayer: Skill Issue: Harness Engineering for Coding Agents
Check your understanding
Reading is free — your progress isn't being saved
Nothing you did on this page is recorded. Paid students get saved progress and verified completions, recorded quiz answers, the spaced-repetition review queue, labs, and a certificate for each course they finish.
30-day money-back guarantee. One-time payment — lifetime access and updates.