Files
dotnet__skills/CONTRIBUTING.md
Abhitej John ca1b47371f Add automated per-plugin versioning (NBGV) with /version-bump + weekly backstop (#813)
* Add automated per-plugin versioning (NBGV) with /version-bump + weekly backstop

WHY
Tools that surface skills (Copilot CLI, Claude Code, Codex, Cursor) read a
plugin's version directly from its checked-in manifest. With no versioning
discipline, a plugin's behavior can change while its advertised version stays
flat, so clients never learn to re-pull, and there is no human-readable signal
of what changed. We want correct, current versions in the repo with minimal
manual work and without bloating the marketplace clone.

WHAT
- Per-plugin semantic versioning via Nerdbank.GitVersioning (NBGV). Each plugin
  owns a version.json whose pathFilters exclude the generated manifests and the
  version.json itself, so version height tracks real content changes only.
- The computed version is materialized into the checked-in manifests
  (plugin.json and .codex-plugin/plugin.json) so every consumer reads a current
  value with no build step on their side.
- eng/version/Sync-PluginVersions.ps1 is the single workhorse. It resolves the
  set of changed plugins from a git diff, computes each version with nbgv
  (predicting the squash-merge height for PRs), and either reports or stamps.

AUTOMATIONS (two, low-touch by design)
- /version-bump: an admin/maintainer comments the command on a PR and the
  affected plugins are stamped on the PR branch. Gated on collaborator
  permission (admin/write/maintain); forks are rejected before any privileged
  step. No other PRs are auto-modified.
- weekly-version-sync: a Monday backstop (and workflow_dispatch) that stamps any
  drift on main, opens/updates a single bot PR, and explains the per-plugin
  reason. This self-heals anything that merged without a bump.

We deliberately did NOT auto-edit contributor PRs or add a noisy advisory
comment bot; maintainers stay in control and the signal stays clean.

SECURITY (multi-model adversarial review: GPT-5.5 + Gemini 3.1 Pro)
- Supply chain (High, both models): dotnet tool restore would have honored a
  nuget.config authored in the PR tree, letting an attacker remap the nbgv
  package source to a malicious feed and run code in the privileged
  contents:write context. Mitigated with a trusted eng/version/nuget.config
  (clear + nuget.org-only + packageSourceMapping), overlaid from main and used
  via --configfile so PR-supplied configs are ignored. No nuget.config is
  tracked in the repo today, so this path was genuinely exploitable.
- TOCTOU (Medium): /version-bump now checks out the authorized head SHA rather
  than the mutable branch name; a racing push fails non-fast-forward, which is
  the safe outcome.
- Injection: Set-ManifestVersion uses a MatchEvaluator (not a replacement
  string) so a "$"-bearing version cannot re-expand, plus a strict
  major.minor.patch guard that throws on a malformed base, leaving manifests
  untouched.
- A base-only version.json bump (0.1 -> 0.2) is correctly detected and stamped.

VERIFIED
End-to-end against a real NBGV git harness: content-scoped predict, base-only
bump -> x.y.0, docs-only -> [], weekly drift stamping, malformed-base guard,
and --configfile restore (exit 0). actionlint passes on both workflows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address Copilot review feedback

- Add missing plugins/dotnet-test-migration/version.json so it participates
  in versioning (it was the only plugin without one; manifests are at 0.1.0).
- CONTRIBUTING: the two manifests are not byte-identical; say the version is
  duplicated across two manifest files instead.
- weekly-version-sync: include version.json in commit attribution so a
  base-only bump is explained rather than showing 'no attributable commits'.
- Get-NbgvInfo: capture nbgv stderr and include it in the thrown error so CI
  failures are diagnosable, while keeping stdout clean for JSON parsing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-13 10:07:45 -07:00

364 lines
17 KiB
Markdown

# Contributing
Thanks for your interest in contributing. We expect to accept external contributions, but the bar for merging is intentionally high.
This repository contains shared building blocks for coding agents:
- Skills: reusable, task focused instruction packs
- Agents: role based configurations that bundle tool expectations and skill selection
Because these artifacts can affect many users and workflows, we prioritize correctness, clarity, and long term maintainability over speed.
## Code ownership
Every plugin, skill, and agent must have designated owners in the `.github/CODEOWNERS` file. When you add a new skill or agent, add a matching CODEOWNERS entry. Ownership must be either:
- **Two or more FTE GitHub aliases** (e.g., `@user1 @user2`), or
- **A GitHub team alias** (e.g., `@dotnet/my-team`)
This ensures that every contribution area has accountable reviewers and that PRs are automatically routed to the right people.
## Repository layout
```text
plugins/
<plugin>/
plugin.json
skills/
<skill-name>/
SKILL.md
scripts/
references/
assets/
agents/
<agent-name>.agent.md
tests/
<plugin>/
<skill-name>/
eval.yaml
<fixture files>
```
Every plugin must have a plugin.json file in the plugin root that is linked to from the marketplace.json file.
### Plugin organization
Skills are grouped into domain-specific plugins. When proposing a new skill, place it in the plugin that best matches its domain. See [README.md](README.md) for the current list of plugins.
If your skill does not fit any existing plugin, consider creating a new one.
To create a new plugin:
1. Add `plugins/<plugin-name>/plugin.json` and a `skills/` directory beneath it.
2. Add a matching entry in `.github/plugin/marketplace.json`, `.claude-plugin/marketplace.json`, `.cursor-plugin/marketplace.json`, and `.agents/plugins/marketplace.json`. Keep plugin entries consistent across all marketplace manifests (including `plugins[].source` format) to reduce drift and make future updates safer.
Also add a `plugins/<plugin-name>/version.json` (copy an existing one) so the plugin participates in automated versioning. Start its `plugin.json` version at `0.1.0`.
3. Add a CODEOWNERS entry for the new plugin and its tests (see [Code ownership](#code-ownership)).
4. Add the plugin to the **What's Included** table in the root `README.md`.
5. Create a `tests/<plugin-name>/` directory for skill tests.
See existing plugins for the expected format.
### The `dotnet-experimental` plugin
Use `dotnet-experimental` when you want to try out a skill idea but are not yet confident it belongs in a stable plugin — for example, when the skill is outside your usual area of responsibility, the approach is unproven, or you want community feedback before committing to a long-term home.
Skills in `dotnet-experimental`:
- May change, be reworked, or be removed without notice.
- Are held to the same quality and testing standards as any other skill (frontmatter, `eval.yaml`, etc.).
- Should eventually graduate to a stable plugin or be retired. When a skill has proven itself, move it to the appropriate domain plugin and update tests accordingly.
Place experimental skills under `plugins/dotnet-experimental/skills/` with matching tests in `tests/dotnet-experimental/`.
## Plugin versioning
Each plugin is versioned independently. The same version is duplicated across every manifest a
consumer reads: `plugins/<plugin>/plugin.json` and `plugins/<plugin>/.codex-plugin/plugin.json`
(both present for every plugin), plus an optional `plugins/<plugin>/.claude-plugin/plugin.json`
that only plugins needing an inline Claude manifest carry (e.g. `dotnet-msbuild`'s binlog MCP
server). Consumers (Copilot CLI, Claude, Codex, Cursor) read the version directly from this
repository.
Versioning is automated with [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning).
A per-plugin `plugins/<plugin>/version.json` scopes the git height to that plugin's subtree, so the
**patch** number is derived from history — you do not edit it by hand. The generated manifests
(`plugin.json`, `.codex-plugin/plugin.json`, and `.claude-plugin/plugin.json` where present) and
`version.json` itself are excluded from that height via the `pathFilters`, so editing only manifest
metadata (anything other than a deliberate base bump in `version.json`) does **not** change the patch
number and is **not** picked up by `/version-bump` or the weekly sync. Touch a skill or other plugin
content to bump the version.
What this means when you contribute:
- **Every plugin carries its own `plugins/<plugin>/version.json`.** It declares the plugin's version
base; the weekly sync fails fast if a plugin ships a `plugin.json` without one, so none is ever
left unversioned.
- **Don't hand-edit the `version` field** in any of the manifests (`plugin.json`,
`.codex-plugin/plugin.json`, or `.claude-plugin/plugin.json`). The patch number is computed and
stamped automatically, and a manual edit will be overwritten.
- **The only version field you may change is the base** (`"version"`) in `plugins/<plugin>/version.json`,
and only to declare a deliberate **minor or major** release of that plugin (e.g. `0.1` → `0.2` or `1.0`).
Changing the base resets the patch number to `0`.
- After a PR changes a plugin's content, bumping its version is optional:
- A maintainer can comment **`/version-bump`** on a same-repo PR to stamp the new version onto the branch.
- Otherwise the **weekly version sync** opens a PR that stamps any plugin whose content changed without a
version bump, explaining each change. Nothing is ever missed.
Patch numbers are predicted from git history, so two PRs bumped concurrently can land the same patch
number for a plugin; the weekly sync recomputes the authoritative height on `main` and reconciles any
collision. Version-only changes do not trigger skill evaluations.
## Before you start
- Search existing issues and pull requests to avoid duplicates.
- Start with an issue before you submit a pull request for a new skill, a new agent, or any non trivial change. This helps us align on scope and avoids wasted work.
- Small fixes like typos, broken links, or clearly isolated corrections can go straight to a pull request.
- Keep changes small and focused. One skill or one agent per pull request is a good default.
## What we look for
We are most likely to accept contributions that are:
- Addresses a LLM gap and is clearly motivated by a real use case
- Likely to be used frequently and is general (not repo-specific)
- Narrow in scope and easy to review
- Tool conscious and explicit about assumptions
- Verifiable with concrete validation steps
- Written to be durable across repo changes
We are less likely to accept contributions that:
- Add broad frameworks, meta tooling, or large reorganizations
- Duplicate guidance that already exists in another skill
- Encode private environment details, credentials, or company specific secrets
- Depend on proprietary tools or access that most contributors will not have
- Skills that make use of third party tools will be evaluated on a case by case basis. Acceptance of such skills will depend on our evaluation of the provenance and maturity of any such tools.
## Proposing a new skill
Please review the **What we look for** section and add justification for the skill in your issue and PR.
A skill should be self-contained and:
- Clearly state **what it does** and **when to use it**.
- Frontmatter (name and description) is small and minimal, just enough for LLM to understand when to use it
- Keep the SKILL.md body under 500 lines for optimal performance. Split content into separate files when you approach this limit. Use a progressive disclosure pattern, referring to those files from the SKILL.md file where needed.
- Specify required inputs (repo context, environment, access needs).
- Prefer concrete checklists and verification steps over vague guidance.
Create a new folder under a plugin's `skills/` directory:
```text
plugins/<plugin>/skills/<skill-name>/SKILL.md
```
A skill should answer three questions up front:
1. What outcome does the skill produce
2. When should an agent use it
3. How does the agent validate success
### Skill naming
Use short, kebab-case names that mirror how developers naturally phrase the task, prioritizing keyword overlap over grammar — e.g., add-aspnet-auth, configure-jwt-auth, setup-identity-server. Optionally using gerund style (verb-ing) is acceptable as well - e.g., configuring-caching.
Optimize for intent matching: lead with the action verb users actually say (add, configure, setup, deploy) followed the outcome the skill is aiming to assist.
The `SKILL.md` is required to have front-matter at a minimum:
Create the file with required YAML frontmatter:
```yaml
---
name: <skill-name>
description: <description of what the skill does, when to use it, and when not to use it>
---
```
> **Tip:** The `description` field is used by the agent runtime to decide whether to load the full skill.
> Include **when to use** and **when not to use** guidance directly in the description so the agent can
> select or skip skills without reading the entire `SKILL.md`. This avoids unnecessary token usage.
> See [`thread-abort-migration/SKILL.md`](plugins/dotnet-upgrade/skills/thread-abort-migration/SKILL.md) for a good example.
### Recommended `SKILL.md` sections
- **Purpose**: one paragraph describing the outcome.
- **When to use** / **When not to use** (put the essentials in the frontmatter `description`; expand here only if more detail is needed).
- **Inputs**: what the agent needs (files, commands, permissions).
- **Workflow**: numbered steps with checkpoints.
- **Validation**: how to confirm the result (tests, linters, manual checks).
- **Common pitfalls**: known traps and how to avoid them.
### Skill checklist
Include a `SKILL.md` that covers:
- Purpose and non goals
- When to use and when not to use (summarized in the frontmatter `description`; body section for extended detail)
- Inputs and prerequisites
- Step by step workflow with checkpoints
- Validation steps that can be run or observed
- Failure modes and recovery guidance
Also:
- Avoid duplicating text across multiple skills. Prefer referencing shared patterns.
- Do not include content copied from other repositories. If you are inspired by existing work, rewrite in your own words and adapt it to our conventions.
## Proposing a new agent
An agent definition should be opinionated but bounded:
- Describe the **role** (e.g., "WinForms Expert", "Security Reviewer", "Docs Maintainer").
- Define boundaries (what the agent should not do).
- List the skills it expects to use and how it chooses among them.
Add an agent file under a plugin's `agents/` directory:
```text
plugins/<plugin>/agents/<agent-name>.agent.md
```
### Agent checklist
Include documentation that explains:
- Role and intended tasks
- Boundaries and safety constraints
- Tooling assumptions
- How the agent chooses which skills to apply
- What a good completion looks like, including validation expectations
## Testing and validation
Skills and agents are documentation driven, but we still treat them as production assets.
- Every change should include a validation section that a reviewer can follow.
- If your change references commands, keep them cross platform when practical. If not, state the supported environment.
- If your change depends on external services, document how a reviewer can validate without privileged access, or explain why validation is not possible.
### Writing skill tests
Each skill should have an `eval.yaml` file that defines test scenarios. Tests live under the repo root `tests/` directory, matching the plugin and skill name:
```text
tests/<plugin>/<skill-name>/eval.yaml
```
A minimal eval file:
```yaml
scenarios:
- name: "Describe what the agent should do"
prompt: "The prompt sent to the agent"
assertions:
- type: "output_contains"
value: "expected text in agent output"
rubric:
- "The agent correctly identified the issue"
- "The agent suggested a concrete fix"
timeout: 120
```
See the [skill-validator README](eng/skill-validator/src/README.md) for the full eval.yaml format — assertion types, setup options, fixture files, constraints, and rubric details.
### Running tests locally
Prerequisites: the .NET SDK version specified by `global.json` (or a newer compatible SDK) and `gh auth login`.
```bash
# Run tests for a single plugin
dotnet run --project eng/skill-validator/src/SkillValidator.csproj -- evaluate --tests-dir tests/dotnet-msbuild plugins/dotnet-msbuild/skills
# Run tests for a single skill (pass the skill directory directly)
dotnet run --project eng/skill-validator/src/SkillValidator.csproj -- evaluate --tests-dir tests/dotnet-msbuild plugins/dotnet-msbuild/skills/common-build-errors
```
See the [skill-validator README](eng/skill-validator/src/README.md) for additional flags (`--runs`, `--model`, `--verbose`, etc.) and all available subcommands.
> [!WARNING]
> If you share the results in a Pull Request, make sure to have `--runs` configured to at least 3 but better 5 for reliable results.
### CI evaluation
Tests run automatically on pull requests that modify files under `plugins/`. The evaluation workflow discovers changed plugins and runs the skill-validator for each one. Results are posted as a PR comment and uploaded as build artifacts.
If a scenario fails or regresses, see [Investigating Results](eng/skill-validator/src/docs/InvestigatingResults.md) for how to download artifacts, interpret `results.json`, and diagnose common failure patterns.
## Writing style
- Be concise and specific.
- Prefer numbered steps for workflows.
- Prefer checklists for requirements.
- Define terminology the first time it appears.
- Avoid excessive formatting and avoid clever wording that could be misread by an agent.
## Security and safety
- Do not include secrets, tokens, or internal URLs.
- If you discover a security issue, do not open a public issue with sensitive details. Use the repository or organization security reporting process instead.
### External references
Skills often reference external tools, documentation, and projects — this is
expected and welcome, including community and third-party resources. To help
reviewers stay aware of external dependencies, the repository includes an
automated reference scanner (integrated into `skill-validator check`) that runs
in CI against plugin content (SKILL.md, agent files, and reference docs).
The scanner treats all of the following as CI-blocking errors:
- `http://` URLs where `https://` should be used
- `<script>` tags loading external resources without an `integrity` (SRI) attribute
- Pipe-to-shell patterns (`curl ... | bash`)
- URLs pointing to domains not listed in `eng/known-domains.txt`
Community tools and third-party projects are evaluated on a case-by-case basis
(see "What we look for" above). If your skill references a new external domain,
add it to `eng/known-domains.txt` in the same PR — the reviewer will
approve it alongside the skill content.
## Review process
Maintainers may request changes for:
- Clarity and unambiguous instructions
- Reduced scope
- More explicit validation
- Compatibility with multiple agent runtimes
- Consistency with existing conventions
We may close pull requests that are out of scope or too large to review. If that happens, we are happy to suggest a smaller path forward.
## Licensing and provenance
Only submit content that you have the right to contribute.
- Do not include copyrighted text from other projects.
- You may be asked to confirm that your contribution is original or appropriately licensed.
## Getting help
If you are unsure where a change belongs or how to structure a skill or agent, open an issue describing:
- The user problem
- The proposed outcome
- A small example of the desired behavior
If you're not sure whether something belongs under `skills/` or `agents/`, a good rule of thumb is:
- Put **reusable task playbooks** in `skills/`.
- Put **role + operating model** in `agents/`.
## Quality bar
Skills and agents in this repo should be:
- **Actionable**: the agent can follow them without guesswork.
- **Minimal**: no extra features or scope creep; focus on the task.
- **Verifiable**: always include a way to validate success.
- **Tool-conscious**: don't assume capabilities that might not exist in every runtime.
## Skill-Validator & Evaluation workflow
Changes to `eng/skill-validator` or the `.github/workflows/evaluation*.yml` workflows must be made from a branch in the `dotnet/skills` repository (i.e., not from a fork). This is a security measure.
For pull requests from forks, the evaluation workflow (triggered via `/evaluate`) always uses the workflow YAML from the default branch of `dotnet/skills` and builds the validator from that default-branch checkout, so any changes to these files in the forked PR will be ignored during evaluation.