Muscles
Learned patterns, TL;DR system, heat tiers, writing your own.
Learned patterns in .soma/amps/muscles/ as markdown with frontmatter (type, status, topic, keywords, heat, loads). Loaded by heat within token budget (default: 2000). Hot (≥5) = full body, warm (≥1) = TL;DR only, cold = name listed. Add a ## TL;DR section — it’s what loads 90% of the time. /pin to keep hot, /kill to drop cold.
Muscles are learned patterns — reusable knowledge that Soma builds from experience. Unlike protocols (which are behavioral rules you write), muscles emerge organically from work. They’re Soma’s playbook.
Muscle or protocol?
The short version: a muscle makes an action better. A protocol decides whether the action happens at all.
| Muscle | Protocol | |
|---|---|---|
| Answers | ”how do I do this well?" | "may I do this — and if not, what instead?” |
| Nature | knowledge, method, judgement | a rule, with consequences |
| Arrives | loaded by heat, or summoned by a gate’s read-first | fires at the moment its rule is broken |
| Effect on a tool call | improves it — you write better code because you read it | gates it — remind, block, or redirect to the right tool |
| Prompt cost | resident: hot = full body, warm = TL;DR, cold = free | ~zero until broken (a gates: block loads nothing) |
| Fails by | never being recalled — a reflex you didn’t have | over-firing on innocent work, or its premise going stale |
| Good fit | ”here is the tone the roadmap needs" | "don’t grep -r a tree — use soma:code.find” |
They compose, and that’s the point. A gate can name a muscle as its read-first, so the
protocol supplies the trigger and the muscle supplies the knowledge:
gates:
- paths: ["roadmap.json"]
mode: block
read-first: roadmap-tone-check.md # ← a MUSCLE, not a doc
rule: "The roadmap is not the CHANGELOG. Read the muscle, then run the tone-check script."
That pairing solves the muscle’s oldest problem. A cold muscle costs nothing and is never recalled; a hot one is recalled but costs tokens every turn forever. Behind a gate it costs nothing AND arrives exactly when it applies — no heat required.
Folding a long muscle
If a muscle is long, split it by what each half does:
- the part that is a rule — don’t do X, do Y — becomes a gate’s one-line
rule: - the part that is judgement — examples, tone, tradeoffs — stays in the muscle as the read target
The reader then gets a single corrective line at the moment of the mistake, and only opens the long
version when they actually need it. Don’t move the whole muscle into a gate — a rule: that
runs to a paragraph is a document with extra steps, and it loads on every violation.
How Muscles Form
Muscles start as observations. When Soma notices a pattern across sessions — a deployment process, a code style, an API workflow — it writes it down as a muscle file in .soma/amps/muscles/.
A muscle is a markdown file with frontmatter:
---
type: muscle
status: active
topic: [deployment, vercel, astro]
keywords: [deploy, build, preview, production]
created: 2026-03-09
updated: 2026-03-09
heat: 3
loads: 0
---
# Deployment — Muscle
> Learned patterns for deploying Astro sites to Vercel.
## TL;DR
Build with `pnpm build`, deploy with `npx vercel --prod`.
Always verify with curl after deploy. Check build output for page count changes.
## Full Process
1. Run `pnpm build` and check for errors
2. Verify page count matches expectations
3. Deploy with `npx vercel --prod`
4. Confirm alias URL responds with 200
5. Commit and push
Frontmatter Fields
| Field | Type | Description |
|---|---|---|
type | "muscle" | Always muscle |
status | active | dormant | retired | Controls discovery. Only active muscles load. |
tags | string[] | Searchable tags (used by focus matching) |
topic | string[] | What this muscle covers (broad categories) |
keywords | string[] | Finer search terms for lookup and focus matching |
triggers | string[] | object[] | Focus triggers — keywords that auto-activate this muscle (see Focus) |
tools | string[] | Scripts this muscle relates to |
heat | number | Current heat level — determines loading tier |
loads | number | Legacy boot-load counter (no longer written as of v0.35.x; heat lives in state.json). |
created | date | When the muscle first formed |
updated | date | Last modification |
The Digest System
Muscles can be large — a full logo design muscle might be 200+ lines. Loading all of that into the system prompt wastes context. The TL;DR system solves this with a two-tier approach.
Every muscle should have a ## TL;DR section:
## TL;DR
Concise summary of the key patterns — what the agent needs at a glance.
When to apply. What to watch for. 2-6 lines max.
When a muscle loads as warm (digest tier), only the ## TL;DR content enters the prompt. When it loads as hot, the full body loads.
No TL;DR? The muscle can only load as hot (full) or cold (not at all). Write TL;DRs — they’re what loads 90% of the time.
Note: The
## TL;DRformat is shared across all AMPS (protocols, muscles, automations). Older muscles may use<!-- digest:start/end -->markers — both formats work, but## TL;DRis preferred.soma doctorauto-converts on boot.
Heat & Loading Tiers
Like protocols, muscles use the heat system to decide what loads:
| Tier | Heat | What Loads | When |
|---|---|---|---|
| 🔥 Hot | ≥ fullThreshold (default: 5) | Full body | Heavily used, recent |
| 🟡 Warm | ≥ digestThreshold (default: 1) | TL;DR section only | Occasionally used |
| ❄️ Cold | 0 | Name listed, nothing loaded | Unused, decayed |
Loading respects a token budget (default: 2000 estimated tokens). Hot muscles load first (up to maxFull, default: 2), then warm muscles fill remaining budget (up to maxDigest, default: 8). Cold muscles are listed by name so the agent knows they exist.
All thresholds are configurable in settings.json.
Cold-start boost (the first 48 hours)
A muscle you just wrote loads as a TL;DR digest even if no usage has triggered it yet. The heat system applies a temporary floor for items younger than 48 hours:
effective_heat = max(raw_heat, digestThreshold + 3) # if created < 48h ago
With default thresholds, that puts a brand-new muscle at effective heat 4 — above the digest threshold (loads as warm) but below the full-load threshold. After 48 hours, the boost lifts and your actual usage takes over.
Why: without the boost, a muscle written Tuesday wouldn’t be visible to the agent until a Wednesday correction triggered it — by which point the moment has passed. The boost makes “my just-written context shows up next session” the default.
Caveat — budget overflow: the boost gets you into the warm tier; whether you actually load depends on whether maxDigest (default: 8) hotter muscles already filled the budget. If 8 hotter muscles are loaded, the new one falls to cold (name-only) until usage promotes it. Same for hot → warm: if maxFull (default: 2) is filled by hotter muscles, a new heat-5+ muscle falls to warm digest.
For the full picture (including how to verify a specific muscle’s tier): heat-system.md § New Muscle Visibility.
Writing a Muscle
With soma new (recommended)
Scaffolds the file from a template with frontmatter pre-filled:
soma new muscle my-workflow
soma new muscle my-workflow -d "Run tests before every commit."
soma new muscle my-workflow -t testing,ci,commit-hook
soma new muscle my-workflow --global # write to ~/.soma/ instead of project
soma new muscle my-workflow --no-edit # skip opening $EDITOR
Idempotent — re-running on an existing name opens it in $EDITOR rather
than clobbering. Use --force to overwrite.
The template at templates/default/_muscle-template.md is the single source
of truth for frontmatter shape. If you want to change conventions for new
muscles project-wide, edit that template rather than each muscle file.
Writing manually (lower friction, more ceremony)
You can still write the file by hand if you prefer:
touch .soma/amps/muscles/my-workflow.md
---
type: muscle
status: active
topic: [testing, ci]
keywords: [jest, vitest, test-runner]
heat: 3
loads: 0
---
# Testing Workflow — Muscle
## TL;DR
Run `pnpm test` before every commit. Use vitest for unit tests.
Coverage threshold is 80%. CI runs the same suite.
## Full Process
...your detailed patterns here...
Set heat: 3 or higher so it loads on next boot. Heat will decay naturally if the muscle isn’t used.
Where Muscles Live
.soma/amps/muscles/
├── svg-logo-design.md ← learned from logo sessions
├── deployment.md ← learned from deploy workflows
├── github-app-auth.md ← learned from auth patterns
└── social-preview-gen.md ← learned from OG image work
By default, muscles inherit from parent .soma/ directories when inherit.muscles is true (the default). Parent muscles are discovered alongside project muscles and compete for the same token budget based on heat. To disable, set inherit.muscles: false in settings.json.
Heat Commands
| Command | Effect |
|---|---|
/pin my-muscle | Bumps heat by pinBump (default: +5). Keeps it loaded. |
/kill my-muscle | Drops heat to 0. Won’t load until used again. |
Tips
- Keep muscles focused. One muscle per domain. Don’t mix deployment and testing.
- Write TL;DRs first. The TL;DR is what loads 90% of the time. Make it good.
- Let heat do the work. Don’t manually set heat to 15 on everything. Let usage patterns decide what matters.
- Retire, don’t delete. Set
status: retiredinstead of removing. The knowledge stays searchable. - Update, don’t duplicate. When patterns evolve, update the existing muscle. Don’t create
deployment-v2.md.