-
Notifications
You must be signed in to change notification settings - Fork 6
feat(ai): add AI agent skills for repository workflows #348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
piotrzajac
wants to merge
1
commit into
master
Choose a base branch
from
feat/ai-agent-skills
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --- | ||
| name: add-attribute | ||
| description: Scaffold a new xUnit2 parameter attribute in the Core project. Use this skill when adding a new [AttributeName] that applies to all mock modules (AutoMoq, AutoFakeItEasy, AutoNSubstitute). Encodes the extension model, coding conventions, and test conventions for this repository. Invoke as: add-attribute AttributeName. | ||
| --- | ||
|
|
||
| # add-attribute | ||
|
|
||
| Scaffold a new parameter attribute in `Core` following all repository conventions. | ||
|
|
||
| Parameter attributes apply to individual test method parameters and affect how AutoFixture | ||
| generates values for them. They derive from `CustomizeAttribute` (AutoFixture.Xunit2) and | ||
| override `GetCustomization`. | ||
|
|
||
| See `references/extension-model.md` for an annotated full example. | ||
|
|
||
| ## Steps | ||
|
|
||
| **1. Understand the attribute's purpose** | ||
|
|
||
| - What constraint does it place on the generated value? | ||
| - Does it accept constructor parameters? What are their types and valid ranges? | ||
| - Review existing similar attributes for patterns: | ||
| - `[Except]` — excludes specific values | ||
| - `[PickFromRange(min, max)]` — constrains to a numeric range | ||
| - `[PickFromValues(v1, v2)]` — picks from an explicit list | ||
| - `[PickNegative]` — generates negative numbers only | ||
|
|
||
| **2. Create the attribute class** | ||
|
|
||
| Path: `src/Objectivity.AutoFixture.XUnit2.Core/Attributes/<Name>Attribute.cs` | ||
|
|
||
| Required conventions: | ||
|
|
||
| - `sealed class` deriving from `CustomizeAttribute` (decision-28) | ||
| - `using` directives go **inside** the namespace block (StyleCop SA1210) | ||
| - `global::` prefix on all external packages (AutoFixture, etc.) | ||
| - `[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]` | ||
| - `parameter.NotNull(nameof(parameter))` guard in `GetCustomization` (decision-29) | ||
| - No XML doc comments on public members (decision-27); use self-documenting names | ||
|
|
||
| **3. Create the test class** | ||
|
|
||
| Path: `src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/<Name>AttributeTests.cs` | ||
|
|
||
| Required structure: | ||
|
|
||
| - `[Collection("<Name>Attribute")]` — use the **subject** class name | ||
| - `[Trait("Category", "Attributes")]` | ||
| - BDD DisplayName on every `[Fact]` and `[Theory]` (decision-24) | ||
| - AAA structure with mandatory `// Arrange`, `// Act`, `// Assert` comments (decision-25) | ||
| - `[AutoMockData]` placed **above** `[Theory]` | ||
| - No `new Fixture()` — inject `IFixture` via test method parameters | ||
|
|
||
| Minimum test coverage: | ||
|
|
||
| - Constructor validation (invalid arguments throw expected exceptions) | ||
| - Property values are stored correctly | ||
| - `GetCustomization` returns a customization that causes AutoFixture to generate | ||
| values satisfying the constraint | ||
|
|
||
| **4. Update AGENTS.md** | ||
|
|
||
| Add a row to the "Parameter Attributes (Core, apply to all modules)" table: | ||
|
|
||
| ```text | ||
| | `[<Name>]` | <short description of what it does> | | ||
| ``` | ||
|
|
||
| **5. Run validate** | ||
|
|
||
| Follow `.ai/skills/validate/SKILL.md` to confirm build and tests pass. | ||
|
|
||
| ## Rules | ||
|
|
||
| - Add to `Core` only if the attribute applies to all three mock modules | ||
| - Never add Moq/FakeItEasy/NSubstitute-specific logic to `Core` | ||
| - Build must pass with 0 warnings before marking the task complete |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| # Extension Model Reference — Parameter Attributes | ||
|
|
||
| This document walks through `PickFromRangeAttribute` as a canonical example of how parameter | ||
| attributes are implemented in this repository. | ||
|
|
||
| ## Annotated source | ||
|
|
||
| ```csharp | ||
| namespace Objectivity.AutoFixture.XUnit2.Core.Attributes | ||
| { | ||
| // ① using directives go INSIDE the namespace block (StyleCop SA1210) | ||
| using System; | ||
| using System.Reflection; | ||
|
|
||
| // ② global:: prefix on all external packages (decision — C# style) | ||
| using global::AutoFixture; | ||
| using global::AutoFixture.Kernel; | ||
| using global::AutoFixture.Xunit2; | ||
|
|
||
| using Objectivity.AutoFixture.XUnit2.Core.Common; | ||
| using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; | ||
|
|
||
| // ③ AttributeUsage restricts to Parameter; AllowMultiple = false is typical | ||
| [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] | ||
| // ④ sealed class (decision-28) — all attribute-derived types are sealed | ||
| // ⑤ derives from CustomizeAttribute (AutoFixture.Xunit2), not DataAttribute | ||
| public sealed class PickFromRangeAttribute : CustomizeAttribute | ||
| { | ||
| // Public constructor overloads for each supported numeric type | ||
| public PickFromRangeAttribute(int minimum, int maximum) | ||
| : this((object)minimum, maximum) | ||
| { | ||
| } | ||
|
|
||
| // ... additional overloads (uint, long, ulong, double, float) follow the same pattern | ||
|
|
||
| // Private canonical constructor validates the range | ||
| private PickFromRangeAttribute(object minimum, object maximum) | ||
| { | ||
| if (((IComparable)minimum).CompareTo((IComparable)maximum) > 0) | ||
| { | ||
| throw new ArgumentOutOfRangeException( | ||
| nameof(minimum), | ||
| $"Parameter {nameof(minimum)} must be lower or equal to {nameof(maximum)}."); | ||
| } | ||
|
|
||
| this.Minimum = minimum; | ||
| this.Maximum = maximum; | ||
| } | ||
|
|
||
| public object Minimum { get; } | ||
|
|
||
| public object Maximum { get; } | ||
|
|
||
| // ⑥ override GetCustomization — the single required method from CustomizeAttribute | ||
| public override ICustomization GetCustomization(ParameterInfo parameter) | ||
| { | ||
| // ⑦ parameter.NotNull(nameof(parameter)) — use NotNull() guard (decision-29) | ||
| // NotNull() is an extension method from Core/Common/ | ||
| // It throws ArgumentNullException when parameter is null | ||
| return new FilteringSpecimenBuilder( | ||
| new RequestFactoryRelay( | ||
| (type) => new RangedNumberRequest(type, this.Minimum, this.Maximum)), | ||
| new EqualRequestSpecification(parameter.NotNull(nameof(parameter)))) | ||
| .ToCustomization(); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Key points | ||
|
|
||
| | # | Rule | Where enforced | | ||
| | --- | --- | --- | | ||
| | ① | `using` inside namespace | StyleCop SA1210 (build fails if violated) | | ||
| | ② | `global::` prefix on external packages | StyleCop SA1135 / team convention | | ||
| | ③ | `[AttributeUsage(AttributeTargets.Parameter)]` | Restricts attribute usage to parameters | | ||
| | ④ | `sealed` | decision-28 | | ||
| | ⑤ | Derives from `CustomizeAttribute` | AutoFixture.Xunit2 integration contract | | ||
| | ⑦ | `NotNull()` guard | decision-29 | | ||
|
|
||
| ## SpecimenBuilders used in data-narrowing attributes | ||
|
|
||
| | Class | Purpose | | ||
| | --- | --- | | ||
| | `FilteringSpecimenBuilder` | Wraps a builder and applies it only to matching requests | | ||
| | `RequestFactoryRelay` | Creates an AutoFixture request from the parameter type | | ||
| | `EqualRequestSpecification` | Matches requests equal to the given `ParameterInfo` | | ||
| | `RangedNumberRequest` | Built-in AutoFixture request for a number within a range | | ||
|
|
||
| ## Minimal attribute skeleton | ||
|
|
||
| Use this as a starting point for a new attribute: | ||
|
|
||
| ```csharp | ||
| namespace Objectivity.AutoFixture.XUnit2.Core.Attributes | ||
| { | ||
| using System; | ||
| using System.Reflection; | ||
|
|
||
| using global::AutoFixture; | ||
| using global::AutoFixture.Xunit2; | ||
|
|
||
| using Objectivity.AutoFixture.XUnit2.Core.Common; | ||
|
|
||
| [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] | ||
| public sealed class MyNewAttribute : CustomizeAttribute | ||
| { | ||
| public MyNewAttribute(/* constructor parameters */) | ||
| { | ||
| // validate and store | ||
| } | ||
|
|
||
| public override ICustomization GetCustomization(ParameterInfo parameter) | ||
| { | ||
| parameter.NotNull(nameof(parameter)); | ||
|
|
||
| // return an ICustomization that applies the constraint | ||
| throw new NotImplementedException(); | ||
| } | ||
| } | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| --- | ||
| name: create-branch-pr | ||
| description: Create a feature branch and open a pull request following repository conventions. Validates branch naming, prefills the PR description from the project template, and walks through the full PR checklist. Use when ready to publish changes. Invoke as: create-branch-pr [TASK-N]. | ||
| --- | ||
|
|
||
| # create-branch-pr | ||
|
|
||
| Create a properly named branch and open a PR with the standard description template. | ||
|
|
||
| See `assets/pr-template.md` for the full prefilled PR description. | ||
|
|
||
| ## Steps | ||
|
|
||
| **1. Confirm the branch name** | ||
|
|
||
| Format: `<type>/<kebab-description>` | ||
|
|
||
| The type must match the Conventional Commits type of the primary commit: | ||
|
|
||
| | Type | Use when | | ||
| | --- | --- | | ||
| | `feat/` | new attribute or feature | | ||
| | `fix/` | bug fix | | ||
| | `refactor/` | code restructuring without behaviour change | | ||
| | `chore/` | maintenance, tooling, dependency updates | | ||
| | `ci/` | CI/CD workflow changes | | ||
| | `docs/` | documentation only | | ||
|
|
||
| Examples: `feat/pick-from-list-attribute`, `fix/enumerable-allocation`, `docs/add-skills` | ||
|
|
||
| **2. Create and push the branch** | ||
|
|
||
| ```bash | ||
| git checkout -b <branch-name> | ||
| git push -u origin <branch-name> | ||
| ``` | ||
|
|
||
| **3. Walk through the PR checklist** | ||
|
|
||
| Confirm each item before opening the PR: | ||
|
|
||
| - [ ] Commit messages follow Conventional Commits (`type(scope): description`) | ||
| - [ ] `dotnet build src/Objectivity.AutoFixture.XUnit2.AutoMock.sln` passes with no warnings | ||
| - [ ] `dotnet test src/Objectivity.AutoFixture.XUnit2.AutoMock.sln` passes on all framework slices | ||
| - [ ] Code coverage is not degraded (Codecov verifies on CI) | ||
| - [ ] Mutation score is not degraded (Stryker verifies on CI) | ||
| - [ ] New tests follow GIVEN/WHEN/THEN naming and AAA structure | ||
| - [ ] No new `[SuppressMessage]` without a justification comment | ||
| - [ ] No `// TODO:` comments added — open a GitHub issue instead | ||
| - [ ] No new dependencies incompatible with the MIT license | ||
|
|
||
| **4. Open the PR** | ||
|
|
||
| Use the prefilled template from `assets/pr-template.md`: | ||
|
|
||
| ```bash | ||
| gh pr create \ | ||
| --title "<type>(scope): <description>" \ | ||
| --body "$(cat .ai/skills/create-branch-pr/assets/pr-template.md)" | ||
| ``` | ||
|
|
||
| In the PR body: | ||
|
|
||
| - Replace `Closes #` with the actual GitHub issue number if applicable | ||
| - Add a label to the PR after creation (`gh pr edit <number> --add-label "<label>"`) | ||
| - The `@coderabbitai summary` placeholder will be filled automatically by CodeRabbit on review | ||
|
|
||
| **5. Link the backlog task** (if a TASK-N was provided) | ||
|
|
||
| ```bash | ||
| npx backlog task edit TASK-N --status "Done" | ||
| ``` | ||
|
|
||
| ## Rules | ||
|
|
||
| - Direct pushes to `master` are not allowed — always use a feature branch | ||
| - All checklist items must be confirmed before opening the PR | ||
| - The PR title must follow Conventional Commits format |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Summary | ||
|
|
||
| <!-- Placeholder in the PR description that CodeRabbit replaces with the high-level summary. --> | ||
| @coderabbitai summary | ||
|
|
||
| Closes # | ||
|
|
||
| - Please add a description of the issue this PR is addressing. Link the relevant issue if applicable. | ||
| - Add a label to the PR. | ||
|
|
||
| ## Checklist | ||
|
|
||
| - [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) (`type(scope): description`) | ||
| - [ ] `dotnet build src/Objectivity.AutoFixture.XUnit2.AutoMock.sln` passes with no warnings | ||
| - [ ] `dotnet test src/Objectivity.AutoFixture.XUnit2.AutoMock.sln` passes on all framework slices | ||
| - [ ] Code coverage remains at least at the level prior the change (verified by Codecov) | ||
| - [ ] Mutation score remains at least at the level prior the change (verified by Stryker) | ||
| - [ ] New tests follow the GIVEN/WHEN/THEN naming convention and AAA structure (see [AGENTS.md](../AGENTS.md)) | ||
| - [ ] No new `[SuppressMessage]` without a justification comment | ||
| - [ ] No `// TODO:` comments added - open a GitHub issue instead | ||
| - [ ] No new dependencies introduced that are incompatible with the MIT license (verified by FOSSA) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| --- | ||
| name: new-test | ||
| description: Write a new test class for a given subject type following repository conventions. Use when adding tests for a new or existing class. Encodes BDD naming (decision-24) and AAA structure (decision-25). Invoke as: new-test SubjectClass. | ||
| --- | ||
|
|
||
| # new-test | ||
|
|
||
| Scaffold a properly structured test class for a given subject. | ||
|
|
||
| See `assets/test-template.md` for a complete scaffolded class ready to copy and fill in. | ||
|
|
||
| ## Steps | ||
|
|
||
| **1. Name the test class and file** | ||
|
|
||
| - File: `<SubjectClass>Tests.cs` | ||
| - Class: `<SubjectClass>Tests` | ||
| - Namespace: mirrors the source project namespace under `.Tests` | ||
| - Source: `Objectivity.AutoFixture.XUnit2.Core.Attributes` | ||
| - Test: `Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes` | ||
| - Location: `src/<Module>.Tests/<same-folder-path-as-source>/` | ||
|
|
||
| **2. Apply required class attributes** | ||
|
|
||
| ```csharp | ||
| [Collection("<SubjectClass>")] // subject class name — NOT the test class name | ||
| [Trait("Category", "<Area>")] // e.g. "Attributes", "AutoData", "Core" | ||
| public class <SubjectClass>Tests | ||
| ``` | ||
|
|
||
| **3. Name test methods using BDD convention** (decision-24) | ||
|
|
||
| For `[Theory]` (multiple data rows, precondition varies): | ||
|
|
||
| - DisplayName: `GIVEN <precondition> WHEN <action> THEN <outcome>` | ||
| - Method name: `Given<Precondition>_When<Action>_Then<Outcome>` | ||
|
|
||
| For `[Fact]` (single deterministic case): | ||
|
|
||
| - DisplayName: `WHEN <action> THEN <outcome>` | ||
| - Method name: `When<Action>_Then<Outcome>` | ||
|
|
||
| Rules: | ||
|
|
||
| - `DisplayName` is **always** set; never omit it | ||
| - GIVEN, WHEN, THEN are written in UPPER CASE; the rest is sentence case | ||
| - Method name uses PascalCase with underscores between the three BDD sections | ||
| - Never use `Test` as a suffix or prefix in method names | ||
|
|
||
| **4. Structure every test with AAA** (decision-25) | ||
|
|
||
| ```csharp | ||
| [Fact(DisplayName = "WHEN X THEN Y")] | ||
| public void WhenX_ThenY() | ||
| { | ||
| // Arrange | ||
|
|
||
| // Act | ||
|
|
||
| // Assert | ||
| } | ||
| ``` | ||
|
|
||
| All three comment blocks are mandatory even when a section is empty. | ||
|
|
||
| **5. Place data-source attributes above `[Theory]`** | ||
|
|
||
| ```csharp | ||
| [AutoMockData] | ||
| [Theory(DisplayName = "GIVEN X WHEN Y THEN Z")] | ||
| public void GivenX_WhenY_ThenZ(IFixture fixture) { ... } | ||
| ``` | ||
|
|
||
| The data-source attribute must appear **above** `[Theory]`, never below. | ||
|
|
||
| **6. Inject test data via parameters** | ||
|
|
||
| - Never use `new Fixture()` in test methods | ||
| - Inject `IFixture` via the test method signature | ||
| - For attribute wiring tests: use `Mock<IFixture>` and `Mock<IAutoFixtureAttributeProvider>` | ||
|
|
||
| ## Rules | ||
|
|
||
| - Every `[Fact]` and `[Theory]` must have `DisplayName` set | ||
| - `[Collection]` must use the **subject** class name, not the test class name | ||
| - Data-source attributes go **above** `[Theory]` |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a repo-root link for
AGENTS.mdin the PR template.Line 18 uses
../AGENTS.md, which is brittle when rendered in PR descriptions. Use/AGENTS.md(or full GitHub URL) to avoid broken links.Suggested patch
📝 Committable suggestion
🤖 Prompt for AI Agents