npx skills add ...
npx skills add microsoft/apm --skill docs-impact-classifier
npx skills add microsoft/apm --skill docs-impact-classifier
Use this skill to classify the documentation impact of a pull request diff, returning one of three verdicts -- no-change, in-place edit, or structural change -- with bounded LLM cost. Activate as a sibling skill of docs-sync; the orchestrator calls this first, before any panel spawn, to keep cost floor at 1 LLM call when no docs work is needed. Reads .apm/docs-index.yml as the corpus map; never reads the full corpus.
Single responsibility: given a PR diff and the .apm/docs-index.yml
corpus map, emit ONE classification verdict.
This skill is the cost gate for the entire docs-sync system. ~70% of
PRs should exit at verdict no_change with zero panel spawn.
This is a 3-layer funnel inside a single skill invocation:
The skill returns the verdict from the earliest layer that can decide.
Read .apm/docs-index.yml to load no_impact_paths[] and
user_surface_paths[]. Get the changed file list from the PR diff
(gh pr diff --name-only).
This handles:
tests/**).github/workflows/**)docs/**) -- out of scope, docs-sync doesn't review docs PRs.apm/**)Expected hit rate: ~70% of PRs short-circuit here.
If L0 did not exit, extract user-observable symbols from the diff:
^@click.command, ^@cli.command, or any apm <verb> mention in added/removed lines.^@click.option, --[a-z-]+ patterns.def <name> in src/apm_cli/__init__.py or src/apm_cli/api/**.apm.yml, apm.lock.yaml, apm-policy.yml parsers._rich_error, click.echo, raise ... Error().For each extracted symbol, consult .apm/docs-index.yml#symbol_index
to find the documented pages. Collect all hits into candidate_pages[].
Also grep -rn <symbol> docs/src/content/docs/ for symbols NOT in
the index (catches drift between index and corpus).
If L1 found zero candidate pages AND zero schema/CLI/flag changes:
return {verdict: "no_change", confidence: "medium", source: "L1", scope_pages: []}.
Otherwise, invoke the doc-analyser persona with EXACTLY this context envelope (must fit in ~8 KB tokens):
gh pr diff --stat output).apm/docs-index.yml (the whole file; it's ~8 KB seeded, may grow)pr_doc_diff_paths[]: the list of paths under docs/src/content/docs/**
that the PR itself already modifies (drives the in_place_resolved
downgrade rule in "In-place-resolved detection" below).Ask doc-analyser to return JSON matching this schema:
structural_proposal is populated only when verdict is structural.
scope_pages is populated for in_place and structural verdicts.
| Verdict | Meaning | Panel size | Cost |
|---|---|---|---|
no_change | No user-observable surface changed | 0 panel spawns | ~0-1 LLM call |
in_place_resolved | Doc impact existed, but the PR's OWN diff already patches every page in scope_pages -- author already did the work | 0 panel spawns; skill emits NO advisory | ~1 LLM call |
in_place | One to a few pages need a paragraph or section update; no new pages, no TOC change | N candidate pages x (doc-writer + python-architect) + editorial-owner + growth-hacker + CDO | ~6-12 LLM calls |
structural | A new page is needed, OR an existing page should be split/merged, OR the TOC needs to change to fit a new concept | architect first (TOC delta), then in-place panel for affected pages | ~10-15 LLM calls |
BEFORE returning in_place, intersect your scope_pages[] with the
list of files the PR itself touches under docs/** (provided to you
by the orchestrator under pr_doc_diff_paths[]). If EVERY scope page
already appears in pr_doc_diff_paths, downgrade to in_place_resolved
and emit reasoning of the form "Author already patched ".
This is the well-behaved-author path; the skill stays silent.
If only SOME scope pages are pre-patched, keep in_place and list the
REMAINING (unpatched) pages in scope_pages[]. Note the pre-patched
ones in reasoning for transparency.
When the L1 layer reports an ADDED public symbol that matches an
EXISTING public symbol's name in the corpus (e.g. PR adds apm update
but apm update already appears in 9 docs pages with different
semantics), this is a RENAME or BREAKING SEMANTIC CHANGE. Bias toward
structural (not in_place):
Do NOT collapse a rename into in_place just because the affected
pages already exist. The shape of the work is structural even when no
new page is strictly required.
in_place with empty scope_pages -- invalid; orchestrator will reject.structural without structural_proposal -- invalid.in_place when EVERY scope page is in pr_doc_diff_paths -- should be in_place_resolved.structural to seem thorough -- the CDO will catch this. Return the minimal true verdict.in_place for a verb-swap PR.Return a SINGLE JSON document matching the schema in Step 3 as the final message of your task. No prose around the JSON. The orchestrator parses your last message.