Guidance for AI agents (and humans) working in this repository. Read this
before making changes, then read the relevant plan in .plans/outstanding/.
CompaniesHouse.NET is a .NET client SDK for the
Companies House Public Data API.
It is published as two NuGet packages:
CompaniesHouse— the core client (CompaniesHouseClient) and all request/response models.CompaniesHouse.Extensions.Microsoft.DependencyInjection— DI helpers for registering the client withIServiceCollection.
We are building a new major version on the prerelease branch. Breaking
changes are expected and welcome. The old surface lives on master and can be
referenced for behaviour, but we are rebuilding the client endpoint-by-endpoint
from the official API documentation rather than porting the old code verbatim.
The single source of truth for the work is .plans/. Do not freelance a
large redesign — pick up an outstanding plan, refine it if needed, and execute
it.
These are settled for the new major version. Do not reverse them without updating the relevant plan and calling it out explicitly.
- Multi-target
net8.0;net9.0;net10.0. Nonetstandard, nonet45. Drop theMicrosoft.NETFramework.ReferenceAssembliesandMicrosoft.Net.Httpreferences. System.Text.Jsononly. Remove every reference toNewtonsoft.Json(see issue #188). No new dependency on Json.NET in any project, including tests.- No plain C#
enums on the wire. Every API "enum" is modelled as a string-backedreadonly record structthat preserves the raw value and never throws on an unrecognised value. See.plans/outstanding/03-string-backed-value-types.mdand the design blog post: https://kevsoft.net/2026/06/28/enums-in-api-contracts.html. - Enum values are generated, not hand-written. A Roslyn source
generator produces the string-backed types from the Companies House
api-enumerationsYAML (pulled in as a git submodule) plus our own local "extra" lists. We ship a new package version to pick up new values — we do not hand-edit generated types. See plans04and05. CompaniesHouseClientstays the entry point. Every capability hangs off it as its own focused sub-client (e.g. search, company profile, officers), each behind its own interface, exactly as today.- DI uses
IOptions<>. The DI package usesAddOptions,IConfigurationbinding and validation, with overloads to configure the client several ways. See.plans/outstanding/02-di-extensions-ioptions.md. nullablereference types enabled across all projects.- Test stack: xUnit + Shouldly. No NUnit, no FluentAssertions (license
changed to a paid tier from v8). Use
[Fact]/[Theory]/[MemberData]andIAsyncLifetimefor async setup/teardown; assert with Shouldly's.ShouldBe(...)family. For deep object-graph comparisons against test fixtures that hold raw wire strings, use the repo's ownEquivalencyAssertionExtensions.ShouldBeEquivalentTo(...)helper inCompaniesHouse.Tests(bridges enum <-> wire string, no FluentAssertionsIEquivalencyStepneeded).
src/
CompaniesHouse/ core client + models
CompaniesHouse.Extensions.Microsoft.DependencyInjection/ DI helpers
(planned) CompaniesHouse.SourceGenerator/ enum value-type generator
tests/
CompaniesHouse.Tests/ unit tests
CompaniesHouse.IntegrationTests/ hit the real API (needs key)
CompaniesHouse.ScenarioTests/ end-to-end behaviour
CompaniesHouse.Extensions.*.Tests/ DI tests
samples/SampleProject/ runnable usage sample
external/api-enumerations/ (planned) git submodule
spec/swagger.json local CH OpenAPI 2.0 entrypoint
spec/upstream/developer-specs.company-information.service.gov.uk/... vendored $ref specs
CompaniesHouse.slnx solution (XML .slnx format)
.plans/ the work breakdown (read this)
spec/swagger.json is now the root local spec file. Its $ref graph is
rewritten to local paths under spec/upstream/ so spec-driven work (for
example, model nullability decisions) can run offline and deterministically.
- File-scoped namespaces,
ImplicitUsingsenabled,LangVersionlatest. - Warnings are errors (
TreatWarningsAsErrors=true) — keep the build clean. - One public type per file; interface
IThinglives next toThing. - URLs are built with small, testable URI builder types (see
src/CompaniesHouse/UriBuilders). Keep this pattern for new endpoints. - Async methods take a
CancellationToken(defaulted) and end inAsync. - JSON property names come from the API (snake_case); map with
[JsonPropertyName(...)]or a snake_case naming policy — never rename the wire contract. - Prefer central package management: versions live in
Directory.Packages.props, not in individual.csprojfiles.
Run from the repository root.
dotnet restore
dotnet build -c Release
dotnet test -c Release # unit + scenario tests
dotnet format --verify-no-changes # style gateIntegration tests need a Companies House API key in the api_key environment
variable and are skipped/failed without one — do not treat their absence as a
regression when working offline.
- Pick up a plan from
.plans/outstanding/. Work in the order implied by the numeric prefixes (foundation first) unless the plan says otherwise. - Keep changes surgical and endpoint-scoped. Build the client up gradually; do not rewrite everything in one pass.
- Update the plan as you learn. Plans are living documents — refine tasks, record decisions, and note open questions.
- When a plan is fully delivered and verified, move its file from
.plans/outstanding/to.plans/completed/in the same change. - Design for the recurring issues. A huge share of historical bug reports are "new enum value broke deserialisation" (#168, #185, #186, #197, #200, #201, #209, #218) and "missing field on a response" (#205, #206, #211, #212, #217, #221). The string-backed value types and generator exist to kill the first class entirely; model responses faithfully from the docs to avoid the second.
- Cite your sources. Reference the specific API doc page and/or GitHub issue in code comments and PRs when a decision is non-obvious.
- API reference: https://developer-specs.company-information.service.gov.uk/companies-house-public-data-api/reference
- Enumerations repo: https://github.com/companieshouse/api-enumerations
- Enum design rationale: https://kevsoft.net/2026/06/28/enums-in-api-contracts.html
- Issue tracker: https://github.com/kevbite/CompaniesHouse.NET/issues
On push to master or prerelease, the CI workflow automatically:
- Builds and packs both NuGet packages.
- Validates package metadata (README presence, nuspec readme tag).
- Pushes packages to NuGet.org.
- Creates a GitHub release with:
- Direct links to each package on NuGet.org (
nuget.org/packages/{PackageId}/{Version}) - Copy-paste
dotnet add packagecommands for both packages - Downloadable
.nupkgand.snupkgfiles as release assets
- Direct links to each package on NuGet.org (
Maintainers: you do not need to manually write release notes or link to NuGet. The workflow handles it automatically.
Commit in small amounts with a summary of what work we're building and not include the co-authorized by, however, do not push! Don't commit the .plans folder or the AGENTS.md