Crucible
Less code is more. Burn the diff down. Optimize, simplify, and delete bad vibes.
npx -y skills add ryanelian/crucible-agent-skill --skill crucibleAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 11 days oldThe repository was created 11 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Autonomous, loop-until-clean diff refinement engine. Iteratively burns down feature and refactor diffs by deleting dead code, flattening empty abstractions, and enforcing strict layer boundaries (Sink & Lift). Every change is verified against tests. Use when asked to "crucible", "refine until clean", "burn down diff", "simplify until done", or via /crucible.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
14.0 KB, as published. Nobody here has run it
Crucible
Mantra: Less code is more. Code that does not run cannot slow you down — prefer deletion and simpler paths so the app does less work per request.
Identity: You refine the scoped edit set (feature/refactor diff, or an explicit path) — delete, simplify, flatten. You do not add features, widen product scope, or redesign architecture.
Batching & done when
ACTIVE until done. Prove clean with an empty pass when you can; do not stop after one tidy-up because it “looks fine.”
Path scope: /crucible path/or/area (see Scope — explicit path overrides git).
10 passes per batch. N resets to 1 each batch. One Pass N: line per pass
(see Output). Never skip a number. Never start pass 11 in a batch. Never
auto-continue past the cap.
| Stop | Next |
|---|---|
| Empty pass (any N) | Done. No ask. |
| Pass 10 non-empty | Ask for another 10. |
| User yes | New batch, N = 1, same scope + lists. |
| User no / "stop crucible" / "normal mode" | Done. |
Loop in one turn when tools allow. Passes back-to-back until empty or cap;
no asks between passes. Yield mid-batch only if tools are blocked — then, if the
/loop skill is available, arm a wake (purpose crucible, next N + scope);
otherwise ask the user to continue. Never re-arm after empty pass or declined
batch. Do not invent a loop mechanism if /loop is not installed.
Scope
Not the whole repo. Resolve the edit set as follows:
- Explicit path (wins): If the user names a file or folder
(
/crucible src/services/billing.tsx, “crucible on src/auth”, etc.), that path is the edit set — even when those files are git-clean / unmodified. For a folder, include source files under it (skipnode_modules/, build output,.env, etc.). Do not require a dirty diff. - Else dirty work:
git diff+git diff --staged+ untracked that belong to the change (same junk skips). - Else clean tree, feature committed:
git diff <default-branch>...HEAD(three dots). Resolve<default-branch>frommain,master,trunk,dev, ororigin/HEAD/ the remote default — whichever exists. - Else fallback: files touched this session that belong to the change.
Lock that edit set for the session (plus files crucible changes inside it). Re-read contents each pass. Do not expand beyond it — no drive-by neighbors, no package-wide cleanup outside the named path.
Caller checks are repo-wide (critical): before deleting or narrowing an export, search the whole repo for importers/callers. Zero callers in the edit set is not enough. Classify callers as prod vs test when hunting test-only surface.
Tests for scoped code are in scope when they block a take or assert deleted API.
The pass
N starts at 1; increment by 1 each pass.
- Read scoped files as they are now.
- Hunt only for:
- Dead code — unused exports/helpers/methods with zero callers across the repo
- Test-only production surface — app/prod symbols whose only callers are
tests (or test helpers). Prefer delete the prod API and fix/remove those
tests. Skip / mark ambiguous if it looks like an intentional test seam or
supported test utility (e.g.
testing/export, documented testkit). - branches that duplicate the same return
- single-use wrappers / temporaries that only rename
- Pass-through chains —
A → B → C → Dwhere B/C only forward. PreferAcall the real work (Dor inlined body). Delete empty middle hops only if they have zero other callers repo-wide and add no policy, validation, auth, retries, or observability. Skip / ambiguous if a hop owns real behavior or is a shared boundary. - equivalent shorter stdlib/API already used nearby
- DRY — duplicated logic in the edit set that can collapse without a new framework or premature abstraction
- Mixed concerns — HTTP/SQL/orchestration/input/output jammed into pure logic (or the reverse); sink or lift each concern (Sink & Lift Rule), don’t add empty hops
- Fat interfaces / unused surface — methods clients must depend on but never call → delete or narrow so callers depend on less
- Flag/branch growth — one function growing cases via flags when unused paths can die or the one real path can stand alone
- Take gate-passing candidates one at a time; verify each before the next.
- Verify (critical): lightest check — scoped tests → else typecheck/lint on
touched files → else note skipped. On failure: revert that candidate; do
not patch it; record
skipped [failed verification: <short reason>]; continue other candidates in this pass. - Check then report — before the pass line, confirm every take was verified or explicitly verify-skipped. Emit one line for N (see Output). Then follow Batching & done when.
Gate
Take = YAGNI or KISS that holds all of:
- Less code, or clearly simpler control flow
- No new layer / indirection / framework "for clarity"
- Does not hurt performance (same or better on the hot path)
- Does not hurt security (no weaker auth, validation, or trust boundaries)
- Does not hurt correctness (same behavior, including error/cancel paths)
Skip (do not take):
- Rename / move churn with no behavior or size win
- Layer merges/splits that need a design fight
- Clever micro-opts that add structure or a second path
- Anything that weakens security or can fail open into wrong data
- Migrations, new dependencies, broad API rewrites
- Accessibility basics or anything the user asked to keep
When unsure: don't — tag as ambiguous.
Ambiguous
Unclear takes (not hard Gate skips). Session list:
- Per pass:
skipped [ambiguous: <what> — <why one clause>] - Dedup by subject; refresh why if it changes
- Roll up on stop / cap ask (see Output); not mandatory work
Rules
- KISS (Keep It Stupidly Simple) + YAGNI (You Ain't Going To Need It) — simplest path that works; delete unused surface; no “might need later.”
- DRY (Do Not Repeat Yourself) — one source of truth; collapse copies when that shrinks code without a new abstraction layer.
- SRP (Single Responsibility Principle) — one job per module. Simplify leaky
implementations by separating concerns, not by adding ceremony:
- Pure / unit — deterministic from inputs only (e.g. string/number/date transforms, parsers, pure calculators). No input/output side effects, clocks, or hidden globals — so it can be unit-tested reliably. That’s it.
- Data / repository — only talks to the DB/store. That’s it. Whatever record/class/input is passed into the method parameter is what gets written to (or read as) the table/row — 1:1, no pre-processing, no post-processing in this layer. If pre/post must live here, it needs a very strong reason or an explicit user request; otherwise apply Sink & Lift Rule.
- Service / orchestration — only orders collaborator calls: pure / unit, data / repository, and/or other service / orchestration. Doesn’t own SQL, HTTP parsing, or the internals of those callees.
- API / transport — only validates/parses the request and calls the service; doesn’t own how persistence or pure/unit work runs. Flatten empty forwarders. More files ≠ more solid.
- No oscillation — do not undo a structural change from earlier in this session (including prior batches).
- Deletion over abstraction. Boring over clever. Gate first, aesthetic second.
- User insists on a skipped item → take next pass, no re-arguing.
Sink & Lift Rule
When logic bleeds into the wrong layer (especially data / repository or API / transport), move it by these triggers. Repository does not absorb leaked work by default.
Sink → pure / unit
Trigger: A repository, service, or API/transport contains inline data mapping, text parsing, status calculations, or validation formulas.
Rule: If the work is deterministic (same inputs → same output) and has zero store/HTTP side effects and zero collaborator calls, sink it to a pure / unit helper.
// before — repository formats inline
class UserRepository {
async create(data: UserInput) {
const cleanEmail = data.email.trim().toLowerCase()
return db.users.insert({ ...data, email: cleanEmail })
}
}
// after — pure / unit owns the transform; service applies it; repo stays 1:1
export const normalizeEmail = (email: string) => email.trim().toLowerCase()
class UserService {
async create(data: UserInput) {
return this.users.create({ ...data, email: normalizeEmail(data.email) })
}
}
class UserRepository {
async create(data: UserInput) {
return db.users.insert(data)
}
}
Lift → service / orchestration
Trigger: A repository or API/transport contains multi-step control flow, preconditions across tables/services, or coordinates calls between components.
Rule: If the work sequences actions, decides execution flow, or combines results from multiple sources, lift it into service / orchestration.
// ❌ BEFORE: OrderRepository executes SQL for the 'users' table & enforces business policy
class OrderRepository {
async createOrder(userId: string, item: string) {
// 🛑 LEAK 1: SQL query touching a DIFFERENT table ('users') inside OrderRepository
const userResult = await db.query('SELECT is_blocked FROM users WHERE id = $1', [userId])
// 🛑 LEAK 2: Business policy decision inside database layer
if (userResult.rows[0]?.is_blocked) {
throw new Error("Blocked user cannot place order")
}
// Actual order query
return db.query('INSERT INTO orders (user_id, item) VALUES ($1, $2) RETURNING *', [userId, item])
}
}
// ✅ AFTER: Each repository owns SQL for 1 table; Service lifts the workflow & business rule
class UserRepository {
async findById(userId: string) {
const res = await db.query('SELECT is_blocked FROM users WHERE id = $1', [userId])
return res.rows[0]
}
}
class OrderRepository {
async insert(userId: string, item: string) {
const res = await db.query('INSERT INTO orders (user_id, item) VALUES ($1, $2) RETURNING *', [userId, item])
return res.rows[0]
}
}
class OrderService {
constructor(private userRepo: UserRepository, private orderRepo: OrderRepository) {}
async createOrder(userId: string, item: string) {
const user = await this.userRepo.findById(userId)
if (user?.is_blocked) throw new Error("Blocked user cannot place order") // Business rule lifted here
return this.orderRepo.insert(userId, item)
}
}
Take vs skip
Take (dead code / test-only production surface)
Zero actual callers across the repo. Example report:
Pass 1: took [dead formatId]. skipped [].
Take (flatten forwarders / KISS)
Middle hop that only forwards arguments with zero behavior. Do not make API / transport call data / repository directly — keep SRP layers; delete the empty middle hop only.
// before — OrderGateway only forwards
class OrderGateway {
findById(id: string) { return this.orders.findById(id) }
}
class OrderService {
get(id: string) { return this.gateway.findById(id) }
}
// after — service calls repository; dead pure-forwarder gateway gone
class OrderService {
get(id: string) { return this.orders.findById(id) }
}
Pass 1: took [flatten OrderService→OrderRepository; dead OrderGateway]. skipped [].
Take (Sink & Lift Rule)
Leaked mapping/formatting/orchestration — apply Sink & Lift Rule (complete before/after there). Example report:
Pass 1: took [sink email normalization to pure normalizeEmail]. skipped [].
Skip (do not take)
// skip — forwarder owns behavior (auth, validation, retries, …)
async getUser(id: string) {
await this.auth.checkPermissions(id)
return this.userRepo.findById(id)
}
// skip — intentional testkit / supported test utility
export function createTestDatabase() { /* … */ }
// skip — rename churn with no size/behavior win
const userRecord = await db.get(id) // renaming to `user` alone = aesthetic
Pass 1: took []. skipped [ambiguous: getUser auth check — not a pure forwarder].
Pass 1: took []. skipped [ambiguous: createTestDatabase — supported testkit].
Pass 1: took []. skipped [rename churn].
Pass 1: took [dead formatId]. skipped [failed verification: typecheck — inline fetchUser].
Output
Every pass: exactly one numbered line. No essays. No silent passes.
Pattern: Pass N: took […]. skipped […].
Tag skips when useful: plain gate skip, ambiguous: …, failed verification: ….
After empty / cap ask / decline: emit Leftover ambiguous: […]; […]. if the
list is non-empty (omit if empty). Keep the list across yes-batches.
Pass 1: took [dead formatId]. skipped [rename churn, ambiguous: ErrorCode map — might be public API].
Pass 2: took [inline fetchUser]. skipped [].
Pass 3: nothing worth taking. Crucible done.
Leftover ambiguous: [ErrorCode map — might be public API].
Cap ask form: Pass 10: took […]. skipped […]. Cap reached. Continue for another 10 passes?
Boundaries
Governs deletes/simplifies in the edit set, not how you talk.
If the /ponytail skill is available, use its ladder for take vs skip, merged with Crucible's Sink & Lift Rule (since ponytail does not include Sink & Lift). Crucible retains ownership of the repeat-until-clean loop. No /ponytail? The gate is enough.
If the /loop skill is available, use it only to wake the next pass when the turn must yield mid-batch (see Batching & done when). No /loop? Ask the user to continue — do not fake a wake.
Gives 0 of the 12 instructions most refactoring skills give
Counted across 521 of the 525 authors here whose files we hold, read 2026-08-06
- run tests after each changein 59 of 521, across 56 files
- write tests before refactoringin 27 of 521, across 24 files
- preserve external behaviorin 26 of 521, across 22 files
- remove dead codein 25 of 521, across 24 files
- make small incremental changesin 20 of 521, across 17 files
- break the implementation into tiny commitsin 18 of 521, across 5 files
- ask the user about alternative optionsin 17 of 521, across 4 files
- create a GitHub issue with the planin 17 of 521, across 4 files
- explore the repository to verify assertionsin 17 of 521, across 4 files
- interview the user about the refactorin 16 of 521, across 3 files
- check the codebase for test coveragein 16 of 521, across 3 files
- refactor one thing at a timein 16 of 521, across 12 files
Said here and by no other author read
- Process at most ten passes per batch
- Lock the edit set at session start
- Search the entire repo before deleting exports
- Revert candidates that fail verification
- Move pure logic into a dedicated helper
- Lift control flow to service orchestration
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.