cc-os/plugins/cc-architect/references/tool-patterns/deterministic-scripting.md

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:

  1. Binary correctness? - Is the output valid/invalid rather than good/better/best?
  2. Identical execution? - Would I do this the same way every time?
  3. Structured data? - Does it involve parsing, transforming, or validating structured formats?
  4. 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

  1. Match project context - A Ruby script in a Rails project integrates naturally. A Python script in a Rails project adds cognitive overhead.

  2. 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.

  3. 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.

  4. 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:

  1. Exit codes - 0 for success, non-zero for failure
  2. Structured output - JSON or clear text for parsing
  3. Idempotent - Running twice produces same result
  4. 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