Compare commits

..

8 Commits

Author SHA1 Message Date
55f1324286 Merge pull request 'feat/claude-code-harness' (#2) from feat/claude-code-harness into main
All checks were successful
Publish NuGets / build (release) Successful in 34s
Reviewed-on: #2
2026-03-12 06:44:11 -04:00
Mathias Beaulieu-Duncan
b34bf874b4 Remove Claude harness — replaced by claude-cqrs-plugin
The in-repo .claude/ harness (rules, skills, settings) is superseded by
the standalone claude-cqrs-plugin which provides the same guidance as a
reusable plugin across all Svrnty.CQRS projects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:42:50 -04:00
Mathias Beaulieu-Duncan
c6de10b98b Move UseSvrntyCqrs() from MinimalApi to core Svrnty.CQRS package
gRPC-only projects couldn't call app.UseSvrntyCqrs() without adding the
MinimalApi package. The method only calls ExecuteMappingCallbacks() which
is already in core — it had no MinimalApi dependency. Adds ASP.NET Core
FrameworkReference to the core package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:38:26 -04:00
Mathias Beaulieu-Duncan
3945c1a158 Add project-init agent for scaffolding new CQRS projects
Scaffolds a complete Svrnty.CQRS project from a natural language
description — creates solution, web project, DAL with PostgreSQL,
entities, Program.cs, first feature, proto file, and .editorconfig.
Defaults to gRPC-only; MinimalApi added only on request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:38:26 -04:00
7614f68512 Merge pull request 'feat/claude-code-harness' (#1) from feat/claude-code-harness into main
All checks were successful
Publish NuGets / build (release) Successful in 32s
Reviewed-on: #1
2026-03-12 03:35:26 -04:00
Mathias Beaulieu-Duncan
fdee02c960 Apply dotnet format with new editorconfig rules
Automated formatting: BOM removal, using sort order, final newlines,
whitespace normalization across all projects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 03:30:50 -04:00
Mathias Beaulieu-Duncan
a4525bad6a Add Claude Code harness: rules, skills, hooks, and editorconfig
- Add path-specific rules for commands/queries, dynamic queries, validation, and gRPC
- Add /add-command, /add-query, /add-dynamic-query scaffolding skills
- Add project settings with post-edit formatting, proto validation, and build-gate hooks
- Add .editorconfig codifying existing code style conventions
- Trim CLAUDE.md from 414 to 130 lines (domain details moved to rules)
- Add .harness-version tracking for the shared claude-harness repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 03:30:27 -04:00
Svrnty
3df094b9e7 docs: sanitise product references, add "Where This Fits" to README
Co-Authored-By: Svrnty Inc. <jp@svrnty.io, mathias@svrnty.io>
2026-02-27 13:08:17 -05:00
44 changed files with 3503 additions and 3449 deletions

View File

@ -1,50 +0,0 @@
{
"permissions": {
"allow": [
"Bash(dotnet clean:*)",
"Bash(dotnet run)",
"Bash(dotnet add:*)",
"Bash(timeout 5 dotnet run:*)",
"Bash(dotnet remove:*)",
"Bash(netstat:*)",
"Bash(findstr:*)",
"Bash(cat:*)",
"Bash(taskkill:*)",
"WebSearch",
"Bash(dotnet tool install:*)",
"Bash(protogen:*)",
"Bash(timeout 15 dotnet run:*)",
"Bash(where:*)",
"Bash(timeout 30 dotnet run:*)",
"Bash(timeout 60 dotnet run:*)",
"Bash(timeout 120 dotnet run:*)",
"Bash(git add:*)",
"Bash(curl:*)",
"Bash(timeout 3 cmd:*)",
"Bash(timeout:*)",
"Bash(tasklist:*)",
"Bash(dotnet build:*)",
"Bash(dotnet --list-sdks:*)",
"Bash(dotnet sln:*)",
"Bash(pkill:*)",
"Bash(python3:*)",
"Bash(grpcurl:*)",
"Bash(lsof:*)",
"Bash(xargs kill -9)",
"Bash(dotnet run:*)",
"Bash(find:*)",
"Bash(dotnet pack:*)",
"Bash(unzip:*)",
"WebFetch(domain:andrewlock.net)",
"WebFetch(domain:github.com)",
"WebFetch(domain:stackoverflow.com)",
"WebFetch(domain:www.kenmuse.com)",
"WebFetch(domain:blog.rsuter.com)",
"WebFetch(domain:natemcmaster.com)",
"WebFetch(domain:www.nuget.org)",
"Bash(mkdir:*)"
],
"deny": [],
"ask": []
}
}

96
.editorconfig Normal file
View File

@ -0,0 +1,96 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{csproj,props,targets,xml}]
indent_size = 2
[*.{json,yml,yaml}]
indent_size = 2
[*.proto]
indent_size = 2
[*.cs]
# Namespace
csharp_style_namespace_declarations = file_scoped:warning
# Braces — Allman style
csharp_new_line_before_open_brace = all
# Usings
dotnet_sort_system_directives_first = true
csharp_using_directive_placement = outside_namespace:warning
# var preferences — use var when type is apparent
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Expression bodies — prefer for simple members
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_constructors = false:suggestion
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
# Null checking
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences — exclude interface members (netstandard2.1 compat)
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
# Field naming — _camelCase for private fields
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_underscore
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, private_protected
dotnet_naming_symbols.private_fields.required_modifiers =
dotnet_naming_style.camel_case_underscore.required_prefix = _
dotnet_naming_style.camel_case_underscore.capitalization = camel_case
# Constants — PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = pascal_case
dotnet_naming_symbols.constants.applicable_kinds = field
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.pascal_case.capitalization = pascal_case
# Interfaces — I prefix
dotnet_naming_rule.interfaces_should_begin_with_i.severity = warning
dotnet_naming_rule.interfaces_should_begin_with_i.symbols = interfaces
dotnet_naming_rule.interfaces_should_begin_with_i.style = begins_with_i
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case
# Async methods — Async suffix
dotnet_naming_rule.async_methods_should_end_with_async.severity = suggestion
dotnet_naming_rule.async_methods_should_end_with_async.symbols = async_methods
dotnet_naming_rule.async_methods_should_end_with_async.style = ends_with_async
dotnet_naming_symbols.async_methods.applicable_kinds = method
dotnet_naming_symbols.async_methods.required_modifiers = async
dotnet_naming_style.ends_with_async.required_suffix = Async
dotnet_naming_style.ends_with_async.capitalization = pascal_case

View File

@ -1,6 +1,6 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to AI agents when working with code in this repository.
## Project Overview

View File

@ -4,6 +4,15 @@
Our implementation of query and command responsibility segregation (CQRS).
## Where This Fits
This is a backend framework of the [Svrnty Agent System](../README.md).
**Layer**: Framework
**Depends on**: Nothing (standalone .NET framework)
**Depended on by**: a-gent-app (backend services), flutter_cqrs_datasource (client)
**Git**: [git.openharbor.io/svrnty/dotnet-cqrs](https://git.openharbor.io/svrnty/dotnet-cqrs)
## Getting Started
> Install nuget package to your awesome project.

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace Svrnty.CQRS.Abstractions.Attributes;

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace Svrnty.CQRS.Abstractions.Attributes;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Reflection;
using Svrnty.CQRS.Abstractions.Attributes;

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Reflection;
using Svrnty.CQRS.Abstractions.Attributes;

View File

@ -1,4 +1,4 @@
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions;

View File

@ -1,4 +1,4 @@
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions;

View File

@ -1,4 +1,4 @@
namespace Svrnty.CQRS.Abstractions.Security;
namespace Svrnty.CQRS.Abstractions.Security;
public enum AuthorizationResult
{

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;

View File

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Svrnty.CQRS.DynamicQuery.Abstractions;

View File

@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using PoweredSoft.DynamicQuery.Core;
namespace Svrnty.CQRS.DynamicQuery.Abstractions;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Svrnty.CQRS.DynamicQuery.Abstractions;

View File

@ -1,4 +1,4 @@
namespace Svrnty.CQRS.DynamicQuery.Abstractions;
namespace Svrnty.CQRS.DynamicQuery.Abstractions;
public interface IDynamicQueryParams<out TParams>
where TParams : class

View File

@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using PoweredSoft.DynamicQuery.Core;
using Svrnty.CQRS.Abstractions;
using Svrnty.CQRS.Abstractions.Attributes;
using Svrnty.CQRS.Abstractions.Discovery;
@ -14,7 +15,6 @@ using Svrnty.CQRS.Abstractions.Security;
using Svrnty.CQRS.DynamicQuery;
using Svrnty.CQRS.DynamicQuery.Abstractions;
using Svrnty.CQRS.DynamicQuery.Discover;
using PoweredSoft.DynamicQuery.Core;
namespace Svrnty.CQRS.DynamicQuery.MinimalApi;

View File

@ -1,4 +1,4 @@
using System;
using System;
using Pluralize.NET;
using Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using Svrnty.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using Svrnty.CQRS.DynamicQuery.Abstractions;
namespace Svrnty.CQRS.DynamicQuery;

View File

@ -1,6 +1,6 @@
using System;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using System;
namespace Svrnty.CQRS.DynamicQuery;

View File

@ -1,10 +1,10 @@
using Svrnty.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PoweredSoft.DynamicQuery.Core;
using Svrnty.CQRS.DynamicQuery.Abstractions;
namespace Svrnty.CQRS.DynamicQuery;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
@ -6,9 +6,9 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Svrnty.CQRS.DynamicQuery.Abstractions;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using Svrnty.CQRS.DynamicQuery.Abstractions;
namespace Svrnty.CQRS.DynamicQuery;

View File

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using PoweredSoft.Data.Core;

View File

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using FluentValidation;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions;

View File

@ -1,13 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Svrnty.CQRS.Grpc.Generators.Helpers;
using Svrnty.CQRS.Grpc.Generators.Models;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svrnty.CQRS.Grpc.Generators
{
namespace Svrnty.CQRS.Grpc.Generators;
[Generator]
public class GrpcGenerator : IIncrementalGenerator
{
@ -3359,4 +3359,3 @@ namespace Svrnty.CQRS.Grpc.Generators
p.Length > 0 ? char.ToUpperInvariant(p[0]) + p.Substring(1).ToLowerInvariant() : ""));
}
}
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
namespace Svrnty.CQRS.Grpc.Generators.Helpers
{
namespace Svrnty.CQRS.Grpc.Generators.Helpers;
internal static class ProtoTypeMapper
{
private static readonly Dictionary<string, string> TypeMap = new Dictionary<string, string>
@ -99,4 +99,3 @@ namespace Svrnty.CQRS.Grpc.Generators.Helpers
return csharpType.Replace("?", "");
}
}
}

View File

@ -1,8 +1,8 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Svrnty.CQRS.Grpc.Generators.Models
{
namespace Svrnty.CQRS.Grpc.Generators.Models;
public class CommandInfo
{
public string Name { get; set; }
@ -80,4 +80,3 @@ namespace Svrnty.CQRS.Grpc.Generators.Models
IsElementGuid = false;
}
}
}

View File

@ -1,5 +1,5 @@
namespace Svrnty.CQRS.Grpc.Generators.Models
{
namespace Svrnty.CQRS.Grpc.Generators.Models;
public class DynamicQueryInfo
{
public string Name { get; set; }
@ -25,4 +25,3 @@ namespace Svrnty.CQRS.Grpc.Generators.Models
HasParams = false;
}
}
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
namespace Svrnty.CQRS.Grpc.Generators.Models
{
namespace Svrnty.CQRS.Grpc.Generators.Models;
/// <summary>
/// Represents a discovered streaming notification type for proto/gRPC generation.
/// </summary>
@ -47,4 +47,3 @@ namespace Svrnty.CQRS.Grpc.Generators.Models
Properties = new List<PropertyInfo>();
}
}
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
namespace Svrnty.CQRS.Grpc.Generators.Models
{
namespace Svrnty.CQRS.Grpc.Generators.Models;
public class QueryInfo
{
public string Name { get; set; }
@ -27,4 +27,3 @@ namespace Svrnty.CQRS.Grpc.Generators.Models
IsResultPrimitiveType = false;
}
}
}

View File

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions;
using Svrnty.CQRS.Discovery;
@ -43,7 +44,7 @@ public class CqrsBuilder
/// <summary>
/// Adds a command handler to the CQRS pipeline
/// </summary>
public CqrsBuilder AddCommand<TCommand, TCommandHandler>()
public CqrsBuilder AddCommand<TCommand, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>()
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
{
@ -54,7 +55,7 @@ public class CqrsBuilder
/// <summary>
/// Adds a command handler with result to the CQRS pipeline
/// </summary>
public CqrsBuilder AddCommand<TCommand, TResult, TCommandHandler>()
public CqrsBuilder AddCommand<TCommand, TResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>()
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TResult>
{

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Svrnty.CQRS.Abstractions.Discovery;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Svrnty.CQRS.Abstractions.Discovery;

View File

@ -26,6 +26,10 @@
<None Include="..\README.md" Pack="true" PackagePath="" CopyToOutputDirectory="Always" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Svrnty.CQRS.Abstractions\Svrnty.CQRS.Abstractions.csproj" />
</ItemGroup>

View File

@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Configuration;
namespace Svrnty.CQRS.MinimalApi;
namespace Svrnty.CQRS;
public static class WebApplicationExtensions
{

View File

@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Svrnty.CQRS;
using Svrnty.CQRS.Abstractions;
using Svrnty.CQRS.DynamicQuery;
using Svrnty.CQRS.FluentValidation;
using Svrnty.CQRS.Grpc;
using Svrnty.Sample;
using Svrnty.CQRS.MinimalApi;
using Svrnty.CQRS.DynamicQuery;
using Svrnty.CQRS.Abstractions;
using Svrnty.Sample;
var builder = WebApplication.CreateBuilder(args);

View File

@ -1,5 +1,5 @@
using PoweredSoft.Data.Core;
using System.Linq.Expressions;
using PoweredSoft.Data.Core;
namespace Svrnty.Sample;