Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
55c7bcc
CONFIG: Bump Solution from SLN to SLNX
perirrs May 13, 2026
b3442e9
CONFIG: Bump .net from 8.0 to 10.0
perirrs May 13, 2026
05c3659
CONFIG: Bump CompareNETObjects from 4.83.0 to 4.84.0
perirrs May 13, 2026
69220d0
CONFIG: Bump FluentAssertions from 6.12.1 to 7.2.2
perirrs May 13, 2026
4de3237
CONFIG: Bump Microsoft.Extensions.Configuration from 8.0.0 to 10.0.8
perirrs May 13, 2026
0b3b414
CONFIG: Bump Microsoft.NET.Test.Sdk from 17.11.1 to 18.5.1
perirrs May 13, 2026
e519c39
CONFIG: Bump Microsoft.Extensions.Configuration.Json from 8.0.1 to 10…
perirrs May 13, 2026
96ed3c0
CONFIG: Bump xunit from 2.9.2 to 2.9.3
perirrs May 13, 2026
d5aba25
CONFIG: Bump Xeption from 2.8.0 to 2.9.0
perirrs May 13, 2026
a767731
CONFIG: Bump xunit.runner.visualstudio from 2.8.2 to 3.1.5
perirrs May 13, 2026
928fa1d
CONFIG: Bump coverlet.collector from 6.0.2 to 10.0.0
perirrs May 13, 2026
243f2f5
CONFIG: Bump Microsoft.Extensions.DependencyInjection from 9.0.0 to 1…
perirrs May 13, 2026
8f4aa58
CONFIG: Bump Microsoft.Extensions.Hosting from 8.0.0 to 10.0.8
perirrs May 13, 2026
02f7581
CONFIG: Bump Newtonsoft.Json from 13.0.3 to 13.0.4
perirrs May 13, 2026
80d8b05
CONFIG: Bump Xeption from 2.8.0 to 2.9.0
perirrs May 13, 2026
3ed6825
CONFIG: Bump ADotNet from 3.0.5 to 4.2.0
perirrs May 13, 2026
ae65109
CONFIG: Replace CleanMoq 42.42.42 with Moq 4.20.72
perirrs May 13, 2026
141ef65
CONFIG: Lock FluentAssertions to [7.2.2]
perirrs May 13, 2026
b1bce26
CODE RUB: Added the-standard-skills
May 14, 2026
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
1,306 changes: 1,306 additions & 0 deletions .agents/skills/the-standard-architecture/SKILL.md

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions .agents/skills/the-standard-architecture/contracts/contracts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"skill": "the-standard-architecture",
"version": "1.0.0",

"naming": {
"broker_interface": "I{Resource}Broker",
"broker_class": "{Resource}Broker",
"broker_method": "{Action}{Entity}Async (infrastructure verbs: Insert, Select, Update, Delete, Post, Get, Put)",
"service_interface": "I{Entity}Service",
"service_class": "{Entity}Service",
"service_method": "{BusinessVerb}{Entity}Async (business verbs: Add, Retrieve, Modify, Remove)",
"processing_service_interface": "I{Entity}ProcessingService",
"processing_service_class": "{Entity}ProcessingService",
"processing_service_method": "{ProcessVerb}{Entity}Async (process verbs: Ensure, Upsert, TryAdd, TryRemove, Verify)",
"orchestration_service_interface": "I{Entity}OrchestrationService",
"orchestration_service_class": "{Entity}OrchestrationService",
"aggregation_service_interface": "I{Entity}AggregationService",
"aggregation_service_class": "{Entity}AggregationService",
"controller_class": "{Entities}Controller (plural entity name)",
"controller_route": "/api/{entities} (plural lowercase)"
},

"layer_dependency_rules": {
"allowed": [
"Exposer → Service (Foundation, Processing, Orchestration, Aggregation)",
"Foundation Service → Broker",
"Processing Service → Foundation Service",
"Orchestration Service → Processing Service or Foundation Service",
"Aggregation Service → any Service",
"Any Service → Support Broker (Logging, DateTime, Identifier)"
],
"forbidden": [
"Foundation Service → Foundation Service (same-layer call)",
"Foundation Service → Processing Service (upward dependency)",
"Foundation Service → Infrastructure directly (must go through broker)",
"Broker → Broker (entity brokers must not call other entity brokers)",
"Exposer → Broker (must always go through a service)",
"Service → Exposer (downward exposure call)",
"Processing Service → multiple Foundation Services (must be exactly one)"
]
},

"broker_rules": {
"max_external_resources_per_broker": 1,
"flow_control_allowed": false,
"exception_handling_allowed": false,
"all_methods_must_be_async": true,
"language": "infrastructure",
"must_implement_interface": true
},

"foundation_service_rules": {
"entity_types_handled": 1,
"input_output_type": "same_entity",
"validation_order": ["structural", "logical", "external", "dependency"],
"circuit_breaking": "structural validations must break immediately on null",
"continuous_validation": "collect all field errors before throwing",
"exception_categories": [
"{Entity}ValidationException",
"{Entity}DependencyValidationException",
"{Entity}CriticalDependencyException",
"{Entity}DependencyException",
"{Entity}ServiceException"
]
},

"processing_service_rules": {
"foundation_service_dependencies": "exactly_one",
"validation": "used-data-only",
"patterns": ["Ensure", "Upsert", "TryAdd", "TryRemove", "Verify"]
},

"orchestration_service_rules": {
"dependency_count": "2-3 (Florance Pattern)",
"call_order": "natural or explicitly enforced",
"multi_entity": true
},

"aggregation_service_rules": {
"order_validation": false,
"mock_sequence_assertions": false,
"validation_level": "basic structural only"
},

"exposer_rules": {
"business_logic": false,
"mapping_only": true,
"single_contact_point_per_entity": true,
"route_convention": "/api/{entities}",
"http_success_codes": {
"POST": 201,
"GET": 200,
"PUT": 200,
"DELETE": 200
},
"http_error_codes": {
"ValidationException": 400,
"DependencyValidationException": 400,
"CriticalDependencyException": 500,
"DependencyException": 500,
"ServiceException": 500
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// ---------------------------------------------------------------
// BAD EXAMPLE: Non-Standard Broker
// Each violation is annotated with the rule it breaks.
// ---------------------------------------------------------------

using System;

namespace MyProject.Brokers
{
// VIOLATION arch-001: No interface — broker cannot be mocked in tests
// VIOLATION arch-008: Wraps TWO resources (storage AND email) in one broker
public class StudentBroker
{
private readonly AppDbContext dbContext;
private readonly SmtpClient emailClient;

public StudentBroker(AppDbContext dbContext, SmtpClient emailClient)
{
this.dbContext = dbContext;
this.emailClient = emailClient;
}

// VIOLATION arch-006: Uses business language (Add) instead of infrastructure (Insert)
// VIOLATION arch-002: Contains flow control (if statement)
// VIOLATION arch-003: Handles exceptions — brokers must never catch exceptions
// VIOLATION arch-009: Not async
public Student AddStudent(Student student)
{
// arch-002 VIOLATION: flow control in broker
if (student == null)
{
throw new ArgumentNullException("student is null");
}

try
{
// arch-003 VIOLATION: exception handling in broker
this.dbContext.Students.Add(student);
this.dbContext.SaveChanges();

// arch-008 VIOLATION: sending email from an entity broker
this.emailClient.Send(new MailMessage("noreply@school.com", student.Email,
"Welcome", "You have been added."));

return student;
}
catch (Exception ex)
{
// arch-003 VIOLATION: catching and rethrowing in broker
throw new Exception($"Failed to add student: {ex.Message}");
}
}

// VIOLATION arch-006: Uses business language (GetById) instead of (SelectStudentById)
// VIOLATION arch-004: Reads connection string from environment directly
// instead of owning configuration through injection
public Student GetById(Guid id)
{
// arch-004 VIOLATION: broker does not own its configuration
var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION");

return this.dbContext.Students.Find(id);
}
}
}

// ---------------------------------------------------------------
// BAD EXAMPLE: Non-Standard Logging Broker
// ---------------------------------------------------------------

namespace MyProject.Brokers.Loggings
{
public class LoggingBroker : ILoggingBroker
{
private readonly ILogger<LoggingBroker> logger;

public LoggingBroker(ILogger<LoggingBroker> logger) =>
this.logger = logger;

// VIOLATION arch-009: Task.Run() wraps a synchronous ILogger<T> call,
// producing a heap-allocated Task and introducing thread-pool overhead
// for no benefit. ILogger<T> is already synchronous — no offloading needed.
// Use `async ValueTask` + direct call instead.
public ValueTask LogWarningAsync(string message) =>
new ValueTask(Task.Run(() => this.logger.LogWarning(message)));

// VIOLATION arch-009: Same Task.Run() anti-pattern on every method.
public ValueTask LogErrorAsync(Exception exception) =>
new ValueTask(Task.Run(() => this.logger.LogError(exception, exception.Message)));

public ValueTask LogCriticalAsync(Exception exception) =>
new ValueTask(Task.Run(() => this.logger.LogCritical(exception, exception.Message)));

public ValueTask LogInformationAsync(string message) =>
new ValueTask(Task.Run(() => this.logger.LogInformation(message)));

public ValueTask LogTraceAsync(string message) =>
new ValueTask(Task.Run(() => this.logger.LogTrace(message)));

public ValueTask LogDebugAsync(string message) =>
new ValueTask(Task.Run(() => this.logger.LogDebug(message)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// ---------------------------------------------------------------
// BAD EXAMPLE: Non-Standard Service
// Each violation is annotated with the rule it breaks.
// ---------------------------------------------------------------

namespace MyProject.Services
{
// VIOLATION arch-021: Handles more than one entity type (Student AND Course)
// VIOLATION arch-022: Uses infrastructure language (Insert, Select) not business language
// VIOLATION arch-080: Calls another foundation-level service directly (CourseService)
public class StudentService
{
private readonly IStorageBroker storageBroker;
private readonly ICourseService courseService; // arch-080 VIOLATION: same-layer call

public StudentService(IStorageBroker storageBroker, ICourseService courseService)
{
this.storageBroker = storageBroker;
this.courseService = courseService;
}

// VIOLATION arch-022: Infrastructure language (Insert) instead of business (Add)
// VIOLATION arch-023: No validation before delegating
// VIOLATION arch-024: No null check (structural validation) — no circuit-breaking
public async Task<Student> InsertStudent(Student student)
{
// arch-023 VIOLATION: Directly inserts without any validation
return await this.storageBroker.InsertStudentAsync(student);
}

// VIOLATION arch-025: No logical validation
// VIOLATION arch-028: Does not collect all errors — throws on first failure only
public async Task<Student> UpdateStudent(Student student)
{
// arch-024 VIOLATION: No null check
// arch-025 VIOLATION: No logical validation
if (student.Name == null)
{
throw new Exception("Name is null"); // not a Standard exception
}

// arch-080 VIOLATION: calling peer service from foundation service
var courses = await this.courseService.RetrieveAllCoursesAsync();

return await this.storageBroker.UpdateStudentAsync(student);
}

// VIOLATION arch-029: Raw exception is rethrown without localization or categorization
// VIOLATION arch-030: Exception is not categorized into Standard exception types
public async Task<Student> GetStudentById(Guid id)
{
try
{
return await this.storageBroker.SelectStudentByIdAsync(id);
}
catch (Exception ex)
{
// arch-029 VIOLATION: Not localized, not categorized, not logged
throw new Exception($"Database error: {ex.Message}");
}
}
}
}
Loading
Loading