Blastoff Rails
The Repo Is
the Harness
Shipping an AI harness inside a Rails app, so the agent works like a teammate trained on your system, not a random autocomplete.
Demo
The model writes the code.
The harness defines the process.
Reliability, quality, conventions, efficiency (tokenomics), constraints, security, and compatibility don't come from trusting the models, better models, or better prompts. You get them by shipping the harness inside your app, for your whole team to use.
Deterministic non-determinism
- Conventions
- patterns, style, components
- Security
- auth, secrets, access
- Workflows
- testing, worktrees, Q/A
- Artifacts
- plans, docs, templates
- Planning
- epics, sprints, tasks
- Architecture
- solution, scale, tests
- Iteration
- TDD, debugging, refactoring
- Design
- UI, UX, layout
The harness is where you draw the line.
The cheaper, faster, more open the model, the less it knows your conventions, legacy, and constraints.
As models commoditize, the harness is the moat.
Five layers, in the repo
CLAUDE.md / AGENTS.md — the entry contract..claude/skills — thin commands that route the work.docs/conventions — runtime context, not moral support..claude/hooks — the path made executable.bin/ — known workflows the agent shouldn't re-derive.Tell it how to enter the repo
## Critical Rules
1. **MANDATORY: ALWAYS run `/rg:conventions` before writing ANY code**
2. **MANDATORY: ALWAYS read the relevant `docs/` file before ANY feature**
3. **MANDATORY: ALWAYS use `/rg:plan` to plan work**
4. **MANDATORY: ALWAYS use `/rg:work` to begin work**
5. **MANDATORY: NEVER edit, delete, or mutate staging, production, or remote data**
Strong language is load-bearing
<EXTREMELY_IMPORTANT>
BEFORE YOU DO ANYTHING — read this and follow it.
1. *ALWAYS* Run /rg:conventions to load the index
2. *ALWAYS* Read the docs relevant to your task
3. *ALWAYS* Output a "Conventions loaded:" block
4. *NEVER* Write code without reading the docs
5. *NEVER* Skip re-reading because you read it earlier
</EXTREMELY_IMPORTANT>
Thin skills, reference files
---
name: rg:conventions
description: Load project coding conventions before writing code.
---
# Project Conventions
Convention docs live in `docs/conventions/`, by domain.
Read the relevant ones with the Read tool before writing code.
| Doc | When to read |
|-----|--------------|
| docs/conventions/architecture/coding_patterns.md | naming, YAGNI |
| docs/conventions/hotwire/turbo_streams.md | streams, DOM IDs |
| docs/conventions/testing/rspec/overview.md | rules, anti-patterns |
Wrap the skills you want
---
name: rg:plan
description: >-
Plan a feature with automatic convention loading, then hand
off to compound-engineering:ce-plan for structured planning.
---
# Plan With Convention Context
Orchestrate: understand → conventions → docs → plan.
**NEVER auto-invoke a work skill.** This produces a plan only.
Re-read every turn
<EXTREMELY_IMPORTANT>
**ALWAYS re-read convention docs before writing code, even
if you read them earlier.** Context compression means earlier
reads may be gone from working memory.
**ALWAYS output this confirmation before writing any code:**
Conventions loaded:
- docs/conventions/architecture/coding_patterns.md
</EXTREMELY_IMPORTANT>
System doc
---
title: "AI Architecture"
description: "Ai::Client service layer, provider abstraction, error hierarchy, and prompt system"
category: application
applies_to: [ai, llm, ruby-llm, anthropic, openai, dspy]
last_updated: 2026-03-26
---
# AI Architecture
The AI service layer provides a unified interface for LLM interactions.
All AI calls go through `Ai::Client` — never use provider gems directly.
## Quick Start
```ruby
# One-shot question (uses claude-haiku-4-5 by default)
answer = Ai::Client.ask("Summarize this text: #{text}")
```
Reasoning
## Use `static values` for all controller state -- never query DOM on `connect()`
The server renders data attributes; the controller reads them
declaratively. Always declare type and default together for
non-String values.
bad / good
// BAD -- queries DOM for initial state
connect() {
const selected = this.element.querySelector("input[type=radio]:checked")
}
// GOOD -- server provides state via values
static values = { default: String }
connect() {
if (this.hasDefaultValue) this.revealByValue(this.defaultValue)
}
End with a checklist
## Checklist
- [ ] All state in `static values` with typed defaults -- not DOM queries
- [ ] `connect()` paired with `disconnect()` for all timers, listeners, references
- [ ] `data-controller` element wraps all `data-action` elements that reference it
Make commands deterministic
# Block direct rspec — must use bin/agent-rspec
if echo "$COMMAND" | grep -qE 'rspec\s' && ! echo "$COMMAND" | grep -qE 'bin/agent-rspec'; then
echo "BLOCKED: Do not run rspec directly. Use bin/agent-rspec." >&2
exit 2
fi
# Block starting the dev server
if echo "$COMMAND" | grep -qE '(bin/dev|rails\s+s\b|rails\s+server)'; then
echo "BLOCKED: Do not start the dev server." >&2
exit 2
fi
Close the off-ramps
"deny": [
"Skill(brainstorming)",
"Skill(write-plan)",
"Skill(using-git-worktrees)"
],
"allow": [
"Bash(bin/agent-rspec:*)",
"Bash(bin/rails:*)",
"Bash(bin/rubocop:*)"
]
Delegate known workflows
if [[ -f "${GIT_ROOT}/config/master.key" ]]; then
ln -sfn "${GIT_ROOT}/config/master.key" \
"${target}/config/master.key"
ok " ✓ Linked config/master.key"
fi
Is the convention being followed?
And is it even needed?
A convention is a hypothesis until you test it. You can't manage what you don't measure.
How the eval works
Same app, same prompt. The only variable is the harness.
Each bucket is an instruction
It tells you where your determinism layer is working, wasted, or unproven.
The durable lessons
Thank you
Ship the
process
The model isn't the process. The repo is. As the models commoditize, your harness is the moat.