Compare commits

..

10 Commits

Author SHA1 Message Date
Kevin Veen-Birkenbach
cd32841847 feat(refactor): require a test net and SRP, SPOT, KISS, DRY
Some checks failed
🧪 Test / 🧪 Lock + lint (push) Has been cancelled
🔄 Update / 🧠 Update skills-lock.json (push) Has been cancelled
Before editing, the skill now establishes a safety net: run existing
coverage for a green baseline, or write characterization tests against
the current behaviour where the code is testable logic. Those tests must
still pass unchanged afterwards, so a test that had to be edited means
the refactor moved behaviour and gets reverted.

The refactor itself is now governed by SRP, SPOT, KISS and DRY, and no
file containing program code may exceed 250 lines. Description and
markup languages are exempt, as is a file the language or framework
genuinely forbids splitting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 18:35:04 +02:00
Kevin Veen-Birkenbach
1bc7711f38 feat(refactor): add skill for cleaning up the uncommitted change set
The skill refactors every file that is currently uncommitted in the git
working tree up to the project's coding standards, without changing
behaviour and without widening the scope to untouched files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 18:35:04 +02:00
Kevin Veen-Birkenbach
27aaabf305 feat(autotune): add skill for generating and tuning skills
The skill generates new skills and optimizes existing ones for token
usage, error rate, focus and persisted learnings. Every decision is
confirmed through single-select (radio button) questions, and the skill
only runs on an explicit operator trigger.

A UserPromptSubmit hook reminds the operator at most once an hour that
/autotune exists; the reminder is a hint for the operator, never a
trigger for the agent. The install-time settings patch registers it
alongside the caveman and ponytail plugins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 18:35:03 +02:00
Kevin Veen-Birkenbach
a42d6b76f4 Merge branch 'master' of github.com:kevinveenbirkenbach/skills
Some checks failed
🧪 Test / 🧪 Lock + lint (push) Has been cancelled
2026-07-25 18:15:01 +02:00
Kevin Veen-Birkenbach
9ac5444628 feat(skills): add no-defaults skill
Forbids substantive defaults for parameters, settings and env lookups
across languages; only false, null and "" are free. A missing value
must fail loudly at the earliest point instead of silently becoming a
plausible wrong answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 18:14:31 +02:00
Kevin Veen-Birkenbach
94031f5a66 feat(observe): watch a target and triage new failures on a loop
Add the observe skill: /observe <target> drives a dynamic /loop that polls a named CI run, pipeline, build or log, diffs the failures against a session baseline, and applies triage (hence dialectic per job) to newly appeared failures only. Chains onto the existing triage and dialectic skills; hardened against re-triaging fixed failures and against idle-polling harness-tracked work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 09:11:02 +02:00
Kevin Veen-Birkenbach
55f9935c63 feat(skills): add active-listening, dialectic, triage, and robot
- active-listening: gather every requirement before acting; ask until nothing
  needed is open, then reflect back.
- dialectic: thesis / adversarial antithesis / synthesis looped to a ~99%
  thesis, deep-inspecting every accessible source; calls active-listening.
- triage: drive a failing pipeline to green by applying dialectic to every
  failing job independently.
- robot: autonomous-execution contract - no questions once the goal is clear,
  never stop before it is reached, only cleared (non-denied/non-ask) commands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 16:16:47 +02:00
Kevin Veen-Birkenbach
91b9845c1b feat(skills): add commit workflow, explain and help-skills
comments-clean purges explanatory comments before every operator-requested stage or commit and penalizes own violations into persistent memory; commit-no-verify commits the exact staging index with hooks skipped, the invocation itself being the per-commit authorization; commit-next chains both and stages the next logical bundle with an explain and confidence briefing; explain walks code and its collaborators in execution order and offers a Mermaid schema in the affected README; help-skills lists every project, global and plugin skill with triggers and composition.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 16:16:47 +02:00
Kevin Veen-Birkenbach
30edba08bc feat(confidence): report calibrated confidence on every stage and fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:36:30 +02:00
Kevin Veen-Birkenbach
3d754c6e0e feat(autoskill): propose a skill when the operator repeats a pattern
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:36:30 +02:00
19 changed files with 857 additions and 5 deletions

View File

@@ -26,7 +26,7 @@ Copy the skills into a specific project (`<repo>/.agents/skills` and `<repo>/.cl
make project TARGET=/path/to/repo make project TARGET=/path/to/repo
``` ```
Both commands also enable the caveman and ponytail plugins in the target's `.claude/settings.json` so their modes auto-activate on session start; existing settings are preserved. Both commands also patch the target's `.claude/settings.json`: the caveman and ponytail plugins are enabled so their modes auto-activate on session start, and the `autotune` reminder hook is registered so the agent points you at `/autotune` at most once an hour. Existing settings are preserved.
Restart your agent afterwards so it loads the new skills. Restart your agent afterwards so it loads the new skills.

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
// Enable the caveman and ponytail plugins in a Claude Code settings.json so // Enable the caveman and ponytail plugins in a Claude Code settings.json so
// their SessionStart hooks auto-activate both modes. Merges non-destructively: // their SessionStart hooks auto-activate both modes, and register the autotune
// existing marketplaces and enabled plugins are preserved, unparseable files // reminder hook. Merges non-destructively: existing marketplaces, plugins and
// are left untouched. // hooks are preserved, unparseable files are left untouched.
"use strict"; "use strict";
const fs = require("fs"); const fs = require("fs");
const path = require("path");
const settingsPath = process.argv[2]; const settingsPath = process.argv[2];
if (!settingsPath) { if (!settingsPath) {
@@ -38,5 +39,15 @@ for (const [name, entry] of Object.entries(MARKETPLACES)) {
} }
data.enabledPlugins = Object.assign(data.enabledPlugins || {}, PLUGINS); data.enabledPlugins = Object.assign(data.enabledPlugins || {}, PLUGINS);
const reminder = path.join(path.dirname(settingsPath), "skills", "autotune", "hooks", "reminder.sh");
data.hooks = data.hooks || {};
data.hooks.UserPromptSubmit = data.hooks.UserPromptSubmit || [];
const registered = JSON.stringify(data.hooks.UserPromptSubmit).includes("autotune/hooks/reminder.sh");
if (!registered) {
data.hooks.UserPromptSubmit.push({
hooks: [{ type: "command", command: `bash ${JSON.stringify(reminder)}` }],
});
}
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n"); fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
console.log(`skills: enabled caveman + ponytail plugins in ${settingsPath}`); console.log(`skills: enabled caveman + ponytail plugins and the autotune reminder in ${settingsPath}`);

View File

@@ -0,0 +1,38 @@
---
name: active-listening
description: >
Gather every requirement before acting. Trigger at the start of any task
whose scope, constraints, or success criteria are not fully pinned down, and
whenever another skill needs an operator-only fact — ask focused questions
until nothing needed is still open, then reflect the understanding back.
Portable across projects.
---
Do not act on a half-understood request. Listen actively until you could state
the task, its constraints, and its definition of done without guessing.
## Procedure
1. **Surface the unknowns.** List every open question the task leaves: ambiguous
scope, unstated constraints, success criteria, edge cases, which files or
systems are in play, what "done" means, and any decision only the operator
can make.
2. **Derive what you can first.** Remove from that list every question you can
answer yourself from the code, the logs, the git history, or a sensible
default. Active listening is not offloading your own investigation onto the
operator.
3. **Ask, focused.** Put the genuinely-open questions to the operator — grouped,
concrete, answerable, with a recommended default where one exists. Prefer a
few high-leverage questions over a long interrogation. Use the host's
structured question mechanism when one exists.
4. **Listen and integrate.** Fold each answer in. A new answer often opens a new
question — keep going.
5. **Loop until closed.** Repeat until no needed information is still open.
6. **Reflect back.** Restate the task, its constraints, and its definition of
done in your own words for a final confirmation before acting.
## Rules
- Never invent an answer to an open question to avoid asking.
- Never ask what you can determine yourself.
- One genuinely-blocking unknown is enough reason to ask; do not proceed past it.

30
skills/autoskill/SKILL.md Normal file
View File

@@ -0,0 +1,30 @@
---
name: autoskill
description: >
Detect operator repetition and turn it into a skill. Trigger when the
operator gives an instruction, correction, or prompt pattern for roughly
the third time in a conversation (or says they keep repeating themselves),
and the pattern is general enough to reuse. Portable across projects.
---
When you notice the operator repeating the same kind of instruction,
correction, or prompt pattern (about three occurrences, exact wording may
vary), do the following:
1. Finish the current task first; never interrupt work in progress.
2. Then propose, in one short paragraph: the repeated pattern you observed,
a skill name suggestion, the trigger phrasing, and what the skill would
do. Ask whether to create it.
3. On approval, write the skill:
- a conversation shortcut or expansion: add it to the `shortcuts` skill
table instead of creating a new skill.
- project-specific behavior: create it in the project's own skills
directory (e.g. `skills/<name>/SKILL.md` in the repository).
- portable behavior: create `skills/<name>/SKILL.md` in the operator's
skills repository.
Follow the local naming conventions and keep the skill a thin,
single-purpose instruction; route to an authoritative doc when one
exists instead of duplicating it.
4. Do not propose a skill for one-off work, secrets, or anything whose
repetition is already covered by an existing skill; when an existing
skill almost fits, propose extending it instead.

92
skills/autotune/SKILL.md Normal file
View File

@@ -0,0 +1,92 @@
---
name: autotune
description: >
Generate new agent skills and optimize existing ones so the agent works more
efficiently: fewer input and output tokens, fewer errors, more focus, and
learnings that persist across sessions. Every proposal is confirmed with the
operator through single-select (radio button) questions. Trigger ONLY on an
explicit operator request (/autotune, "autotune", "tune the skills"); never
run it on your own initiative. Portable across projects.
---
Autotune turns observed friction into durable skills. It runs on demand only,
it changes nothing without an explicit answer from the operator, and every
skill it writes or rewrites lands in the operator's skills repository.
## Trigger discipline
- Run **only** when the operator asks: `/autotune`, "autotune", "tune the
skills", or an equivalent instruction.
- The 60-minute reminder hook is a hint for the operator, not a trigger. When it
fires, print the hint and continue the operator's actual request.
- Never propose, write, or edit a skill as a side effect of unrelated work.
## Step 1: collect evidence
Before asking anything, gather concrete friction from the current session and,
where readable, from the project's memory and agent instruction files:
- **Token waste**: repeated file re-reads, whole-file reads where a range was
enough, long tool output pasted back, re-running a command instead of
grepping a saved log, verbose report prose.
- **Errors**: rejected tool calls, permission prompts, retried commands,
corrections the operator had to give twice.
- **Focus loss**: work that drifted beyond the request, unrequested tests or
deploys, questions that stalled an autonomous run.
- **Lost learnings**: facts re-derived this session that a memory file or a
skill should already have carried.
List each finding as one line: `symptom -> cost -> candidate fix`. Skip
anything that happened once and is not a pattern.
## Step 2: ask, do not decide
Use `AskUserQuestion` with `multiSelect: false` so every question renders as
radio buttons. Never assume an answer, never batch several decisions into one
option, and never write a file before the answers are in.
Ask in this order, at most four questions per call:
1. **Scope** - "What should autotune do this run?" Options: `Create a new
skill`, `Optimize an existing skill`, `Both`, plus the ranked findings if
more than one candidate exists.
2. **Target** - which finding becomes a skill, or which existing skill gets
optimized. One option per candidate, each with its measured cost in the
description.
3. **Shape** - for a new skill: `Standalone skill`, `Row in the shortcuts
skill`, `Extend an existing skill`, `Memory entry instead of a skill`.
For an optimization: `Tighten wording only`, `Change the trigger`, `Add
rules`, `Split into two skills`, `Merge into another skill`.
4. **Placement** - `Operator skills repository (portable)` or
`This project's skills directory (project-specific)`.
Present the drafted skill text (name, trigger phrasing, body outline) and ask
for a final `Write it` / `Revise it` / `Discard` confirmation before touching
disk. Rewrites of an existing skill additionally show a before/after diff of
the changed lines in that confirmation.
## Step 3: write
- Portable skills go to `skills/<name>/SKILL.md` in the operator's skills
repository; project-specific ones to that project's skills directory.
- Frontmatter carries `name` and a `description` that states what the skill
does **and** when to trigger it - the description is the only part loaded into
every session, so it decides whether the skill ever fires.
- Body is thin and single-purpose: the smallest instruction that changes
behaviour. Route to an authoritative doc instead of duplicating it.
- Optimizing an existing skill means the file gets shorter or sharper, not
longer. Delete rules the agent already follows, merge duplicated ones, and
cut every sentence that does not change an action.
- Report what changed in at most three lines, then refresh the reminder stamp
so the next hint is an hour out: `date +%s > ~/.claude/.autotune-stamp`.
## Rules
- One skill per run unless the operator picked `Both`; a broad rewrite of the
whole collection is not what autotune is for.
- Never write a skill for one-off work, for secrets, or for behaviour an
existing skill already covers - propose extending that skill instead.
- A skill that saves tokens but loses correctness is a regression; validation,
error handling, and security steps are never the thing that gets trimmed.
- Efficiency claims stay honest: name the observed friction the skill removes,
never a guessed percentage.

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# UserPromptSubmit hook: emit the autotune hint at most once per interval.
# Stdout lands in the agent's context, which is why the payload tells the agent
# to show the hint and keep going instead of running autotune by itself.
set -euo pipefail
interval="${AUTOTUNE_INTERVAL_SECONDS:-3600}"
stamp="${AUTOTUNE_STAMP:-${HOME}/.claude/.autotune-stamp}"
now="$(date +%s)"
last=0
if [[ -f "${stamp}" ]]; then
read -r last <"${stamp}" || true
[[ "${last}" =~ ^[0-9]+$ ]] || last=0
fi
if ((now - last < interval)); then
exit 0
fi
mkdir -p "$(dirname "${stamp}")"
printf '%s\n' "${now}" >"${stamp}"
cat <<'HINT'
AUTOTUNE HINT: over an hour since the last hint. Show the operator this notice
verbatim as a short block, then continue with their request. Do NOT run
autotune, do not propose skills, do not ask questions about it.
💡 /autotune - generate a new skill or optimize an existing one
(fewer tokens, fewer errors, more focus, learnings that persist).
Runs only when you trigger it.
HINT

View File

@@ -0,0 +1,33 @@
---
name: comments-clean
description: >
Purge explanatory comments from staged and unstaged changes. Run
AUTOMATICALLY, without being asked, every time the operator requests a
commit or stage in any wording (commit, commit no-verify, stage next,
socon, sobu, socobu, "commite ..."), BEFORE executing that request; also
on /comments-clean or when the operator complains about comments.
Explanatory comments are bad practice: they narrate code instead of
letting it speak. Portable across projects.
---
When the operator asks to commit or stage, run this audit first and only
then perform the requested git action; never ask whether to run it.
Audit every staged and unstaged change (`git diff` plus `git diff --cached`,
added lines only) and delete every explanatory comment:
1. Delete: restated code, step narration, section banners, "Note that",
rationale prose, apology comments, and any comment whose information the
code, task name, or commit message already carries. When in doubt,
delete.
2. Keep only: parameter/exit-code docs, nocheck/noqa/shellcheck directives,
and project-sanctioned exception markers that name a concrete trip-wire.
A relabeled narration is not an exception; delete it instead.
3. Re-run the project's comment lint if one exists and re-stage the files
that were already staged.
4. Penalty: if this audit finds anything you wrote yourself, admit it in
the report (file and count, no excuses), lower your confidence numbers
for the affected bundle, and write the incident to persistent agent
memory (extend the existing minimal-comments feedback entry with the
date and the pattern you slipped on) so the calibration survives the
session.

View File

@@ -0,0 +1,20 @@
---
name: commit-next
description: >
Commit the staged bundle with --no-verify, then stage the next logical
bundle and brief the operator on it. Trigger on /commit-next or when the
operator asks to commit and stage the next bundle in one step. Portable
across projects.
---
1. Run the commit-no-verify skill on the currently staged changes
(comments-clean audit first, then `git commit --no-verify`, exact
staging index only).
2. Group the remaining uncommitted changes into logical bundles (one
concern per bundle: same failure class, same mechanism, or same role)
and stage the next one; never mix concerns to save a commit.
3. For the newly staged bundle run the explain skill (what the changes do
and how the pieces interact, file:line-anchored) followed by the
confidence skill (fix confidence, break risk, evidence, residuals).
4. Close with what remains unstaged and wait for the operator's commit
decision; never auto-commit the newly staged bundle.

View File

@@ -0,0 +1,15 @@
---
name: commit-no-verify
description: >
Commit the currently staged changes with --no-verify (skip the pre-commit
hooks). Trigger on /commit-no-verify or when the operator asks for a
no-verify commit in any wording. Invoking this skill IS the explicit
per-commit authorization that --no-verify requires; it never carries over
to later commits. Portable across projects.
---
1. Run the comments-clean audit over the staged and unstaged changes first.
2. Commit exactly the staging index with `git commit --no-verify` and a
Conventional-Commits message derived from the staged diff; never add
unstaged files, never push.
3. Report the created commit hash and what remains uncommitted.

View File

@@ -0,0 +1,29 @@
---
name: confidence
description: >
Report calibrated confidence unprompted. Trigger whenever you stage
changes, declare a fix or bundle ready, or summarize completed work the
operator may commit or deploy — the operator must never have to ask "how
sure are you?". Portable across projects.
---
Every stage summary, fix report, or ready-to-commit bundle ends with a
confidence block:
1. **Fix confidence** (percent): probability the change does what it
claims. **Break risk** (percent): probability it breaks anything else.
Two separate numbers, never one blended figure.
2. **Evidence**: the strongest proof lines only — what was executed,
reproduced, enumerated, or read from authoritative source. Distinguish
proven (ran it, saw it) from derived (reasoned statically).
3. **Residuals**: name exactly what keeps the number below 100 and what
would close the gap (live deploy, missing artifact, unexercised path).
Never a bare percentage without its residuals.
Calibration rules:
- Number the claim you verified, not the effort you spent; discount after
any error the operator caught in the same area.
- Distinguish "fixes the observed failure" from "makes the job green" when
hidden layers may follow, and give both numbers.
- Static analysis alone caps at the low nineties; only executed evidence
(test, reproduction, live probe) justifies more.

52
skills/dialectic/SKILL.md Normal file
View File

@@ -0,0 +1,52 @@
---
name: dialectic
description: >
Reach a defensible conclusion through a dialectical process — thesis,
adversarial antithesis, synthesis — iterated until one thesis survives every
attack at ~99% confidence. Trigger for any consequential root-cause, design,
or decision where being wrong is expensive and the answer is not yet certain.
Deep-inspects every accessible source. Portable across projects.
---
Reach the truth by dialectic, not by first guess. Do not stop at a plausible
answer; drive the loop until a thesis survives every attack at ~99% confidence.
## Before you start: listen
Invoke the `active-listening` skill first. Do not begin the dialectic until you
have gathered every requirement, constraint, and success criterion the task
needs. A dialectic run on the wrong question wastes the whole loop. Return to
`active-listening` mid-loop whenever an objection turns on a fact only the
operator holds.
## The loop
1. **Thesis.** Form the strongest initial claim you can, grounded in evidence,
not assumption. Deep-inspect every source you can reach: the source code and
git history; the running containers (exec, logs, inspect through whatever
make or CLI helpers the project exposes); CI logs and artifacts; tests; docs.
Cite `file:line` and command output — never a bare assertion.
2. **Antithesis.** Spawn MULTIPLE independent skeptics (parallel subagents or a
workflow) whose only job is to REFUTE the thesis. Each re-inspects the
sources itself and defaults to "refuted" on any concrete hole. Give diverse
skeptics diverse lenses: correctness, an alternate mechanism, does-it-
reproduce, does-the-fix-break-something-else.
3. **Synthesis.** Fold every surviving objection back into a refined thesis. A
skeptic that found a real hole replaces or corrects the thesis; a skeptic
that failed to refute raises confidence.
4. **Iterate.** Repeat thesis → antithesis → synthesis until a full skeptic
round finds no new hole and the thesis holds at ~99%. If confidence stalls
below 99%, name the exact residual and the evidence that would close it, then
go get that evidence (more inspection, or `active-listening` for an
operator-only fact) and loop again.
## Rules
- Executed evidence beats reasoning: run it, exec into it, read the log. A
thesis backed only by static reasoning caps in the low nineties.
- The antithesis MUST be independent — never let the thesis-author be its own
sole skeptic.
- Diversity over redundancy: skeptics with different lenses catch failure modes
identical skeptics miss.
- Report the final thesis with its evidence AND the losing objections, so the
operator sees why it survived.

25
skills/explain/SKILL.md Normal file
View File

@@ -0,0 +1,25 @@
---
name: explain
description: >
Explain code and its interactions. Trigger when the operator asks what a
piece of code does, how a mechanism works, why components interact the way
they do, or invokes /explain on a file, function, role, or module.
Portable across projects.
---
Explain the named code so an engineer who has never seen it can follow it:
1. Read the code and every collaborator it touches (callers, callees,
templates, configs) before explaining; never explain from the name alone.
2. Lead with the purpose in one sentence, then walk the flow in execution
order: inputs, transformations, side effects, outputs. Name the real
files and line references.
3. Make the interactions explicit: which component induces, triggers, or
consumes which, and where the single source of truth lives. Point out
trip-wires (non-obvious ordering, escaping, caching, permission edges)
as part of the flow, not as an appendix.
4. Afterwards ask whether to persist a Mermaid diagram of the explained
mechanism under a `## Schema` heading in the README.md next to the
affected code (create the heading if missing, replace its previous
diagram if present). On approval, keep the diagram to the explained
scope: components as nodes, induction/data flow as labeled edges.

View File

@@ -0,0 +1,25 @@
---
name: help-skills
description: >
List and explain every skill available in the current session. Trigger on
/help-skills, "welche skills gibt es", "skill übersicht", or when the
operator asks what skills exist or what a skill does. Portable across
projects.
---
Produce a one-shot overview of every skill the session can invoke:
1. Sources, in this order: the session's available-skills listing, the
project's own skills (`skills/` and `.claude/skills/` in the repo), the
user-global skills (`~/.claude/skills/`), and plugin-provided skills.
Deduplicate by name; note when a project skill shadows a global one.
2. Output one table per origin (project, global, plugin): skill name,
trigger (slash command or phrasing), one-line purpose in your own words
— compress the description, do not paste it.
3. When the operator names a specific skill or topic, explain only the
matching skills in more depth: what they do, when they fire on their
own, and how they compose (e.g. commit-next → commit-no-verify →
comments-clean).
4. End with the activation hint: project installers or `make install` in
the skills repository refresh the live copies; a restart loads renamed
or new skills.

108
skills/no-defaults/SKILL.md Normal file
View File

@@ -0,0 +1,108 @@
---
name: no-defaults
description: >
Forbid default values for parameters, settings, and environment lookups in
every language. Load and apply AUTOMATICALLY on every task that writes,
edits, generates, refactors, or reviews code, config, templates, schemas,
or infrastructure — never wait to be asked. A default silently substitutes
a guess for a value the caller failed to supply, so a misconfiguration
becomes wrong behaviour instead of a loud failure. Demand strict, explicit
values and fail loudly when one is missing. `false`, `null` and `""` are the
only free defaults; every other default needs a written justification.
Portable across projects.
---
A default value is a guess, written at the point where the caller's intent is
least known, and it turns a missing value into a plausible wrong answer instead
of an error. Two callers then read the same call differently: one means "use the
default", the other simply forgot. That ambiguity is the defect this rule exists
to prevent.
Never write a substantive default. Require the value. When it is absent, fail
immediately, loudly, and with a message that names the missing thing.
## The rule
1. Every parameter, option, setting, environment lookup, column, and template
variable MUST be supplied explicitly by its caller.
2. A missing value MUST abort at the earliest possible moment — argument
parsing, config load, container start — never at the point of use, and never
by proceeding with a substitute.
3. The error MUST name the missing key and where to set it.
4. Optionality, when a value genuinely may be absent, MUST be modelled as an
explicit named case that the call site handles, never as a silent
substitution. `None`/`null` handled in a visible branch is fine; a fallback
baked into the accessor is not.
## The empty defaults are free
`false`, `null` (`None`, `nil`, `undefined`), and the empty string `""` MAY be
used as defaults without justification. They are the one class that carries no
guess: they denote absence itself rather than inventing a value the caller might
have meant. `${VAR:-}`, `d.get(k)`, `a ?? null`, and `def f(a=None)` are fine.
Every other default value — a number, a non-empty string, a non-empty list or
map, an enum member, a path, a timeout, a retry count — MUST carry an inline
comment stating why that specific value is correct for every caller that omits
it. No comment, no default. Use whatever exception-comment form the project
sanctions; where none exists, a plain comment on or directly above the default.
The comment is the point: it forces you to name the assumption, so a reviewer
can disagree with it. "Sensible", "standard", and "usual" name nothing — if the
justification does not survive being written down, the default does not survive
either.
## Banned constructs, by language
Each `Never` below means the default is a substantive value. The same construct
with `false`, `null`, or `""` is permitted, and with any other value is permitted
only when justified in a comment.
| Language | Never | Instead |
|---|---|---|
| Bash | `${VAR:-x}`, `${VAR:=x}`, `: "${VAR:=x}"` | `: "${VAR:?set VAR in <file>}"` |
| Python | `def f(a=1)`, `d.get(k, x)`, `getattr(o, n, x)`, `os.environ.get(k, x)`, `argparse(default=)` | required args, `d[k]`, `os.environ[k]`, `add_argument(required=True)` |
| Jinja / Ansible | `\| default(x)`, `lookup('env', k) \| default('')` | `\| mandatory`, `assert` on the var |
| JS / TS | `function f(a = 1)`, `const {a = 1} = o`, `a \|\| x`, `a ?? x` | required params, explicit `if (a === undefined) throw` |
| Go | relying on zero values | constructor that validates every field |
| Rust | `unwrap_or`, `unwrap_or_default`, `#[serde(default)]` | `ok_or(Error::MissingX)?`, `#[serde(deny_unknown_fields)]` |
| Java / C# | optional parameters, `??`, `getOrDefault` | required constructor args, explicit null check that throws |
| SQL | `DEFAULT` on a column | `NOT NULL` and supply it in every insert |
| Make | `VAR ?= x` | `VAR := $(or $(VAR),$(error VAR is required))` |
| Docker / Compose | `${VAR:-x}`, `ENV` placeholders standing in for config | `${VAR:?VAR is required}` |
| Terraform / Helm | `variable { default = }`, `default` in `values.yaml` | no default; fail on the missing input |
## How to apply
Before writing any parameter, ask: what happens if the caller omits it? If the
answer is anything other than "it fails and says so", rewrite it.
While editing, treat every `default`, `?:`, `??`, `||`, `:-`, `:=`, `or`,
`fallback`, and `getOrElse` in a value-resolution position as a finding unless
its substitute is `false`, `null` or `""`, or a comment already justifies it.
Grep your own diff for them before presenting it.
Pushing a default one layer up is not a fix: a caller that passes a constant it
invented has only moved the guess. Follow the value to its true owner —
the operator, the inventory, the deployment config — and require it there.
Removing an existing default is a behaviour change. Do not do it silently as a
drive-by: name it, and let the operator decide whether it lands in this change
or its own.
## What does not count as a justification
"It is conventional", "every other project does it", "the value is obvious",
and "it keeps the signature short" justify nothing — they assert that someone
else made the choice, which is the ambiguity this rule exists to remove. A valid
justification names why this value is right for a caller who says nothing.
An operator asking for a specific default, in this conversation, for this value,
settles it without a comment.
## Penalty
If this audit finds an unjustified default you wrote yourself: say so in the
report, with file and count and no excuses; lower the confidence numbers for the affected
bundle; and write the incident to persistent agent memory, recording the date
and the construct you reached for, so the calibration survives the session.

60
skills/observe/SKILL.md Normal file
View File

@@ -0,0 +1,60 @@
---
name: observe
description: >
Watch a named target (a CI run, a pipeline, a build, a log) on a fixed
cadence and apply the triage skill whenever new failures appear. Trigger on
/observe <target> or when the operator asks to keep monitoring something and
fix regressions as they surface. Portable across projects.
---
Keep a target under continuous watch and act only when it degrades. The loop is
the watcher; triage is the response. Do not re-investigate failures already
resolved this session, and do not idle-poll work the harness will notify you
about.
## Before you start: fix the target
The operator names one concrete target: a CI run id or url, a pipeline, a
branch's checks, a build, or a log file. Restate it in one line and record the
current failure set as the baseline (the failures already known or already
fixed this session). Everything the watch reports is diffed against that
baseline, so a stale baseline re-triages solved failures.
## The loop
Drive this with `/loop` in dynamic mode (no fixed interval unless the operator
gave one): after each observation, schedule the next wake-up yourself and pass
the same `/observe` prompt back so the next firing repeats the watch.
1. **Poll the target.** Pull its current state (job list, check conclusions, the
log's tail). Enumerate the failures exactly as the `triage` skill's step 1
does: failure, cancelled, timed-out; a downstream aggregate that fails only
because an upstream did is not its own failure.
2. **Diff against the baseline.** A failure is *new* only if it is not in the
baseline and was not already triaged this session. If nothing is new, log one
short line ("still green" / "N known failures, no change") and reschedule.
3. **Triage each new failure.** Invoke the `triage` skill on the new set only —
which applies `dialectic` per failing job to a ~99% root cause and a real
fix. Never mask (no retry-until-pass, no disabled check, no soft-skip). Fold
each newly fixed failure into the baseline so it is not re-triaged.
4. **Reschedule.** Pick the next delay from what you are waiting on: an active
run that changes every few minutes gets a check matched to its cadence; a
quiet or long-running target gets a long fallback (1200s+). Keep watching
until the target reaches a terminal state (run completed, pipeline finished)
AND every failure it produced has a verified fix, then stop the loop.
## Rules
- The loop is a fallback timer, not a busy-wait: if the harness will notify you
when tracked work finishes (a background task, a spawned agent), wait for that
notification instead of polling for it; reserve the timed wake-up for external
state the harness cannot see (a remote CI run, a deploy queue).
- Executed evidence beats reasoning: read the actual failing log line and the
artifact, not the job title — triage and dialectic carry this through.
- One baseline, updated in place: dedup every observation against all failures
seen so far, or a flaky-then-fixed failure reappears every cycle and the watch
never converges.
- Report only on change: a new failure and its triage verdict, or a terminal
"target is green, watch closed". Silence between changes is correct.
- Never invent results for a run still in progress; report what the poll
actually returned.

108
skills/refactor/SKILL.md Normal file
View File

@@ -0,0 +1,108 @@
---
name: refactor
description: >
Refactor every file that is currently uncommitted in the git working tree -
staged, unstaged and untracked - up to the project's coding standards,
without changing behaviour. Trigger on /refactor or when the operator asks to
clean up, tidy, or polish the current changes before committing. Portable
across projects.
---
The unit of work is the uncommitted change set, not the repository. Behaviour
stays identical: a refactor that changes what the code does is a bug, not a
cleanup.
## Step 1: fix the file set
Read the set from git, never from memory of what was edited:
```bash
git status --porcelain
```
Take modified, added, renamed and untracked files. Drop deleted files, binaries,
lockfiles, vendored trees and generated output. Restate the resulting list in
one line before touching anything; if it is empty, say so and stop.
Never `git stash` and never revert a working-tree file to compare - other agents
and the operator share this tree. Read the committed version with
`git show HEAD:<path>` into a temporary file instead.
## Step 2: get a safety net first
A refactor is only provably behaviour-preserving if something failed before it
and passes after it. Before editing, check what covers the change set:
- **Coverage exists**: run it now and record the green baseline. A suite that
was already red gets reported, not refactored around.
- **No coverage and the code is testable logic**: write the characterization
tests first, against the current behaviour - including the behaviour you
consider wrong, so the refactor cannot silently change it. Keep them small
and framework-free unless the project already has a framework. Fix the
captured wrongness in a separate change, never in this one.
- **Not sensibly testable**: pure glue, declarative config, a thin wrapper over
a service the test would have to mock into meaninglessness, or a project with
no test setup at all. Say so in one line and refactor without a net; do not
invent a test harness the project never asked for.
The new tests are part of the deliverable and must still pass unchanged after
the refactor. A test that had to be edited to stay green means the behaviour
moved: revert the refactor, not the test.
## Step 3: refactor each file
Read the whole file first, then apply the standards that the surrounding code
and the project's own config already establish (formatter config, linter rules,
existing idioms) over any generic preference of your own.
Four principles govern the result:
- **SRP**: one file, one responsibility; one function, one reason to change. A
unit that needs an "and" to describe it is two units.
- **SPOT**: every fact - a constant, a path, a schema, a rule - lives in exactly
one place and everything else references it.
- **KISS**: the simplest construct that works. No abstraction without a second
caller today, no configuration for a value that never varies.
- **DRY**: the same logic is written once. A literal repeated three times is a
missing constant.
Concretely:
- **File size**: no file containing program code exceeds 250 lines. Split the
overflow along the SRP seam - it is the symptom, not the cause. Description
and markup languages are exempt (Markdown, YAML, JSON, HTML, translation and
data files), as is the rare file the language or framework genuinely forbids
splitting: name that reason in the report, "it was easier" does not qualify.
- **Duplication**: fold repeated logic into the helper that already exists;
create a new one only when two or more call sites need it now.
- **Dead weight**: unused imports, variables, parameters, branches that cannot
be reached, flags with a single value, abstractions with one implementation.
- **Naming**: names that state what a thing is, matching the file's convention.
- **Shape**: split a function that needs a scroll to read; collapse nesting by
returning early; replace magic values with a named constant at its use site.
- **Errors**: never widen a caught exception, never swallow one, never remove a
validation or a security check to make the code shorter.
- **Comments**: invoke the `comments-clean` skill rather than reimplementing its
rules here.
Do not reformat regions you have no substantive fix for - cosmetic churn buries
the real change in the diff. If a file's problem is architectural and cannot be
fixed inside the change set, report it instead of half-doing it.
## Step 4: keep behaviour provable
- Re-run the step 2 net and require the same green result as the baseline.
- Never fold a bug fix, a feature, or a dependency change into a refactor.
Found a real bug? Name it in the report and leave it for a separate change.
- Run the cheap checks the project already has for the touched files (formatter,
linter, the module's own unit tests). Do not start a full suite, a build, or a
deploy on your own initiative - propose it and let the operator trigger it.
- State plainly what was verified and what was not. An unverified refactor is
reported as unverified.
## Step 5: report
One line per file: what changed and why it is safe. Then one line naming what
was deliberately left alone (architectural findings, bugs, files skipped) and
the exact command the operator can run to validate. Do not commit unless the
operator asked for a commit.

38
skills/robot/SKILL.md Normal file
View File

@@ -0,0 +1,38 @@
---
name: robot
description: >
Work fully autonomously to a goal. Trigger when the operator wants hands-off,
uninterrupted execution — the agent drives the task to completion without
check-ins, never stops before the goal is reached, and uses only commands it
is already cleared to run. Portable across projects.
---
Operate as an autonomous robot: given a goal, reach it without hand-holding.
## Contract
- **No questions once the goal is clear.** Clarify only genuinely-blocking
unknowns up front (via the `active-listening` skill), then proceed without
further check-ins. Pick the cleanest option and continue; never pause to
confirm a choice you can make yourself.
- **Never stop before the goal.** Do not end the run, hand back, or declare done
while any part of the goal is unmet — premature termination is forbidden. A
blocker is something to solve or route around, not a reason to quit; if one
step is genuinely, externally impossible, surface exactly why and keep working
on everything else until nothing solvable remains.
- **Stay inside your clearance.** Use ONLY commands you are pre-authorized to
run. Never invoke anything the agent or tool settings mark as denied or as
requiring per-invocation approval (ask) — no commit, push, deploy, or other
approval-gated action unless the operator has already cleared it for this run.
When a needed action is out of clearance, do all the cleared work and report
the single blocked step for the operator to run.
- **Verify, do not assume.** Reaching the goal means proving it: run it, read the
output, confirm the end state. Never report success on a step you did not
actually verify.
## Rules
- Escalate an uncertain root cause or design decision to the `dialectic` skill
rather than guessing; uncertainty is never a reason to stop.
- Sustain the loop across long, multi-step work — the run is finished only when
the whole goal is verifiably met.

44
skills/triage/SKILL.md Normal file
View File

@@ -0,0 +1,44 @@
---
name: triage
description: >
Triage a failing CI run, pipeline, or job set to a verified root cause and a
real fix. Trigger when a build, test, or deploy pipeline has one or more failed
jobs. Applies the dialectic skill to every failing job independently. Portable
across projects.
---
Drive a red pipeline to green by root cause, not by guesswork. Treat every
failing job as its own investigation, and do not declare a fix until that job's
root cause is proven.
## Procedure
1. **Enumerate every failing job.** Pull the run's job list and isolate each job
whose conclusion is failure, cancelled, or timed-out. A downstream aggregate
job that fails only because an upstream job did is not a separate root cause —
note it and move on.
2. **Dialectic per failing job.** For EACH failing job, invoke the `dialectic`
skill: form a thesis about the root cause from evidence (the job log, its
artifacts, the code at the run's commit, git history), attack it with
independent skeptics, and iterate to a ~99% thesis. The jobs are independent,
so run their investigations in parallel where the tooling allows.
3. **Distinguish shared vs hidden causes.** When several jobs share one root
cause, fix it once. When a job hides a second failure behind the first, keep
going until the job is actually green, not just past the first error.
4. **Fix at the root.** Apply the real fix in the repository for each proven root
cause. Never mask a failure — no retry-until-pass, no disabling the check, no
soft-skip. If a failure is genuinely external (upstream outage, flaky infra),
confirm that with evidence and surface it honestly instead of fixing around
it.
5. **Follow the run.** While the run is still in progress, re-check it
periodically and triage each newly failed job as it appears. The run is done
only when it has finished and every failure has a verified fix.
## Rules
- Executed evidence beats reasoning: read the actual failing log line and the
artifact, not the job title.
- Classify each failure: a real defect (fix it), a flake or infra hiccup (prove
it is transient before dismissing), or already-fixed-by-a-later-commit (verify
the fix is present in the run's commit before claiming it).
- Report a per-job verdict: root cause, the evidence that proves it, and the fix.

92
tests/test_autotune.py Normal file
View File

@@ -0,0 +1,92 @@
"""Validate the autotune reminder hook and its settings registration."""
from __future__ import annotations
import json
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILL = REPO_ROOT / "skills" / "autotune" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "autotune" / "hooks" / "reminder.sh"
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
def _run_hook(stamp: Path, interval: str = "3600") -> subprocess.CompletedProcess:
return subprocess.run(
["bash", str(HOOK)],
capture_output=True,
text=True,
check=True,
env={
"PATH": "/usr/bin:/bin",
"HOME": str(stamp.parent),
"AUTOTUNE_STAMP": str(stamp),
"AUTOTUNE_INTERVAL_SECONDS": interval,
},
)
class TestAutotuneSkill(unittest.TestCase):
def test_skill_is_trigger_only(self):
text = SKILL.read_text(encoding="utf-8")
self.assertIn("name: autotune", text)
self.assertIn("multiSelect: false", text)
class TestAutotuneHook(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.stamp = Path(self.tmp.name) / ".claude" / ".autotune-stamp"
def test_first_run_hints_and_stamps(self):
result = _run_hook(self.stamp)
self.assertIn("/autotune", result.stdout)
self.assertTrue(self.stamp.is_file())
def test_second_run_is_silent_within_interval(self):
_run_hook(self.stamp)
self.assertEqual(_run_hook(self.stamp).stdout, "")
def test_hint_returns_after_the_interval(self):
_run_hook(self.stamp)
self.assertIn("/autotune", _run_hook(self.stamp, interval="0").stdout)
def test_corrupt_stamp_does_not_crash(self):
self.stamp.parent.mkdir(parents=True, exist_ok=True)
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
self.assertIn("/autotune", _run_hook(self.stamp).stdout)
@unittest.skipUnless(shutil.which("node"), "node not installed")
class TestSettingsRegistration(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.settings = Path(self.tmp.name) / "settings.json"
def _patch(self) -> dict:
subprocess.run(["node", str(PATCHER), str(self.settings)], check=True, capture_output=True)
return json.loads(self.settings.read_text(encoding="utf-8"))
def test_hook_registered_once(self):
self._patch()
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("autotune/hooks/reminder.sh"), 1)
def test_existing_hooks_preserved(self):
self.settings.write_text(
json.dumps({"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "true"}]}]}}),
encoding="utf-8",
)
data = self._patch()
self.assertEqual(len(data["hooks"]["UserPromptSubmit"]), 2)
if __name__ == "__main__":
unittest.main()