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]
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
- Do not call
brain_writefrom this skill. Emit entries; the operator decides which to commit. - Apply the quality gate before emitting any entry. A skipped gate produces brain pollution.
- 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-retrospectivehas 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-retrospectivedirectly) - 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.mdor 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_queryfor 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 runsbrain_writeper 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_logwith{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_logwith{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-retrospectivefirst if you want a looser, faster surfacing pass before the strict gate. - The brain's
brain_queryandbrain_writetools are the read/write side of the brain that this skill curates.