Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d368d4b5d | ||
|
|
a565b04e91 | ||
|
|
b616c51a2c | ||
|
|
b8bacfa7b3 | ||
|
|
c377c23e21 | ||
|
|
8f9d5dd8bd |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "skills",
|
"name": "skills",
|
||||||
"description": "Mathias's engineering skills library, plus the hooks and MCP servers that make it enforceable: TDD, clean code, debugging, specs, discovery, CI, and secret hygiene.",
|
"description": "Mathias's engineering skills library, plus the hooks and MCP servers that make it enforceable: TDD, clean code, debugging, specs, discovery, CI, and secret hygiene.",
|
||||||
"version": "0.13.0",
|
"version": "0.13.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Mathias",
|
"name": "Mathias",
|
||||||
"url": "https://git.d-ma.be/mathias"
|
"url": "https://git.d-ma.be/mathias"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"_parked": {
|
"_parked": {
|
||||||
"_reason": "cad and gitea-mcp are proxied and reachable (tailscale serve 8445 -> 127.0.0.1:31877, 8446 -> 127.0.0.1:30340; both answer 401 unauthenticated, so auth is enforced and the path is right). They stay parked because their static tokens live in the 1Password AgentSecrets vault, and this host's op identity sees only HomeLab -- 'op vault list' returns HomeLab alone, and resolving op://HomeLab/gitea-mcp-shared-token/password or op://HomeLab/CAD_STATIC_TOKEN/password fails with 'could not find item'. That is infra#224 (AgentSecrets not visible to koala's service account), NOT the OAuth-discovery bug both issues warned about. Enabling is: make the vault visible, add the two op:// references to ~/.op-env, move these entries into mcpServers. See mathias/skills#32 and #33.",
|
"_reason": "cad and gitea-mcp are proxied, reachable and AUTHENTICATING: tailscale serve 8445 -> 127.0.0.1:31877 and 8446 -> 127.0.0.1:30340; an MCP initialize with the correct bearer returns 200 from both (serverInfo gitea-mcp 0.1.0, cad aed2aaf). They stay parked only because nothing puts those two tokens into the session environment. Both live in the 1Password AgentSecrets vault, and by deliberate design (infra#224, closed) no single identity on koala reads both vaults: koala-homelab is HomeLab-only and resolves ~/.op-env, koala-agentsecrets-write is AgentSecrets-only and lives in ~/.op-agent-write.env. Adding an AgentSecrets reference to ~/.op-env does NOT degrade gracefully -- `op run` fails wholesale and every other token, BRAIN_MCP_TOKEN included, goes unresolved with it. Enabling needs a read-scoped AgentSecrets identity in the session-env path; see mathias/skills#32, #33.",
|
||||||
"cad": {
|
"cad": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "https://koala.tail30472e.ts.net:8445/mcp",
|
"url": "https://koala.tail30472e.ts.net:8445/mcp",
|
||||||
|
|||||||
@@ -130,13 +130,48 @@ before `task skills:install`; default is `main` for always-current.
|
|||||||
|
|
||||||
## Contributing a skill
|
## Contributing a skill
|
||||||
|
|
||||||
1. Add a directory `<short-kebab-name>/`.
|
1. Add a directory `skills/<short-kebab-name>/`.
|
||||||
2. Create `<short-kebab-name>/SKILL.md` — start with one-paragraph
|
2. Create `skills/<short-kebab-name>/SKILL.md` with this frontmatter — all four
|
||||||
purpose, then triggers ("use when …"), then mechanics.
|
keys are gated by `task check`, and the index table is generated from the
|
||||||
3. Add a row to `SKILLS_INDEX.md` (description + use-when).
|
last three:
|
||||||
4. Commit directly to `main`. Conventional-commit prefix
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
name: <short-kebab-name>
|
||||||
|
surfaces: [claude-code, library] # any of claude-code | library | chat
|
||||||
|
summary: "<one line for the index table>"
|
||||||
|
use_when: "<the trigger, for the index table>"
|
||||||
|
description: <the paragraph the model selects on>
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Then: one-paragraph purpose, triggers ("use when …"), mechanics.
|
||||||
|
3. Run `task index` and commit the regenerated `SKILLS_INDEX.md`. Do **not**
|
||||||
|
hand-edit the table — `task check` regenerates it and fails on a diff.
|
||||||
|
4. Pressure-test it before shipping: run the scenario against a subagent that
|
||||||
|
does *not* have the skill, write the skill against what that run actually
|
||||||
|
did, then re-run with it. See `superpowers-writing-skills` and #19.
|
||||||
|
5. Commit directly to `main`. Conventional-commit prefix
|
||||||
(`feat:` / `fix:` / `docs:`). Add `Bump-Type: minor` footer when
|
(`feat:` / `fix:` / `docs:`). Add `Bump-Type: minor` footer when
|
||||||
adding a new skill or expanding scope; default patch covers edits.
|
adding a new skill or expanding scope; default patch covers edits.
|
||||||
|
|
||||||
This repo is markdown-only and public — never include secrets, client
|
This repo is markdown-only and public — never include secrets, client
|
||||||
names, or anything under NDA in a skill.
|
names, or anything under NDA in a skill.
|
||||||
|
|
||||||
|
## Mode 2 routing candidates
|
||||||
|
|
||||||
|
These notes used to sit in each SKILL.md as a `## Mode 2 Routing Note` section.
|
||||||
|
They changed nothing about how the skill is invoked — several said so outright —
|
||||||
|
and cost context on every load, so they live here instead. Nothing in the repo
|
||||||
|
depends on this table; it is a record of which skills looked mechanical enough
|
||||||
|
to route to a local model, should a routing layer ever exist.
|
||||||
|
|
||||||
|
| Skill | Note |
|
||||||
|
|---|---|
|
||||||
|
| `cad-babysit` | Phases 2-4 (reading comments/logs, classifying fault vs outcome) are judgment-heavy and should stay Mode 1. Phase 1 (running `cad stuck` and grouping its output) is mechanical and a Mode 2 routing candidate once Plan 6 ships the routing pod. |
|
||||||
|
| `code-review` | This skill is invoked identically in Mode 1 (cloud Claude) and Mode 2 (client-local with supervisor routing). The routing layer (Plan 6) does not exist yet; treat this skill as Mode 1 only for now. Findings format and severity classification are unchanged across modes. |
|
||||||
|
| `debug` | This skill produces high-volume mechanical output (hypothesis enumeration) and is a candidate for Mode 2 routing to a local model in the future. Until Plan 6 ships the routing pod, treat as Mode 1 only. The hypothesis format and discipline are identical regardless of which model generates them. |
|
||||||
|
| `feature-spec` | Spec writing requires interpretation of intent and tradeoff reasoning — work where Claude generally outperforms a 9-13B local model. This skill is intentionally **not** a Mode 2 routing candidate. In Mode 2, the routing layer should leave `feature-spec` calls on Claude. |
|
||||||
|
| `session-retrospective` | Reading long session logs and pulling themes is high-volume, mechanical pattern-recognition work — a strong Mode 2 routing candidate. Until Plan 6 ships the routing pod, treat as Mode 1 only. |
|
||||||
|
| `tdd` | This skill is invoked identically whether the agent is running in Mode 1 (cloud Claude, no routing) or Mode 2 (client-local, supervisor routing layer). The routing pod (Plan 6) does not exist yet; until it does, treat this skill as Mode 1 only. The discipline does not change between modes — only the model behind the call. |
|
||||||
|
| `trainer` | 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. |
|
||||||
|
|||||||
+5
-1
@@ -110,7 +110,11 @@ tasks:
|
|||||||
# harness instead of erroring.
|
# harness instead of erroring.
|
||||||
sh '{{.TASKFILE_DIR}}/bin/check-surfaces' '{{.TASKFILE_DIR}}' || fail=1
|
sh '{{.TASKFILE_DIR}}/bin/check-surfaces' '{{.TASKFILE_DIR}}' || fail=1
|
||||||
|
|
||||||
# 5. Plugin manifest version must match the latest tag, or installers
|
# 5. A documented session_log call must be one the tool would accept:
|
||||||
|
# session_id is its only required field, and nine skills omitted it.
|
||||||
|
sh '{{.TASKFILE_DIR}}/bin/check-session-log' '{{.TASKFILE_DIR}}' || fail=1
|
||||||
|
|
||||||
|
# 6. Plugin manifest version must match the latest tag, or installers
|
||||||
# never see the update (Claude Code gates updates on this field).
|
# never see the update (Claude Code gates updates on this field).
|
||||||
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
||||||
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
||||||
|
|||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# check-session-log — a skill that documents a session_log call must document a
|
||||||
|
# call the tool would actually accept.
|
||||||
|
#
|
||||||
|
# Nine skills documented the payload and none named `session_id`, which is the
|
||||||
|
# tool's only REQUIRED field. An agent following any of them either failed the
|
||||||
|
# call or invented an id — and invented ids do not group, so the pass-rate the
|
||||||
|
# endpoint computes was being assembled from entries that could not be joined
|
||||||
|
# (#36, found by a pressure test against `debug`).
|
||||||
|
#
|
||||||
|
# Also rejected: `duration_ms: <wall-clock>`. An agent does not observe a clock.
|
||||||
|
# A documented field nobody can honestly populate gets fabricated or silently
|
||||||
|
# dropped, and fabricated numbers in a metrics stream are worse than no numbers.
|
||||||
|
#
|
||||||
|
# Usage: check-session-log [checkout-root]
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
|
||||||
|
fail=0
|
||||||
|
|
||||||
|
for dir in "$ROOT"/skills/*/; do
|
||||||
|
[ -f "$dir/SKILL.md" ] || continue
|
||||||
|
name=$(basename "$dir")
|
||||||
|
|
||||||
|
grep -q 'session_log' "$dir/SKILL.md" || continue
|
||||||
|
|
||||||
|
if ! grep -q 'session_id' "$dir/SKILL.md"; then
|
||||||
|
echo "ERROR: skills/$name documents session_log without session_id — the tool's only required field" >&2
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q 'duration_ms:[[:space:]]*<' "$dir/SKILL.md"; then
|
||||||
|
echo "ERROR: skills/$name documents duration_ms as a placeholder an agent cannot measure — omit it or pass a real measurement" >&2
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $fail
|
||||||
@@ -234,8 +234,8 @@ wrong" — it means "nothing on the known list is wrong."
|
|||||||
Call `session_log` once at the end of every phase to record the outcome.
|
Call `session_log` once at the end of every phase to record the outcome.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "cad-babysit", phase: "<phase-name>", final_status: "pass" |
|
- `session_log` with `{session_id: "<this session's id>", skill: "cad-babysit", phase: "<phase-name>", final_status: "pass" |
|
||||||
"fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root:
|
"fail" | "skip", message: "<one-line summary>", project_root:
|
||||||
"<absolute path>"}`
|
"<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** query, read-comments, read-actions-logs, classify-fault-vs-outcome,
|
**Phases for this skill:** query, read-comments, read-actions-logs, classify-fault-vs-outcome,
|
||||||
@@ -247,12 +247,6 @@ decide
|
|||||||
- `fail` — the phase could not be completed (e.g. `cad stuck` errored, Gitea was unreachable).
|
- `fail` — the phase could not be completed (e.g. `cad stuck` errored, Gitea was unreachable).
|
||||||
- `skip` — phase intentionally skipped (e.g. Phase 3 skipped because Phase 2 was conclusive).
|
- `skip` — phase intentionally skipped (e.g. Phase 3 skipped because Phase 2 was conclusive).
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
Phases 2-4 (reading comments/logs, classifying fault vs outcome) are judgment-heavy and should
|
|
||||||
stay Mode 1. Phase 1 (running `cad stuck` and grouping its output) is mechanical and a Mode 2
|
|
||||||
routing candidate once Plan 6 ships the routing pod.
|
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
- Depends on `mathias/cad#14`'s `cad stuck` query — this skill is the ritual around it, not a
|
- Depends on `mathias/cad#14`'s `cad stuck` query — this skill is the ritual around it, not a
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "code-review", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "code-review", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** phase-0-iron-laws, phase-1-context, phase-2-architectural-analysis, phase-3-go-checklist, phase-4-code-smell-detection, phase-5-test-quality
|
**Phases for this skill:** phase-0-iron-laws, phase-1-context, phase-2-architectural-analysis, phase-3-go-checklist, phase-4-code-smell-detection, phase-5-test-quality
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached.
|
- `fail` — the phase's intended outcome was NOT reached.
|
||||||
- `skip` — phase was skipped intentionally.
|
- `skip` — phase was skipped intentionally.
|
||||||
|
|
||||||
**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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## Common Go Anti-Patterns
|
## Common Go Anti-Patterns
|
||||||
|
|
||||||
@@ -285,7 +285,3 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- Load `refactoring` skill when recommending fixes
|
- Load `refactoring` skill when recommending fixes
|
||||||
- Load `test-design` skill for reviewing test quality
|
- Load `test-design` skill for reviewing test quality
|
||||||
- Load `solid` skill for SOLID principle violations
|
- Load `solid` skill for SOLID principle violations
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
This skill is invoked identically in Mode 1 (cloud Claude) and Mode 2 (client-local with supervisor routing). The routing layer (Plan 6) does not exist yet; treat this skill as Mode 1 only for now. Findings format and severity classification are unchanged across modes.
|
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "debug", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "debug", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** read-failure, generate-hypotheses, output
|
**Phases for this skill:** read-failure, generate-hypotheses, output
|
||||||
|
|
||||||
@@ -163,11 +163,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached.
|
- `fail` — the phase's intended outcome was NOT reached.
|
||||||
- `skip` — phase was skipped intentionally.
|
- `skip` — phase was skipped intentionally.
|
||||||
|
|
||||||
**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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
This skill produces high-volume mechanical output (hypothesis enumeration) and is a candidate for Mode 2 routing to a local model in the future. Until Plan 6 ships the routing pod, treat as Mode 1 only. The hypothesis format and discipline are identical regardless of which model generates them.
|
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ If silhouette ≤ 0.20:
|
|||||||
|
|
||||||
**After experiment concludes:**
|
**After experiment concludes:**
|
||||||
- `brain_write` to `jepa-fx/failures/` with any new failure modes discovered
|
- `brain_write` to `jepa-fx/failures/` with any new failure modes discovered
|
||||||
- `session_log` with outcome
|
- `session_log` with `{session_id: "<this session's id>", skill: "experiment-spec", phase: "conclude", final_status: "pass" | "fail" | "skip", message: "<one-line outcome>"}`
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "feature-spec", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "feature-spec", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** draft
|
**Phases for this skill:** draft
|
||||||
|
|
||||||
@@ -254,11 +254,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached (spec rejected, blocked on unresolved questions).
|
- `fail` — the phase's intended outcome was NOT reached (spec rejected, blocked on unresolved questions).
|
||||||
- `skip` — phase was skipped intentionally.
|
- `skip` — phase was skipped intentionally.
|
||||||
|
|
||||||
**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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
Spec writing requires interpretation of intent and tradeoff reasoning — work where Claude generally outperforms a 9-13B local model. This skill is intentionally **not** a Mode 2 routing candidate. In Mode 2, the routing layer should leave `feature-spec` calls on Claude.
|
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "session-retrospective", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "session-retrospective", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** surfaced
|
**Phases for this skill:** surfaced
|
||||||
|
|
||||||
@@ -125,11 +125,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached (retrospective could not produce coherent candidates).
|
- `fail` — the phase's intended outcome was NOT reached (retrospective could not produce coherent candidates).
|
||||||
- `skip` — phase was skipped intentionally (no novel learnings worth surfacing).
|
- `skip` — phase was skipped intentionally (no novel learnings worth surfacing).
|
||||||
|
|
||||||
**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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
Reading long session logs and pulling themes is high-volume, mechanical pattern-recognition work — a strong Mode 2 routing candidate. Until Plan 6 ships the routing pod, treat as Mode 1 only.
|
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of each phase:**
|
**At end of each phase:**
|
||||||
- `session_log` with `{skill: "spec-driven-dev", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "spec-driven-dev", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** specify, plan, tasks, implement
|
**Phases for this skill:** specify, plan, tasks, implement
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached (gate blocked, rework required).
|
- `fail` — the phase's intended outcome was NOT reached (gate blocked, rework required).
|
||||||
- `skip` — phase was skipped intentionally.
|
- `skip` — phase was skipped intentionally.
|
||||||
|
|
||||||
**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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## Cross-References
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -406,20 +406,20 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of `red` phase:**
|
**At end of `red` phase:**
|
||||||
- `session_log` with `{skill: "tdd", phase: "red", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "tdd", phase: "red", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**At end of `green` phase:**
|
**At end of `green` phase:**
|
||||||
- `session_log` with `{skill: "tdd", phase: "green", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "tdd", phase: "green", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**At end of `refactor` phase:**
|
**At end of `refactor` phase:**
|
||||||
- `session_log` with `{skill: "tdd", phase: "refactor", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "tdd", phase: "refactor", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Status semantics:**
|
**Status semantics:**
|
||||||
- `pass` — the phase's intended outcome was reached (red: test fails as expected; green: test passes; refactor: tests still pass after refactor).
|
- `pass` — the phase's intended outcome was reached (red: test fails as expected; green: test passes; refactor: tests still pass after refactor).
|
||||||
- `fail` — the phase's intended outcome was NOT reached (test compiled when it shouldn't, test still fails after green attempt, refactor broke tests).
|
- `fail` — the phase's intended outcome was NOT reached (test compiled when it shouldn't, test still fails after green attempt, refactor broke tests).
|
||||||
- `skip` — phase was skipped intentionally (e.g. refactor not warranted).
|
- `skip` — phase was skipped intentionally (e.g. refactor not warranted).
|
||||||
|
|
||||||
**Why this matters:** the routing pod (Plan 6) reads pass-rate to decide whether to route a future `tdd` call to a local model. If your skill never logs, the routing pod sees no data and may default-route or default-not-route in a way that doesn't reflect real performance.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a `tdd` call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way, and a decision made on no evidence is a coin flip wearing a metric.
|
||||||
|
|
||||||
## Final Rule
|
## Final Rule
|
||||||
|
|
||||||
@@ -429,7 +429,3 @@ Otherwise → not TDD
|
|||||||
```
|
```
|
||||||
|
|
||||||
No exceptions without Mathias's permission.
|
No exceptions without Mathias's permission.
|
||||||
|
|
||||||
## Mode 2 Routing Note
|
|
||||||
|
|
||||||
This skill is invoked identically whether the agent is running in Mode 1 (cloud Claude, no routing) or Mode 2 (client-local, supervisor routing layer). The routing pod (Plan 6) does not exist yet; until it does, treat this skill as Mode 1 only. The discipline does not change between modes — only the model behind the call.
|
|
||||||
|
|||||||
@@ -231,10 +231,10 @@ Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
|||||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||||
|
|
||||||
**At end of `read` phase:**
|
**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>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "trainer", phase: "read", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**At end of `write` phase:**
|
**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>"}`
|
- `session_log` with `{session_id: "<this session's id>", skill: "trainer", phase: "write", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", project_root: "<absolute path>"}`
|
||||||
|
|
||||||
**Phases for this skill:** read, write
|
**Phases for this skill:** read, write
|
||||||
|
|
||||||
@@ -243,11 +243,7 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
|||||||
- `fail` — the phase's intended outcome was NOT reached.
|
- `fail` — the phase's intended outcome was NOT reached.
|
||||||
- `skip` — phase was skipped intentionally (e.g. no candidates passed the gate, write skipped).
|
- `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.
|
**Why this matters:** pass-rate is what a future routing decision would be based on — whether a given call can be served by a local model instead of a cloud one. A skill that never logs contributes no evidence either way.
|
||||||
|
|
||||||
## 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
|
## Cross-References
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Tests for bin/check-session-log.
|
||||||
|
#
|
||||||
|
# The regression: nine skills documented a session_log payload and not one of
|
||||||
|
# them named `session_id` — the tool's ONLY required field. Every documented
|
||||||
|
# call was incomplete, so an agent following any of them either failed the call
|
||||||
|
# or invented an id, and invented ids do not group. Found by a pressure test
|
||||||
|
# against `debug`, then found to be fleet-wide (#36).
|
||||||
|
#
|
||||||
|
# Also gated: `duration_ms: <wall-clock>`, which an agent cannot measure. A
|
||||||
|
# documented field nobody can honestly populate gets fabricated or silently
|
||||||
|
# dropped; both are worse than omitting it.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
|
SUT="$ROOT/bin/check-session-log"
|
||||||
|
|
||||||
|
pass=0
|
||||||
|
fail=0
|
||||||
|
WORK=${TMPDIR:-/tmp}/check_session_log_test.$$
|
||||||
|
|
||||||
|
if [ ! -f "$SUT" ]; then
|
||||||
|
printf 'FAIL: %s does not exist\n' "$SUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup() { rm -rf "$WORK"; }
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
# fixture <name> <body> — a checkout with one skill whose SKILL.md is <body>.
|
||||||
|
fixture() {
|
||||||
|
_f="$WORK/$1"
|
||||||
|
mkdir -p "$_f/skills/alpha"
|
||||||
|
printf -- '---\nname: alpha\ndescription: a\n---\n\n%s\n' "$2" > "$_f/skills/alpha/SKILL.md"
|
||||||
|
printf '%s' "$_f"
|
||||||
|
}
|
||||||
|
|
||||||
|
# expect <rc> <label> <fixture> [needle]
|
||||||
|
expect() {
|
||||||
|
_want=$1; _label=$2; _fx=$3; _needle=${4:-}
|
||||||
|
_out=$(sh "$SUT" "$_fx" 2>&1); _rc=$?
|
||||||
|
if [ "$_rc" -ne "$_want" ]; then
|
||||||
|
fail=$((fail + 1)); printf ' FAIL %s (want rc=%s got %s)\n%s\n' "$_label" "$_want" "$_rc" "$_out"; return
|
||||||
|
fi
|
||||||
|
if [ -n "$_needle" ] && ! printf '%s' "$_out" | grep -q "$_needle"; then
|
||||||
|
fail=$((fail + 1)); printf ' FAIL %s (rc ok, report never says %s)\n%s\n' "$_label" "$_needle" "$_out"; return
|
||||||
|
fi
|
||||||
|
pass=$((pass + 1)); printf ' ok %s\n' "$_label"
|
||||||
|
}
|
||||||
|
|
||||||
|
expect 0 'a skill that never mentions session_log is fine' \
|
||||||
|
"$(fixture quiet 'Just a skill. No logging here.')"
|
||||||
|
|
||||||
|
expect 0 'a documented call naming session_id passes' \
|
||||||
|
"$(fixture good 'Call `session_log` with `{session_id: "<id>", skill: "alpha", phase: "red", final_status: "pass"}`')"
|
||||||
|
|
||||||
|
expect 1 'a documented call omitting session_id fails' \
|
||||||
|
"$(fixture bad 'Call `session_log` with `{skill: "alpha", phase: "red", final_status: "pass"}`')" \
|
||||||
|
'session_id'
|
||||||
|
|
||||||
|
expect 1 'documenting duration_ms as wall-clock fails' \
|
||||||
|
"$(fixture clock 'Call `session_log` with `{session_id: "<id>", skill: "alpha", duration_ms: <wall-clock>}`')" \
|
||||||
|
'duration_ms'
|
||||||
|
|
||||||
|
printf '\ncheck_session_log: %d passed, %d failed\n' "$pass" "$fail"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
Reference in New Issue
Block a user