9.1 KiB
Deterministic Scripting Pattern
Purpose
Shift mechanical, repeatable work from AI context to deterministic scripts. Scripts improve consistency, reduce context bloat, and let AI focus on judgment-based work.
This pattern applies during skill design and planning phases. The AI should recognize scripting opportunities and recommend them, rather than asking the user unprompted.
Core Principle
When the AI encounters a task component during planning, it should ask:
"Is this mechanical, repeatable, and unambiguous? If yes, a script will do this more reliably than I can."
Scripts are not replacements for AI judgment—they're complements. Scripts handle the deterministic foundation; AI handles interpretation and decisions built on that foundation.
Quick Heuristics
Self-check during planning:
- Binary correctness? - Is the output valid/invalid rather than good/better/best?
- Identical execution? - Would I do this the same way every time?
- Structured data? - Does it involve parsing, transforming, or validating structured formats?
- No interpretation? - Can correctness be determined without judgment?
If 3+ answers are "yes" → strong script candidate.
Examples
| Task | Script? | Reasoning |
|---|---|---|
| Validate YAML frontmatter | Yes | Binary correctness, structured data |
| Scaffold directory structure | Yes | Deterministic file operations |
| Parse and normalize field values | Yes | Mechanical transformation |
| Check description length limits | Yes | Quantitative constraint |
| Verify naming conventions | Yes | Pattern matching, no judgment |
| Generate boilerplate from template | Yes | Deterministic substitution |
| Evaluate description quality | No | Qualitative judgment required |
| Choose between design approaches | No | Trade-off analysis |
| Assess code readability | No | Subjective evaluation |
| Decide what to include in a skill | No | Requires understanding intent |
Hybrid Tasks
Some tasks appear qualitative but have quantitative components scripts can handle:
| Task | Script Portion | AI Portion |
|---|---|---|
| Review description | Length limits, forbidden characters, required fields | Clarity, specificity, tone |
| Audit skill structure | File existence, naming conventions, size limits | Content quality, completeness |
| Validate workflow | Required sections present, link validity | Logical flow, clarity |
Principle: Extract quantitative guardrails into scripts. Let AI focus on the qualitative judgment that remains.
Language Selection
Choose language based on project context and tool fit, not personal preference.
Principles
-
Match project context - A Ruby script in a Rails project integrates naturally. A Python script in a Rails project adds cognitive overhead.
-
Use best-in-class for domains - Some tools have clear language winners. Playwright is best supported in Python. Data science tasks favor Python. Shell automation favors Bash.
-
Prefer OOP-capable languages - Ruby, Python, JavaScript/TypeScript support clean object-oriented design. Bash does not—use it only for simple orchestration or when it's genuinely the best fit.
-
Consider maintenance - Who will maintain this script? If the project team knows Ruby, write Ruby. If you're building a general-purpose skill, Python has broader reach.
Examples
| Project Context | Recommended | Reasoning |
|---|---|---|
| Rails application | Ruby | Matches project, team knows it |
| Ruby gem | Ruby | Same ecosystem, natural fit |
| General-purpose skill | Python | Broad reach, well-supported |
| Browser automation | Python | Playwright's best support |
| Data transformation | Python | Rich ecosystem (pandas, etc.) |
| Simple file operations | Bash | Lightweight, universal |
| Node.js project | JavaScript/TypeScript | Matches project context |
| Cross-platform CLI tool | Python or Go | Portability matters |
Anti-patterns
- Writing Bash for complex logic (use a real language)
- Choosing Python for a Ruby project because "Python is more popular"
- Using JavaScript for non-JS projects just because you know it
- Mixing languages within a single skill's scripts without good reason
OOP Principles
Scripts should follow object-oriented principles for maintainability and evolution. These principles, drawn from Sandi Metz's teachings, apply to Ruby, Python, and JavaScript alike.
Single Responsibility
Each class/module does one thing. Each method does one thing.
# Good: Single responsibility
class FrontmatterValidator
def validate(content)
# Only validates frontmatter
end
end
class StructureValidator
def validate(path)
# Only validates directory structure
end
end
# Bad: Multiple responsibilities
class SkillValidator
def validate_everything(path)
# Validates frontmatter AND structure AND content AND...
end
end
Dependency Injection
Objects receive their dependencies; they don't create them.
# Good: Dependencies injected
class SkillScaffolder:
def __init__(self, file_system, template_loader):
self.fs = file_system
self.templates = template_loader
# Bad: Dependencies created internally
class SkillScaffolder:
def __init__(self):
self.fs = RealFileSystem() # Hard to test
self.templates = TemplateLoader() # Tightly coupled
Small, Composable Objects
Prefer many small objects over few large ones. Compose behavior through collaboration.
# Good: Small, composable
validator = CompositeValidator.new([
NameValidator.new,
FrontmatterValidator.new,
StructureValidator.new
])
# Bad: Monolithic
validator = MegaValidator.new # 500 lines, does everything
Immutable Data Where Possible
Prefer transformations that return new objects over mutations.
# Good: Returns new object
def with_updated_name(skill, new_name):
return Skill(name=new_name, **skill.other_attrs)
# Bad: Mutates in place
def update_name(skill, new_name):
skill.name = new_name # Side effect
Tell, Don't Ask
Tell objects what to do; don't ask for their data and make decisions externally.
# Good: Tell the object
validator.validate_and_report(skill_path)
# Bad: Ask and decide externally
if validator.has_frontmatter?(skill_path) && validator.frontmatter_valid?(skill_path)
# External decision-making
end
Integration with Workflows
This pattern integrates at two points:
During Brainstorming
When refining a skill concept, the AI should identify script candidates:
"This skill involves validating workflow YAML and scaffolding directories. Both are mechanical tasks—I recommend scripts for consistency. The qualitative review of workflow clarity stays with the AI."
During Planning (new-skill workflow)
Step 2 (Analyze and Plan) should explicitly consider:
- Which components are script candidates?
- What language fits this project?
- What quantitative guardrails can be extracted?
The planning output should list identified scripts before implementation begins.
Model Guidance
| Task | Recommended Model |
|---|---|
| Writing scripts | Haiku (mechanical, clear requirements) |
| Designing script interfaces | Opus (API design is judgment) |
| Reviewing script correctness | Haiku (mechanical verification) |
| Deciding what to script | Opus (requires understanding intent) |
Verification
Two distinct concepts:
Scripts must be verifiable - Scripts themselves should be reliable and testable:
- Exit codes - 0 for success, non-zero for failure
- Structured output - JSON or clear text for parsing
- Idempotent - Running twice produces same result
- Testable - Can be run in isolation with test inputs
Scripts as verification tools - Scripts can serve as verification mechanisms for skills:
python scripts/validate_skill.py path/to/skill
# Exit code 0 = valid, 1 = invalid
# Output describes any issues found
This complements the Verification Pattern—scripts provide deterministic evidence that work is complete and correct.
Anti-patterns
Over-scripting
Not everything needs a script. If a task requires judgment, context, or interpretation, keep it in AI domain.
Signs of over-scripting:
- Script has many special cases and edge case handling
- Script needs to "understand" content, not just parse it
- Script requires frequent updates as requirements evolve
- Script is longer than the AI instructions it replaced
Under-scripting
Repeated mechanical work that stays in AI context wastes tokens and introduces inconsistency.
Signs of under-scripting:
- Same validation logic described in multiple places
- AI makes occasional errors on mechanical tasks
- Structured data processed differently each time
- No verification possible because there's no script to run
Wrong Abstraction Level
Scripts should operate at the right level—not too granular, not too broad.
Too granular: Separate scripts for checking each frontmatter field Too broad: One script that validates, scaffolds, and generates content Right level: One script for frontmatter validation, one for scaffolding, one for structure checks