Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 12 additions & 161 deletions .agents/skills/dotnet-codestyle/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,130 +101,13 @@ updates, dependency upgrades, benchmarks) on top:

## Coding standards and conventions

Code snippets below are illustrative examples only, replace namespaces and types to match your
project.
Key rules: no `var` (always explicit types), file-scoped namespaces, Allman braces, Nullable
enabled, modern C# features (primary constructors, pattern matching, collection expressions). Every
public surface has XML documentation. Private fields use `_camelCase`, static fields `s_camelCase`,
constants PascalCase. Member ordering follows StyleCop SA1201.

### C# language features

1. **File-scoped namespaces**:

```csharp
namespace Example.Project.Library;
```

2. **Nullable reference types**: enabled (`<Nullable>enable</Nullable>`), use nullable annotations
appropriately, use `required` for mandatory properties.
3. **Modern C# features**: prefer modern language constructs, primary constructors when
appropriate, top-level statements for console apps, pattern matching over traditional checks,
collection expressions when types loosely match, extension methods (the classic
`this`-parameter form or an `extension(<receiver>) { ... }` block on C# 14+), implicit object
creation when the type is apparent, range and index operators.
4. **Expression-bodied members**: use for applicable methods, properties, accessors, operators,
lambdas, local functions.
5. **`var` keyword**: do NOT use `var`, always use explicit types:

```csharp
// Correct
int count = 42;
string name = "test";

// Incorrect
var count = 42;
var name = "test";
```

### Naming conventions

1. **Private fields**: underscore prefix with camelCase:

```csharp
private readonly HttpClient _httpClient;
private int _counter;
```

2. **Static fields**: `s_` prefix with camelCase:

```csharp
private static int s_instanceCount;
```

3. **Constants**: PascalCase:

```csharp
private const int MaxRetries = 3;
```

### Code structure

1. **Global usings**: use `GlobalUsings.cs` for common namespaces:

```csharp
global using System;
global using System.Net.Http;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Logging;
```

2. **Usings placement**: outside the namespace, sorted with `System` directives first:

```csharp
using System.CommandLine;
using System.Runtime.CompilerServices;
using Example.Project.Library;

namespace Example.Project.Console;
```

3. **Braces**: Allman style:

```csharp
public void Method()
{
if (condition)
{
// code
}
}
```

4. **Indentation**: C# files 4 spaces, XML/csproj files 2 spaces, YAML files 2 spaces, JSON files
4 spaces.
5. **Line endings**: not specified here, governed per repo by `.editorconfig` / `.gitattributes`
per GOVERNANCE.md's "Line Endings" section.
6. **`#region`**: do not use regions, prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance
readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers
-> methods -> operators) -> non-public in same order -> nested types.

### Comments and documentation

XML documentation is on: `<GenerateDocumentationFile>true</GenerateDocumentationFile>`, and
missing XML comments for public APIs are suppressed in `.editorconfig`. Every public surface must
still be documented: a single-line summary, additional details in remarks, documented input
parameters, return values, exceptions, and crefs.

```csharp
/// <summary>
/// Example of a single line summary.
/// </summary>
/// <remarks>
/// Additional important details about usage.
/// Multiple lines if needed.
/// </remarks>
/// <param name="category">
/// The quote category to request
/// </param>
/// <param name="cancellationToken">
/// A <see cref="System.Threading.CancellationToken"/> that can be used to cancel the request.
/// </param>
/// <returns>
/// A <see cref="string"/> containing the quote text.
/// </returns>
/// <exception cref="System.ArgumentException">
/// Thrown when <paramref name="category"/> is not a supported value.
/// </exception>
public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
```
For language features, naming, code structure, and XML documentation examples, see
`references/conventions.md`.

## Analyzer suppressions (.NET)

Expand Down Expand Up @@ -312,47 +195,15 @@ The .NET mechanics, narrowest first:

## Testing conventions

1. **Framework**: xUnit v3 or later (the `xunit.v3` package, never the legacy v2 `xunit` package)
with AwesomeAssertions for every assertion. Native xUnit asserts (`Assert.Equal`,
`Assert.True`, ...) are not allowed, use the fluent `.Should()` API. Dynamic test skipping
(`Assert.Skip`, `Assert.SkipWhen`) is control flow, not an assertion, and stays native:

```csharp
[Fact]
public void MethodName_Scenario_ExpectedBehavior()
{
// Arrange
int expected = 42;

// Act
int actual = GetValue();

// Assert
actual.Should().Be(expected);
}
```

2. **Organization**: Arrange-Act-Assert pattern.
3. **Naming**: descriptive names with underscores.
4. **Theory tests**: use `[Theory]` with `[InlineData]`.
xUnit v3 (`xunit.v3`, not the legacy `xunit`) + AwesomeAssertions (`.Should()` API, never native
asserts). Arrange-Act-Assert pattern, descriptive underscore names, `[Theory]`/`[InlineData]` for
parameterized tests. See `references/testing.md` for the framework setup template.

## Project configuration

1. **Target framework**: .NET 10.0 (`<TargetFramework>net10.0</TargetFramework>`).
2. **AOT compatibility**: `<IsAotCompatible>true</IsAotCompatible>`,
`<VerifyReferenceAotCompatibility>true</VerifyReferenceAotCompatibility>`.
3. **Assembly information**: use semantic versioning, include SourceLink
(`<PublishRepositoryUrl>true</PublishRepositoryUrl>`), embed untracked sources
(`<EmbedUntrackedSources>true</EmbedUntrackedSources>`).
4. **Internal visibility**: use `InternalsVisibleTo` for test and benchmark access (adapt the
project names to your repo's test/benchmark projects):

```xml
<ItemGroup>
<InternalsVisibleTo Include="YourBenchmarkProject" />
<InternalsVisibleTo Include="YourTestProject" />
</ItemGroup>
```
.NET 10.0 target, AOT-compatible (`IsAotCompatible=true`, `VerifyReferenceAotCompatibility=true`),
SourceLink, embedded untracked sources, `InternalsVisibleTo` for test/benchmark access. See
`references/project-config.md` for the full property list.

## Best practices

Expand Down
126 changes: 126 additions & 0 deletions .agents/skills/dotnet-codestyle/references/conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# .NET Coding Standards and Conventions

Code snippets below are illustrative examples only, replace namespaces and types to match your
project.

## C# language features

1. **File-scoped namespaces**:

```csharp
namespace Example.Project.Library;
```

2. **Nullable reference types**: enabled (`<Nullable>enable</Nullable>`), use nullable annotations
appropriately, use `required` for mandatory properties.
3. **Modern C# features**: prefer modern language constructs, primary constructors when
appropriate, top-level statements for console apps, pattern matching over traditional checks,
collection expressions when types loosely match, extension methods (the classic
`this`-parameter form or an `extension(<receiver>) { ... }` block on C# 14+), implicit object
creation when the type is apparent, range and index operators.
4. **Expression-bodied members**: use for applicable methods, properties, accessors, operators,
lambdas, local functions.
5. **`var` keyword**: do NOT use `var`, always use explicit types:

```csharp
// Correct
int count = 42;
string name = "test";

// Incorrect
var count = 42;
var name = "test";
```

## Naming conventions

1. **Private fields**: underscore prefix with camelCase:

```csharp
private readonly HttpClient _httpClient;
private int _counter;
```

2. **Static fields**: `s_` prefix with camelCase:

```csharp
private static int s_instanceCount;
```

3. **Constants**: PascalCase:

```csharp
private const int MaxRetries = 3;
```

## Code structure

1. **Global usings**: use `GlobalUsings.cs` for common namespaces:

```csharp
global using System;
global using System.Net.Http;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Logging;
```

2. **Usings placement**: outside the namespace, sorted with `System` directives first:

```csharp
using System.CommandLine;
using System.Runtime.CompilerServices;
using Example.Project.Library;

namespace Example.Project.Console;
```

3. **Braces**: Allman style:

```csharp
public void Method()
{
if (condition)
{
// code
}
}
```

4. **Indentation**: C# files 4 spaces, XML/csproj files 2 spaces, YAML files 2 spaces, JSON files
4 spaces.
5. **Line endings**: not specified here, governed per repo by `.editorconfig` / `.gitattributes`
per GOVERNANCE.md's "Line Endings" section.
6. **`#region`**: do not use regions, prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance
readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers
-> methods -> operators) -> non-public in same order -> nested types.

## Comments and documentation

XML documentation is on: `<GenerateDocumentationFile>true</GenerateDocumentationFile>`, and
missing XML comments for public APIs are suppressed in `.editorconfig`. Every public surface must
still be documented: a single-line summary, additional details in remarks, documented input
parameters, return values, exceptions, and crefs.

```csharp
/// <summary>
/// Example of a single line summary.
/// </summary>
/// <remarks>
/// Additional important details about usage.
/// Multiple lines if needed.
/// </remarks>
/// <param name="category">
/// The quote category to request
/// </param>
/// <param name="cancellationToken">
/// A <see cref="System.Threading.CancellationToken"/> that can be used to cancel the request.
/// </param>
/// <returns>
/// A <see cref="string"/> containing the quote text.
/// </returns>
/// <exception cref="System.ArgumentException">
/// Thrown when <paramref name="category"/> is not a supported value.
/// </exception>
public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
```
17 changes: 17 additions & 0 deletions .agents/skills/dotnet-codestyle/references/project-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# .NET Project Configuration

1. **Target framework**: .NET 10.0 (`<TargetFramework>net10.0</TargetFramework>`).
2. **AOT compatibility**: `<IsAotCompatible>true</IsAotCompatible>`,
`<VerifyReferenceAotCompatibility>true</VerifyReferenceAotCompatibility>`.
3. **Assembly information**: use semantic versioning, include SourceLink
(`<PublishRepositoryUrl>true</PublishRepositoryUrl>`), embed untracked sources
(`<EmbedUntrackedSources>true</EmbedUntrackedSources>`).
4. **Internal visibility**: use `InternalsVisibleTo` for test and benchmark access (adapt the
project names to your repo's test/benchmark projects):

```xml
<ItemGroup>
<InternalsVisibleTo Include="YourBenchmarkProject" />
<InternalsVisibleTo Include="YourTestProject" />
</ItemGroup>
```
25 changes: 25 additions & 0 deletions .agents/skills/dotnet-codestyle/references/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# .NET Testing Conventions

1. **Framework**: xUnit v3 or later (the `xunit.v3` package, never the legacy v2 `xunit` package)
with AwesomeAssertions for every assertion. Native xUnit asserts (`Assert.Equal`,
`Assert.True`, ...) are not allowed, use the fluent `.Should()` API. Dynamic test skipping
(`Assert.Skip`, `Assert.SkipWhen`) is control flow, not an assertion, and stays native:

```csharp
[Fact]
public void MethodName_Scenario_ExpectedBehavior()
{
// Arrange
int expected = 42;

// Act
int actual = GetValue();

// Assert
actual.Should().Be(expected);
}
```

2. **Organization**: Arrange-Act-Assert pattern.
3. **Naming**: descriptive names with underscores.
4. **Theory tests**: use `[Theory]` with `[InlineData]`.
29 changes: 7 additions & 22 deletions .agents/skills/git-commit-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,28 +154,13 @@ the rewrite looks, a rejected push is recoverable, a force-pushed one is not.

## History rewrites re-identify only what changed

**A history rewrite includes only the commits that must change, and re-identifies any commit it
rewrites that is not the agent's own.** Filtering history (`git filter-repo` or an equivalent, for
example to strip PII) re-signs every commit it touches with the rewriter's own key, while the
tooling preserves each commit's original `author`/`committer` unless told otherwise. GitHub
verifies a signature against the commit's `committer` identity, so a signature from the rewriter's
key over a commit still committed by a bot (`dependabot[bot]`, `github-actions[bot]`) or GitHub's
own web-flow does not match its committer and lands `unknown_key`/unverified, which a
require-signed-commits rule then rejects.

Two gates keep committer and signature aligned:

1. **Scope the rewrite to only the commits that must be modified.** By default those are the
rewriter's own, whose committer already matches, so a commit that needs no change stays out of
the rewrite entirely and its identity and signature are never touched.
2. **If a commit that must change is not the rewriter's own, set its `committer` to the rewriter's
own signing identity before re-signing** (and its `author` too, since a rewrite that alters
content should not keep attributing it to the bot). The original bot attribution is deliberately
given up as the cost of having to rewrite it.

Never leave a signature over a commit committed by another identity. Verify after any rewrite that
every rewritten commit is signed and committed under the correct identity
(`git log --show-signature`).
**Do not rewrite a commit that does not need to change.** A history rewrite (e.g. `git filter-repo`
to strip PII) re-signs every touched commit with the rewriter's key. If that commit is still
committed by a bot (`dependabot[bot]`, `github-actions[bot]`) or GitHub's own web-flow, the
signature will not match the committer and the require-signed-commits rule rejects it. Scope the
rewrite to only the commits that must change. Set `committer` (and `author`) to the rewriter's
identity on any non-own commit that must be modified. Verify with `git log --show-signature` after
any rewrite. See `references/history-rewrite.md` for the full two-gate rule.

## Never run destructive git commands without being asked

Expand Down
Loading