v0.5.0Roslyn 5.3.0 upgrade — convert C# 14 code to Calor, exhaustive match enforcement on sum types.See what's new

Effect Manifests: .NET Ecosystem Coverage

How Calor knows the effects of .NET framework and ecosystem library calls

When Calor code calls into .NET libraries, the compiler needs to know what side effects those calls have. Effect manifests provide this information — JSON files that map types and methods to their effects.


The Problem

Calor enforces that every function declares its effects:

Plain Text
§F{f001:SaveUser:pub}
  §O{void}
  §E{db:w,cw}
  §C{DbContext.SaveChanges} §/C
  §C{ILogger.LogInformation} §A STR:"User saved" §/C
§/F{f001}

But how does the compiler know that DbContext.SaveChanges has effect db:w and ILogger.LogInformation has effect cw? Without this knowledge, every external call would be flagged as Unknown.

What's Covered

Calor ships with 14 embedded manifest files covering ~170 types across three tiers:

BCL Types (10 manifests)

Core .NET types the compiler has always known about, plus recent additions:

ManifestTypesExamples
bcl-systemConsole, Math, String, LINQ, CollectionsConsole.WriteLine → cw, Math.Abs → pure
bcl-ioFile, Directory, Stream, TextWriterFile.ReadAllText → fs:r, File.WriteAllText → fs:w
bcl-netHttpClient, Socket, DNS, WebClientHttpClient.GetAsync → net:rw
bcl-dataDbConnection, DbCommand, SqlClientDbCommand.ExecuteNonQuery → db:w
bcl-runtimeRandom, DateTime, Environment, Process, TaskRandom.Next → rand, DateTime.Now → time
bcl-text-jsonJsonSerializer, JsonDocument, Utf8JsonWriterJsonSerializer.Serialize → pure
bcl-text-regexRegex, Match, GroupRegex.IsMatch → pure
bcl-concurrentConcurrentDictionary, Channel, BlockingCollectionConcurrentDictionary.TryAdd → mut
bcl-cryptoSHA256, RSA, Aes, RandomNumberGeneratorSHA256.HashData → pure, RandomNumberGenerator.GetBytes → rand
bcl-framework-interfacesILogger, DbContext, IConfiguration, IHost, ControllerBaseSee below

Framework Interfaces (Tier B)

The most commonly used .NET framework types — these are the calls AI agents generate most often:

TypeMethodEffectWhy
ILogger.LogInformationAll log methodscwObservable output
DbContext.SaveChangesSaveChanges/Asyncdb:wDatabase write
DbContext.FindFind/FindAsyncdb:rDatabase read
IConfiguration.GetSectionGetSection, GetValueenv:rEnvironment/config read
ControllerBase.OkOk, NotFound, BadRequestpureNo side effects
HttpResponse.WriteAsyncWriteAsync, Redirectnet:wNetwork write
IHost.RunAsyncRun, Start, Stopnet:rwStarts network listener
IServiceProvider.GetServiceGetServicepureDI resolution
IOptions<T>.ValueValue getterpureConfiguration read
DbSet<T>.ToListAsyncAll query extensionsdb:rDatabase query
DatabaseFacade.MigrateMigrate, EnsureCreateddb:wSchema modification

Ecosystem Libraries (4 manifests)

Popular NuGet packages that AI agents commonly generate code for:

LibraryKey typesEffects
SerilogLog.Information, ILogger.Writecw
Newtonsoft.JsonJsonConvert.SerializeObjectpure
DapperSqlMapper.Query / Executedb:r / db:w
MediatRIMediator.Sendpure (handler-dependent)
AutoMapperIMapper.Mappure
FluentValidationIValidator.Validatepure
PollyPolicy.Executepure (wraps delegate)

How Resolution Works

When the compiler encounters §C{DbContext.SaveChanges}, it:

  1. Parses the target: type = DbContext, method = SaveChanges
  2. Maps the short name: DbContextMicrosoft.EntityFrameworkCore.DbContext
  3. Looks up the manifest: finds bcl-framework-interfaces.calor-effects.json
  4. Returns the effect: db:w

For instance calls like §C{client.GetAsync} (from converted C# code), the compiler also checks variable declarations — if §B{client} §NEW{HttpClient}, it resolves clientSystem.Net.Http.HttpClientnet:rw.

Resolution Priority

Manifests are loaded in priority order (higher overrides lower):

PrioritySourceExample
0Built-in (embedded in compiler)All 14 shipped manifests
100User-level (~/.calor/manifests/)Personal overrides
200Solution-level ({solution}/.calor-effects/)Team-shared manifests
300Project-local (.calor-effects.json)Project-specific types

Adding Custom Manifests

For types not covered by the built-in manifests, create a .calor-effects.json file in your project root:

JSON
{
  "version": "1.0",
  "description": "Effects for MyApp types",
  "mappings": [
    {
      "type": "MyApp.Services.EmailService",
      "methods": {
        "SendAsync": ["net:w"],
        "ValidateAddress": []
      }
    },
    {
      "type": "MyApp.Data.CacheService",
      "methods": {
        "Get": ["db:r"],
        "Set": ["db:w"],
        "Remove": ["db:w"]
      }
    }
  ]
}

Manifest Schema

Each manifest supports:

  • methods — Per-method effects (or "*" wildcard for all methods)
  • getters / setters — Property accessor effects
  • constructors — Constructor effects (keyed by parameter signature)
  • defaultEffects — Fallback for any method not explicitly listed
  • namespaceDefaults — Default effects for entire namespaces

Effect Code Reference

CodeMeaningExample
cwConsole writeConsole.WriteLine, ILogger.Log
crConsole readConsole.ReadLine
fs:rFilesystem readFile.ReadAllText
fs:wFilesystem writeFile.WriteAllText
net:rNetwork readHttpClient.GetStringAsync
net:wNetwork writeHttpResponse.WriteAsync
net:rwNetwork read+writeHttpClient.GetAsync
db:rDatabase readDbContext.Find, SqlMapper.Query
db:wDatabase writeDbContext.SaveChanges, SqlMapper.Execute
env:rEnvironment readIConfiguration.GetSection
timeSystem timeDateTime.Now
randRandom generationRandom.Next, Guid.NewGuid
mutHeap mutationList.Add, ConcurrentDictionary.TryAdd
throwIntentional throwValidateAndThrow

Broader effects encompass narrower ones: fs:rw covers both fs:r and fs:w.

CLI Tools

Resolve effects for a method

Bash
calor effects resolve Console.WriteLine    # → cw
calor effects resolve DbContext.SaveChanges # → db:w
calor effects resolve ILogger.Log          # → cw
calor effects resolve JsonSerializer.Serialize # → [pure]

Validate manifests

Bash
calor effects validate
calor effects validate --project ./src

List all covered types

Bash
calor effects list
calor effects list --type ILogger

Reducing Manifest Burden with IL Analysis

For methods in your own assemblies (repositories, services, handlers), you can enable cross-assembly IL analysis instead of writing manifests. The compiler traces through the IL of your referenced DLLs and discovers effects automatically.

xml
<PropertyGroup>
  <CalorEnableILAnalysis>true</CalorEnableILAnalysis>
</PropertyGroup>

With IL analysis enabled, the compiler can trace UserRepository.Save()DbCommand.ExecuteNonQuery()db:w without a custom manifest for UserRepository. Manifests still provide the seeds (DbCommand.ExecuteNonQuery → db:w), and IL analysis propagates them through your code.

When to use each:

ApproachBest forCoverage
Built-in manifests.NET framework types (DbCommand, HttpClient, ILogger)~170 types
Custom manifestsEcosystem libraries, interface-heavy patternsPrecise, reviewed
IL analysisYour own assemblies with concrete call chainsAutomatic, best-effort

See the IL analysis guide for details.

Architecture: Why Manifests?

Calor is designed for AI agents as primary code authors. When an agent generates Calor code that calls DbContext.SaveChanges(), the compiler needs to verify the function declares §E{db:w}. Without manifests, every external call would be Unknown — defeating the purpose of effect enforcement.

Manifests are the primary resolution mechanism because they're curated, precise, and cover the interface-heavy patterns (ILogger, IConfiguration) where IL analysis has a 0% resolution rate. IL analysis complements manifests by handling concrete call chains in user code — the gap between "framework types covered by manifests" and "Unknown."