Files
skills/trainer/SKILL.md
Mathias d6a71e370e
Some checks failed
release / tag (push) Has been cancelled
chore: bootstrap skills library — 19 skills + installer + CI auto-tag
Phase 1 of mathias/skills extraction (infra#62 Track D — homelab
next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill
dirs + SKILLS_INDEX.md) and adds the installation surface:

- Taskfile.yml — install / update / list / release / check targets
- install.sh — bootstrap installer for hosts without Task. Idempotent
  symlink wirer; default checkout at ~/.local/share/skills/ on every
  host; SKILLS_REF env var pins a tag (default: main).
- .gitea/workflows/release.yml — auto-tag every push to main by
  Bump-Type footer (major/minor/patch, default patch). Skipped when
  commit contains [skip-release].
- README — usage, versioning, contribution flow, secret-hygiene rule.

Phase 1 wires Claude Code only (~/.claude/skills/<name> global +
<repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode,
antigravity, and gitea-resident agents (cobalt-dingo, agentsquad)
once their skill conventions are researched.

Public repo, markdown-only — no secrets, no client names. Verified
via pre-push grep before initial push.

[skip-release]
2026-05-24 14:59:54 +02:00

11 KiB

name, description
name description
trainer Two-phase brain curation — scan session logs to score candidate learnings (READ), then write the ones that pass the quality gate as generalized knowledge entries (WRITE). Use periodically to keep the brain useful, not noisy.

Trainer

Overview

The trainer is the brain's quality gate. It runs in two phases:

  • READ phase: scan session logs, score candidate learnings 1-5, output the candidate list.
  • WRITE phase: take candidates that score ≥3, apply a quality gate (must generalize, no project-specific identifiers, must be clear cold), emit knowledge entries.

session-retrospective surfaces candidates loosely. The trainer is stricter — it produces only generalized, reusable knowledge, the kind that earns its place in long-term memory.

Core principle: If a knowledge entry only makes sense to someone who was in the session, it does not belong in the brain.

Iron Laws

  1. Do not call brain_write from this skill. Emit entries; the operator decides which to commit.
  2. Apply the quality gate before emitting any entry. A skipped gate produces brain pollution.
  3. No project-specific paths, identifiers, or function names in entries. If you cannot generalize the lesson, drop the entry.

When to Use

  • Periodically (weekly, or after a stretch of intense work) — not after every session
  • When session-retrospective has produced too many candidates to keep — trainer filters them
  • When the brain is becoming noisy and needs curation pruning before adding more

Do not use for:

  • Single sessions with one or two clear learnings (use session-retrospective directly)
  • Project-specific decisions (those belong in CLAUDE.md, not the brain)

Phase READ — Scan and Score

What to Look For

  • Patterns that worked — the approach was clean, correct, and worth reinforcing for future sessions
  • Corrections — something was first done wrong, then corrected. Both sides are valuable: the rationalization that led to the wrong path, and the correction that fixed it.

Scoring (1-5)

Score Criteria
5 Novel pattern, clearly correct, generalizes across projects and domains
4 Good pattern, correct, somewhat project-specific but still broadly useful
3 Correct but obvious — include only if especially clean or pedagogically useful
2 Trivial or already widely known — skip
1 Project-specific noise, mechanical fix, or single-use detail — skip

Anything below 3 is dropped. Anything at 3 is borderline — include only if the operator might benefit from the framing.

READ Output Format

Respond in markdown:

**Candidate N (score: X/5, type: pattern|correction)**
- **What happened:** Brief description of the learning moment
- **Why it's valuable:** What makes this worth preserving
- **Key insight:** The distilled lesson in one sentence

End with: N candidates found (M scoring ≥ 3) — the WRITE phase will use the ≥3 candidates.

Phase WRITE — Emit Knowledge Entries

Quality Gate

Apply BEFORE writing each entry. Drop the entry if any check fails:

  • Lesson is phrased so it could apply to any project, not just this one — no project-specific paths, variable names, function names, or repo identifiers
  • When it applies may name a stack or tool family (e.g. "Go projects with golangci-lint") to scope the relevance, but must not name specific files or functions
  • Example may use concrete tool names, language syntax, or commands to make the point land — concrete examples are pedagogically necessary; only the Lesson and When-it-applies sections need to generalize
  • The insight is clearly stated enough that someone reading it cold (no session context) understands it
  • The lesson is not already in CLAUDE.md or an existing memory file

WRITE Output Format

For each candidate that passes the quality gate:

# [Topic]

## Lesson

[The key insight in 1-3 sentences. Generalized — no project-specific names.]

## When it applies

[Conditions under which this pattern is relevant.]

## Example

[A brief, generic example illustrating the lesson. Pseudocode or
language-agnostic prose preferred over a copy of the original code.]

After all entries, end with: N entries ready for brain_write or 0 entries passed quality gate — <reason>.

The operator (or a calling agent) decides whether to actually run brain_write on each entry. This skill does not write directly.

Worked Example

READ phase output

**Candidate 1 (score: 4/5, type: correction)**
- **What happened:** Implementer subagent reported "tests pass" after running `go test ./...`, but CI failed on a `defer resp.Body.Close()` errcheck violation 30 minutes later.
- **Why it's valuable:** Captures a specific gap between local verification and CI verification that is easy to repeat across Go projects.
- **Key insight:** Per-task verification must include the linter, not just the test runner.

**Candidate 2 (score: 5/5, type: pattern)**
- **What happened:** Repaired malformed JSON from LLM output by stripping invalid escape sequences before parsing. Generalizes well — most LLM-as-JSON pipelines need this.
- **Why it's valuable:** Pattern works across any LLM-as-JSON ingestion pipeline.
- **Key insight:** Treat LLM JSON output as untrusted input that may need normalization before parsing.

**Candidate 3 (score: 3/5, type: pattern)**
- **What happened:** Implementer subagent saved its commit SHA in the structured report so the spec reviewer could verify against a stable revision.
- **Why it's valuable:** Obvious in hindsight, but cleanly framed — passing identifiers between subagents avoids race conditions when the file changes mid-review.
- **Key insight:** Subagent reports should include verifiable identifiers (commit SHAs, file hashes) the reviewer can pin against.

**Candidate 4 (score: 2/5, type: pattern)**
- **What happened:** Renamed a local variable from `data` to `invoiceLineItems` for clarity.
- **Why it's valuable:** It's good practice but trivial.
- **Key insight:** Use names that describe what the data is.

4 candidates found (3 scoring ≥ 3)

WRITE phase output

# Per-task verification must include lint

## Lesson

When dispatching tasks that include "tests pass" as a success criterion,
the verification command must include the project's linter, not just the
test runner. Tests verify behavior; linters catch policy violations
(error handling, unused variables, formatting) that the test suite is
blind to.

## When it applies

Any project with a CI lint gate, particularly Go projects with
golangci-lint configured for errcheck or similar policies.

## Example

In a project with a Taskfile that chains lint + test, the per-task
verification should be `task check`, not `go test ./...`. The latter
will pass locally and fail in CI.

---

# LLM JSON output is untrusted input

## Lesson

Output from an LLM that is supposed to be JSON should be treated as
untrusted input. It may contain invalid escape sequences (often from
markdown-formatted content the LLM included), structurally valid but
semantically wrong fields, or trailing prose. Normalize before parsing.

## When it applies

Any pipeline that calls an LLM with a JSON output contract and parses
the response with a strict JSON parser.

## Example

Wrap the parser in a normalization step: strip invalid escape sequences,
trim trailing prose after the closing brace, validate against the
expected schema. Treat the LLM as a network boundary, not a function call.

---

# Subagent reports should carry verifiable identifiers

## Lesson

When a subagent reports completion of work that another agent will verify,
the report must carry an identifier that pins the verifiable state — a
commit SHA, file hash, or revision number. Without one, the verifier may
read a different state than the subagent produced if the workspace
changes between report and review.

## When it applies

Any multi-agent workflow with a verify-after-implement step, particularly
when the workspace is shared (a worktree or branch held by multiple
processes).

## Example

After committing changes, a subagent reports `Status: DONE, SHA: abc1234`.
The verifier runs `git show abc1234` to read exactly the state the
subagent produced, even if newer commits arrived in the interim.

3 entries ready for brain_write

Anti-Patterns

Anti-Pattern Why It Fails
Score everything 4 or 5 The gate exists for a reason. If everything is high-value, nothing is.
Knowledge entries with project-specific paths They will not match queries from other projects, defeating the brain's value.
Writing entries directly without the quality gate Polluted brain → useless brain. The gate is the whole point.
Skipping READ and going straight to WRITE The score is what determines whether to write. Without it, you are just transcribing.

Brain MCP Integration

The brain holds prior curated knowledge entries. Querying it before READ prevents duplicate entries from cluttering the gate.

Before READ:

  • Run brain_query for the topics that appear in the session log. This prevents scoring candidates that are already in the brain (skip them — same content twice does not help).

After WRITE:

  • This skill does NOT call brain_write. The operator decides which entries to commit, then runs brain_write per accepted entry.

Logging

Call session_log once at the end of every phase to record the outcome. Pass-rate is computed downstream by the /pass-rate HTTP endpoint, which treats pass as success, fail as failure, skip as neither.

At end of read phase:

  • session_log with {skill: "trainer", phase: "read", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}

At end of write phase:

  • session_log with {skill: "trainer", phase: "write", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}

Phases for this skill: read, write

Status semantics:

  • pass — the phase's intended outcome was reached (read: candidates scored; write: gate-passed entries emitted).
  • fail — the phase's intended outcome was NOT reached.
  • skip — phase was skipped intentionally (e.g. no candidates passed the gate, write skipped).

Why this matters: the routing pod (Plan 6) reads pass-rate to decide whether to route a future call to a local model. If your skill never logs, the routing pod sees no data.

Mode 2 Routing Note

Trainer is the same shape as session-retrospective — long-context reading, mechanical filtering — and is a Mode 2 routing candidate. Until Plan 6, treat as Mode 1 only.

Cross-References

  • Load session-retrospective first if you want a looser, faster surfacing pass before the strict gate.
  • The brain's brain_query and brain_write tools are the read/write side of the brain that this skill curates.