#  18 · Coding Standards & Contributor Guide — **Stella Ops**  
*(v2.0 — 12 Jul 2025 · supersedes v1.0)*  

> **Audience** — Anyone sending a pull‑request to the open‑source Core.  
> **Goal** — Keep the code‑base small‑filed, plug‑in‑friendly, DI‑consistent, and instantly readable.

---

##  0 Why read this?

* Cuts review time → quicker merges.  
* Guarantees code is **hot‑load‑safe** for run‑time plug‑ins.  
* Prevents style churn and merge conflicts.  

---

##  1 High‑level principles

1. **SOLID first** – especially Interface & Dependency Inversion.  
2. **100‑line rule** – any file > 100 physical lines must be split or refactored.  
3. **Contract‑level ownership** – public abstractions live in lightweight *Contracts* libraries; impl classes live in runtime projects.  
4. **Single Composition Root** – all DI wiring happens in **`StellaOps.Web/Program.cs`** and in each plug‑in’s `IoCConfigurator`; nothing else calls `IServiceCollection.BuildServiceProvider`.  
5. **No Service Locator** – constructor injection only; static `ServiceProvider` is banned.  
6. **Fail‑fast startup** – configuration validated before the web‑host listens.  
7. **Hot‑load compatible** – no static singletons that survive plug‑in unload; avoid `Assembly.LoadFrom` outside the built‑in plug‑in loader.  

---

##  2 Repository layout (flat, July‑2025)**

```text
src/
├─ backend/
│   ├─ StellaOps.Web/                # ASP.NET host + composition root
│   ├─ StellaOps.Common/             # Serilog, Result<T>, helpers
│   ├─ StellaOps.Contracts/          # DTO + interface contracts (no impl)
│   ├─ StellaOps.Configuration/      # Options + validation
│   ├─ StellaOps.Localization/
│   ├─ StellaOps.PluginLoader/       # Cosign verify, hot‑load
│   ├─ StellaOps.Scanners.Trivy/     # First‑party scanner
│   ├─ StellaOps.TlsProviders.OpenSsl/
│   └─ … (additional runtime projects)
├─ plugins-sdk/                      # Templated contracts & abstractions
└─ frontend/                         # Angular workspace
tests/                               # Mirrors src structure 1‑to‑1
```

There are no folders named “Module” and no nested solutions.

##  3 Naming & style conventions

| Element                                                                         | Rule                                    | Example                         |
| ------------------------------------------------------------------------------- | --------------------------------------- | ------------------------------- |
| Namespaces                                                                      | File‑scoped, StellaOps.<Area>           | namespace StellaOps.Scanners;   |
| Interfaces                                                                      | I prefix, PascalCase                    | IScannerRunner                  |
| Classes / records                                                               | PascalCase                              | ScanRequest, TrivyRunner        |
| Private fields                                                                  | camelCase (no leading underscore)       | redisCache, httpClient          |
| Constants                                                                       | SCREAMING_SNAKE_CASE                    | const int MAX_RETRIES = 3;      |
| Async methods                                                                   | End with Async                          | Task<ScanResult> ScanAsync()    |
| File length                                                                     | ≤ 100 lines incl. using & braces        | enforced by dotnet format check |
| Using directives                                                                | Outside namespace, sorted, no wildcards | —                               |

Static analyzers (.editorconfig, StyleCop.Analyzers package) enforce the above. 

##  4 Dependency‑injection policy

Composition root – exactly one per process:

```csharp
builder.Services
       .AddStellaCore()          // extension methods from each runtime project
       .AddPluginLoader("/Plugins", cfg);   // hot‑load signed DLLs
```

Plug‑ins register additional services via the IoCConfigurator convention described in the Plug‑in SDK Guide, §5.
Never resolve services manually (provider.GetService<T>()) outside the composition root; tests may use WebApplicationFactory or ServiceProvider.New() helpers.
Scoped lifetime is default; singletons only for stateless, thread‑safe helpers.

##  5 Project organisation rules

Contracts vs. Runtime – public DTO & interfaces live in <Area>.Contracts; implementation lives in sibling project.
Feature folders – inside each runtime project group classes by use‑case, e.g.

```text
├─ Scan/
│   ├─ ScanService.cs
│   └─ ScanController.cs
├─ Feed/
└─ Tls/
```

Tests – mirror the structure under tests/ one‑to‑one; no test code inside production projects.

##  6 C# language features

Nullable reference types enabled.
record for immutable DTOs.
Pattern matching encouraged; avoid long switch‑cascades.
Span<T> & Memory<T> OK when perf‑critical, but measure first.
Use await foreach over manual paginator loops.

##  7 Error‑handling template

```csharp
public async Task<IActionResult> PostScan([FromBody] ScanRequest req)
{
    if (!ModelState.IsValid) return BadRequest(ModelState);

    try
    {
        ScanResult result = await scanService.ScanAsync(req);
        if (result.Quota != null) 
        {
            Response.Headers.TryAdd("X-Stella-Quota-Remaining", result.Quota.Remaining.ToString());
            Response.Headers.TryAdd("X-Stella-Reset", result.Quota.ResetUtc.ToString("o"));
        }
        return Ok(result);
    }
}
```

RFC 7807 ProblemDetails for all non‑200s.
Capture structured logs with Serilog’s message‑template syntax.

##  8 Async & threading

* All I/O is async; no .Result / .Wait().
* Library code: ConfigureAwait(false).
* Limit concurrency via Channel<T> or Parallel.ForEachAsync, never raw Task.Run loops.

##  9 Testing rules

| Layer                    | Framework                | Coverage gate              |
| ------------------------ | ------------------------ | -------------------------- |
| Unit                     | xUnit + FluentAssertions | ≥ 80 % line, ≥ 60 % branch |
| Integration              | Testcontainers           | Real Redis & Trivy         |
| Mutation (critical libs) | Stryker.NET              | ≥ 60 % score               |

One test project per runtime/contract project; naming <Project>.Tests.

##  10 Static analysis & formatting

* dotnet format must exit clean (CI gate).
* StyleCop.Analyzers + Roslyn‑Security‑Guard run on every PR.
* CodeQL workflow runs nightly on main.

##  11 Commit & PR checklist

* Conventional Commit prefix (feat:, fix:, etc.).
* dotnet format & dotnet test both green.
* Added or updated XML‑doc comments for public APIs.
* File count & length comply with 100‑line rule.
* If new public contract → update relevant markdown doc & JSON‑Schema.

##  12 Common pitfalls

|Symptom|  Root cause | Fix
|-------|-------------|-------------------
|InvalidOperationException: Cannot consume scoped service...| Mis‑matched DI lifetimes| Use scoped everywhere unless truly stateless
|Hot‑reload plug‑in crash| Static singleton caching plugin types| Store nothing static; rely on DI scopes

> 100‑line style violation |Large handlers or utils |Split into private helpers or new class

##  13 Change log

| Version | Date       | Notes                                                                                              |
| ------- | ---------- | -------------------------------------------------------------------------------------------------- |
| v2.0    | 2025‑07‑12 | Updated DI policy, 100‑line rule, new repo layout, camelCase fields, removed “Module” terminology. |
| 1.0     | 2025‑07‑09 | Original standards.                                                                                |
