Skip to main content

test-skill

Validate a drafted skill — write its trigger fixtures, update the activation contract table, run the static and real-path activation tests, then behaviourally test it in a cold session against a `--safe-mode` baseline. Reads its inputs from disk — brief or shipped frontmatter — so it runs cold. Encodes the traps that make a broken test look like a broken glob: activation is keyed to the `Read` tool so a Bash `cat` activates nothing, it is a per-session cumulative delta so a silent second match is deduplication not failure, the transcript and the stream-json `commands_changed` record are the only witnesses (`skills-invoked.log` and `--debug-file` cannot see it), and `-SkillGroups` prunes user scope so the workflow-only prune must be restored afterwards. Skills only — rules, subagents and hooks keep the manual procedure in root CLAUDE.md.

Ir para a instalação

Informações da origem

Repositório
wardawgmalvicious/agent-config
Última atividade na origem
14 de setembro de 2026 às 23:54
Idioma detectado do SKILL.md
inglês
Estrelas
1
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
2 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
test-skill
description
Validate a drafted skill — write its trigger fixtures, update the activation contract table, run the static and real-path activation tests, then behaviourally test it in a cold session against a `--safe-mode` baseline. Reads its inputs from disk — brief or shipped frontmatter — so it runs cold. Encodes the traps that make a broken test look like a broken glob: activation is keyed to the `Read` tool so a Bash `cat` activates nothing, it is a per-session cumulative delta so a silent second match is deduplication not failure, the transcript and the stream-json `commands_changed` record are the only witnesses (`skills-invoked.log` and `--debug-file` cannot see it), and `-SkillGroups` prunes user scope so the workflow-only prune must be restored afterwards. Skills only — rules, subagents and hooks keep the manual procedure in root CLAUDE.md.
when_to_use
Use when asked to test, validate or verify a skill, to check whether a `paths:` glob fires, after editing a `description`, `when_to_use` or `paths:` glob, or as the follow-on to `/author-skill`.
argument-hint
[skill-name]
disable-model-invocation
false
model
inherit
effort
max
# Test a skill Take a **drafted skill** and end at a validated one: its activation contract written and passing, its behaviour checked in a cold session, and the fixtures it ran against provably unmodified. This is the second half of `/author-skill`, which stops at a linted draft and writes no fixtures on purpose. The coupling between them is the **brief on disk**, not session state — so this skill runs cold, in a fresh session, exactly like `/drift-update`. It also runs on a skill that shipped long ago and has since been edited. That case has **no brief** and needs none; step 1 says where its inputs come from instead. Repo-relative paths are relative to the agent-config repo (`C:\Repos\Personal\agent-config`), not the session's cwd. ## What this validates, and what it does not **Skills only** — the same scope as `author-skill`. Rules get exercised incidentally, because the activation harness checks `claude/rules/*.md` alongside skills on the same fixtures, but authoring rule fixtures is not this skill's job. Out of scope, with their procedures elsewhere: | Artifact | Procedure lives in | | --- | --- | | Subagents | `tests/agents/security-reviewer/README.md` | | Enforcement hooks | same, plus the direct unit-test pattern in root `CLAUDE.md` | | Rules, as authored artifacts | root `CLAUDE.md`, "Validating a change" | ## Phase A — the activation contract Skip this phase entirely if the skill has **no `paths:` glob**. An unconditional skill has no activation contract, so there is nothing to fixture and nothing to assert. Say so and go to Phase B; do not manufacture a fixture to make the phase look done. ### 1. Read the trigger contract from disk Three entry paths, and they take their inputs from different files. Establish which one you are on before reading anything. **A newly drafted skill, arriving from `/author-skill`:** ``` docs/handoffs/execute/<skill-name>.md ``` Take from it the `paths:` glob, the named trigger queries, and the scope decisions. **Read it from the file even if you wrote it an hour ago** — the point of the disk contract is that a fresh session with no memory of the authoring run behaves identically. **An existing skill being retested after a `description`, `when_to_use` or `paths:` edit:** there is no brief, and its absence is not a fault. Briefs are deleted when spent, so a skill that shipped has none by design — stopping here would refuse the exact case the description advertises. Take the three inputs from the artifacts that already own them: | Input | Source | | --- | --- | | `paths:` glob | the `SKILL.md` itself — always authoritative, brief or no brief | | scope decisions | the set's `expected_activations.md`, which **is** the committed contract | | trigger queries | the skill's own `description` and `when_to_use` | That last row is sound rather than a fallback: `when_to_use` is defined upstream as "trigger phrases or example requests", so on a skill that carries one the queries are a first-class frontmatter field, not something to invent. Where a skill has no `when_to_use`, draw the queries from the `description` and **say in the report which ones you derived** — a derived query tests the skill against the trigger surface it actually ships, which is the point, but the reader needs to know no brief vouched for it. **An executed drift brief in `docs/audits/<date>/<source>/`, whose execution log deferred behavioural confirmation to a fresh session:** `/drift-update` cannot exercise a skill it just edited, so its log ends with the deferral and this skill is the follow-on. Inputs come from the shipped artifacts exactly as in the row above — it is not an `/author-skill` brief and names no trigger queries — but its *What to change* section names the one thing the edit added, and **that is the discriminating claim for Phase B**: the detail only the skill makes. The brief is a ledger entry and is never deleted (step 10). First run 2026-09-12 on `fabric-semantic-model-ai-instructions` from brief 05. Such a brief is a **content** edit by construction — Kind says so and the `paths:` glob is untouched — so the activation stamp stands and **Phase A needs only its static half**. `skill-status.py` says `retest-activation` when a glob really moved; trust it over re-running. The one thing you may never do is invent expectations the skill was never written to meet. Reading them off the shipped frontmatter is not that; making them up is. ### 2. Pick the fixture set The glob decides it: | Glob targets | Set | | --- | --- | | Fabric item folders (`**/*.DataPipeline/**`, `.Notebook`, `.Eventhouse`, …) | `tests/skills/fabric-triggers/` | | PBIP / report / semantic-model paths | `tests/skills/pbip-triggers/` | The two sets are **disjoint and jointly exhaustive** over the payload's conditional skills. Don't restate the count here — it was duplicated into six files, checked by nothing, and had drifted three different ways by 2026-09-02, one of them two generations stale. Each set's `expected_activations.md` owns its own figure; derive the total when you actually need it: ```powershell ./scripts/test-activation.ps1 -Set fabric -StaticOnly # then -Set pbip ``` A skill whose glob spans both sets is a design smell — raise it rather than splitting fixtures across sets. ### 3. Write or extend the fixtures Add files under that set's `fixtures/` directory, modelled on real exports. Keep them minimal but structurally faithful: the test asserts *which globs match*, so a file needs the right path and enough content to be plausible, not real data. **Mark any fixture built on an unverified shape.** The fabric set's README has a dedicated section for these, and a fixture invented from a guess will happily pass a test that asserts nothing true. ### 4. Update `expected_activations.md` One row per fixture file, naming the skills that must fire. This is the **contract**, and the static check compares globs against it in both directions — a skill that fires and is not listed fails just as loudly as one listed and not firing. **A row that reads *(none)* and should now name your skill is the assertion being changed.** Say that explicitly in the commit message, because the diff on its own looks like a table edit rather than a retired negative assertion. Check whether the set's prose sections still hold too. The fabric set carries a "Fabric item types with no skill at all" section that names examples; a new skill can make one of them stale. ### 5. Static check — always, before spending a session ```powershell ./scripts/test-activation.ps1 -Set fabric -StaticOnly ``` No session, no tokens, no deploy. It compares frontmatter globs against the contract table and exits non-zero on any mismatch. `-Set` is mandatory and takes `pbip` or `fabric`. You cannot skip it by accident — the full run executes it first and refuses to continue past a failure — but run it alone while iterating, because it is the whole feedback loop for steps 3 and 4. **Run the rules pass too.** Rules carry `paths:` globs and load on the same files, so a fixture with no *skill* may still pull a rule. Doing only the skills pass is how the first version of the fabric set reported "activates nothing" for files that load `fabric-git-serialization`. The snippet is in `tests/skills/fabric-triggers/README.md` — same code, `claude/rules/*.md` instead of `skills/*/*/SKILL.md`, keyed on `p.stem`. ### 6. Real-path test — does the harness agree? ```powershell ./scripts/test-activation.ps1 -Set fabric ``` Deploys to a throwaway probe outside the repo, opens **one** cold session, has it Read every fixture, asserts the transcript, and tears down in a `finally`. **One session covers the whole set** — activation is a per-session cumulative delta, so 56 fixtures cost one session rather than 56. That is what makes this affordable enough to actually run. **Skip this step when the activation stamp is already current.** The stamp hashes `paths:` alone, so on a retest after a body or `description` edit `scripts/skill-status.py` still rates activation current and a cold session would re-prove an unchanged fact; the static check in step 5 is the whole regression, and the Phase B probe witnesses activation anyway because a conditional skill is unreachable until a matching file is Read. First applied 2026-09-12 on `fabric-semantic-model-ai-instructions` — activation stamped 2026-09-01, static 16/16, no session spent. The script already refuses the dangerous shapes, so do not re-implement guards around it: it rejects a `ProbeRoot` inside this repo, refuses user scope as a deploy target, and will not reuse a directory lacking its `.activation-probe` marker. Other parameters: `-ProbeRoot`, `-Model` (default `opus[1m]`), `-KeepProbe` to leave the probe for inspection. ## Phase B — behaviour ### 7. Deploy the groups — and restore the prune **This is the one step that can damage the machine.** Everything else is confined to `tests/` and a throwaway directory; this writes to `~/.claude/skills`, which serves every session here. Only a platform skill needs it — `workflow` and `social` are deployed already and `.claude/skills/` is read in place — so skip to step 8 unless the skill is **new**, which has no junction until the standing form below runs once. ```powershell ./scripts/link-claude.ps1 -SkillGroups workflow,social,fabric # or workflow,social,powerbi ``` `-SkillGroups` **prunes** — a group not listed is removed. This machine's standing state is workflow and social only, so you are temporarily undoing a deliberate prune and must put it back: ```powershell ./scripts/link-claude.ps1 -SkillGroups workflow,social ls ~/.claude/skills | Select-String '^(fabric|pbir|pbid)-' # must return nothing ``` **Never run the script bare.** Omitting `-SkillGroups` deploys every group and silently undoes the prune — it happened on 2026-08-31, and the run reported `Linked` 37 times and ended `Done. All links verified.` There is no output line that reads as wrong. The `ls` above is the only check that catches it. **Don't read the exit code as the verdict.** `link-claude.ps1` returns non-zero whenever any warning fires, and the standing `MCP_DOCKER` drift on this machine means a wholly successful deploy *and* a successful restore both exit 1. Read the `Skills N linked ...; M pruned` line and the `ls` above instead. ### 8. The cold behavioural session **Cold, always.** Skills hot-reload, but subagents, commands and rules do not, and accumulated context can mask a co-load failure. Establish the baseline first: ```bash claude --safe-mode ``` That starts with the entire payload off — `CLAUDE.md`, skills, hooks, MCP, commands, agents — and is the control condition that separates behaviour the payload produces from behaviour the base model produces. It is a flag you type, never something to wire into `settings.json`. **A close baseline is not a failed skill.** Where the base model already knows the domain, `--safe-mode` reproduces most of a good answer, so "the payload run answered well" measures nothing by itself. Compare a detail that is a claim **only the skill makes**. Measured 2026-09-04 on `pbir-filters`: both runs got `SourceRef.Source` and doubled quotes; only the payload wrote the skill's 20-char hex `name` (baseline: a 32-char GUID). **`--safe-mode` does not strip the web tools.** A baseline can fetch the Learn pages a skill was drilled from and re-derive it — a second reason it comes back close. Add `--disallowedTools WebFetch,WebSearch` **to both runs**, the payload side too, or a pass cannot separate "the skill delivered it" from "the model fetched the page the skill cites"; or compare on synthesis that sits on **no single page**. Measured 2026-09-12 on `fabric-deployment-pipelines` (`references/reading-a-failure.md`). **Those two flags do not turn the web off on this machine, and the gap is one-sided.** `microsoft-learn-mcp` is **user scope**, so a payload probe reaches Learn through it whatever `--disallowedTools` says, while `--safe-mode` strips MCP with the payload and the baseline cannot — leaving the very confound the flags were meant to remove. Add `--strict-mcp-config` to the payload arm (with no `--mcp-config` it drops every server) and check the `init` record's `tools` count: 29 with the servers, 22 without, matching the baseline. Measured 2026-09-13: `fabric-eventstream`'s first payload run blocked both web tools and still fetched all four drilled Learn pages. **Prove the baseline actually stripped the payload.** A `--safe-mode` run that silently kept the skill is indistinguishable from one where the base model already knew the answer — both read as "the skill adds nothing". In a `-p` probe the `system`/`init` record names `slash_commands` and `tools`: assert the skill is absent from it and that the count dropped. Measured 2026-09-12 on `fabric-catalog-governance` — 21 commands with the skill absent against 33 with it present — which is what made "the baseline reproduced this finding unaided" a result rather than a guess. **Assert on a name only the payload provides.** `code-review` stays listed under `--safe-mode` because the CLI ships a built-in of that name, so a grep for it reads as a leaked payload — and when `code-review` itself is under test, its absence cannot be read off the list at all; assert its sibling payload names instead (measured 2026-09-13, `references/reading-a-failure.md`). Then, in a normal session, run the trigger queries from step 1. Test **both** invocation paths, because they do not behave alike: a `model:` pin is honoured on `/slash` invocation and silently dropped on model-invocation, while `effort:` applies on both. Where the skill has refusal behaviour, exercise the refusal modes as well — a skill that does the right thing but ignores its own scope guard has failed. `tests/skills/code-review/README.md` has the four-mode matrix (slash review, NL review, slash adversarial, NL adversarial) to copy from. **Disable write tools when a trigger query names a destructive action.** A behavioural probe runs with this machine's credentials — an `az login` survives across tool calls — so "Delete the Marketing domain" or a bulk label write can reach a real tenant, and the skill's own refusal is the only thing in the way. Don't rely on it: pass `--disallowedTools "Bash,PowerShell,Monitor,Agent,Edit,Write,NotebookEdit"` plus `--strict-mcp-config`. The shell tools are not the only way out — `Monitor` runs its `command` in Bash's own shell environment, `Agent` spawns a subagent with its own tools, and the user-scope `MCP_DOCKER` gateway re-exports `merge_pull_request` and `push_files` — and two `land` probes run with the shorter list proved the point by declining to abuse `Monitor` unprompted (`references/reading-a-failure.md`). Added 2026-09-12, after `fabric-catalog-governance`'s own brief supplied both of those queries. **What is measured is what the skill *says*, not whether it can call an API** — free on a query that asks for an explanation, and a real cost on one that asks the skill to *act*: it stops at the first command it cannot issue, and every later step goes unmeasured on that arm. Phrase one arm as a walkthrough ("the exact commands, start to finish"); `land`, 2026-09-13, is the measurement. **Give a trigger query enough context to be answerable.** A bare imperative in an empty probe directory routes to file exploration rather than to a skill: "Deploy my data pipeline to production." sent the session hunting for a pipeline on disk and loaded nothing, while the same sentence framed with a dev workspace reached the skill. The first measures the harness's file-hunting instinct, not the trigger surface. Measured 2026-09-12 on `fabric-deployment-pipelines`. **A conditional skill has neither path until a matching file is Read.** The `paths:` glob keeps it out of the startup listing, so its `description` — the whole model-invocation trigger — is never in context, and `/<name>` answers `Unknown command`. Read a matching file first; that injects the listing entry and the model can then invoke it. The four-mode matrix above applies as written only to an *unconditional* skill. Measured 2026-09-02 on 2.1.252: `/fabric-data-pipeline` was `Unknown command` while `/fabric-gotchas` — same session shape, no `paths:` — ran normally. **Run the behavioural session outside this repo — for a platform skill.** `.claude/settings.json` here collapses every platform skill description to `name-only`, and the description *is* the trigger — so an in-repo run is a guaranteed false negative that looks exactly like a broken skill. **A project-scope skill must run here**, where the payload is on disk and root `CLAUDE.md` duplicates it: disallow the file tools (`Read,Glob,Grep,ToolSearch`) on both arms, then add a third arm with `Skill` disallowed too — **model-invoked, never slash**, or the expansion inlines the body straight past the denial and the arm is inert. `references/reading-a-failure.md`, 2026-09-13. Confirm the skill actually loaded with `/context` rather than by asking the session — self-report is unreliable, and once omitted an unconditional skill that was certainly present. In a `-p` probe where `/context` is unavailable, use the transcript: a model-invoked skill appears as a `Skill` tool_use, while a slash-invoked one is **inlined as a command expansion** and produces no `Skill` call — so an absent `Skill` record disproves nothing on the slash path. The positive witness is the pair: run the slash probe on the query the NL arm answered through a `Skill` call, and a slash run with **no** `Skill` call that still carries the skill's own detail has proved the expansion — without it the model would have had to call `Skill` as the NL run did. Use a query the skill answers itself: on one it delegates, the only `Skill` call is the delegate's and the run reads as ambiguous. Measured 2026-09-13 on `fabric-cli` — NL 3 turns with `Skill fabric-cli`, slash 1 turn with none, the same GUID-vs-friendly-name table in both; the first slash run, on a section that hands off to `fabric-deployment-pipelines`, showed only that skill's call. `--output-format stream-json --verbose` is the cheaper route to those records — it carries the `tool_use` blocks, the `init` record and, for a conditional skill, the `commands_changed` record that witnesses the matching `Read` (`references/reading-a-failure.md`) inline, so nothing has to locate a session id under `~/.claude/projects/`. Read the answers rather than grepping them for expected phrases: on 2026-09-12 an `/owns|owned/` scan missed "items you own" and nearly recorded a passing assertion as a failure. **Launch a slash probe from PowerShell**, not the Bash tool. MSYS2 rewrites a leading-slash argument to `C:/Program Files/Git/<name>`, so `claude -p "/my-skill ..."` never reaches the slash path — and the failure is invisible, because the model reads the mangled text, still recognises the skill name, and invokes it via the Skill tool. The run then looks like a passing slash test while measuring model-invocation. `MSYS2_ARG_CONV_EXCL='*'` works too. Measured 2026-09-02. ### 9. Confirm the fixtures are unmodified ```bash git status ``` Expect no modifications. Fixtures are read-only by validation contract; a run that edits its own inputs invalidates every later comparison. If one changed, revert it and find out which mode did it. ### 10. Stamp, retire the brief, report and hand off **Stamp the run first**, so the record does not depend on this session's report ever being read: ```bash uv run --with pyyaml scripts/skill-status.py --stamp <skill-name> --phase activation,behaviour ``` Name only the phases that ran: an unconditional skill stamps `behaviour` alone, and the script refuses `activation` for it with the same reason Phase A was skipped. The stamp hashes what each phase tested from the **working tree** — which is what this skill ran against — and `scripts/skill-status.py` derives from it whether a later edit needs a retest. Nothing else records a test, so a run without a stamp did not happen as far as the next session can tell. **The stamp is a working-tree write that another live session's commit can discard**, staged or not — it happened twice on 2026-09-12 before a re-stamp chained straight into `git add` and `git commit` landed. When a second session is live in this tree, hand `/commit` the stamp command to re-run immediately before staging, and re-check the verdict after the
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub