Skip to content

Add a production-named fixed tenancy registration API - #2539

Merged
woksin merged 5 commits into
mainfrom
feat/2478-fixed-tenancy
Aug 13, 2026
Merged

Add a production-named fixed tenancy registration API#2539
woksin merged 5 commits into
mainfrom
feat/2478-fixed-tenancy

Conversation

@woksin

@woksin woksin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Added

Fixed

claude added 2 commits August 13, 2026 01:12
DevelopmentTenantIdResolver never consulted IHostEnvironment - it simply
returned the configured tenant id, and the registration switch selected
it unconditionally. Single-tenant production deployments were therefore
using an API named for an environment it does not check, while that same
accessor drives Chronicle namespace and Arc Mongo database resolution.

UseFixedTenancy and FixedTenantIdResolver give that supported
configuration a truthful name. Purely additive: DevelopmentTenantIdResolver
keeps its public type and behavior by deriving from the new resolver, and
DevelopmentTenantId forwards to FixedTenantId so both configuration keys
read one value. UseDevelopmentTenancy is unchanged and not deprecated.

Also lists Subdomain and Fixed in the unknown-resolver-type message,
which omitted Subdomain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@woksin woksin added the minor label Aug 12, 2026
@woksin

woksin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer context, kept out of the body.

Premise verified on the branch, not from the issue. DevelopmentTenantIdResolver.Resolve() was => options.Value.Tenancy.DevelopmentTenantId; with no IHostEnvironment reference anywhere under Tenancy/, and HostBuilderExtensions.cs:103 selected it unconditionally from ResolverType.

Backward compatibility. Purely additive: Fixed = 5 is the next free enum value with 0–4 untouched; DevelopmentTenantIdResolver keeps its public name and constructor signature and now derives from FixedTenantIdResolver; UseDevelopmentTenancy is byte-identical and deliberately not deprecated. No existing public method gained a parameter — the C# hazard is that a trailing optional parameter is binary breaking because the default is baked into the call site, so UseFixedTenancy being a new method is what makes its optional parameter safe.

Configuration binding, checked rather than assumed. Binding is plain reflection ConfigurationBinder via Configure<ArcOptions>(...), which writes through public setters, so the forwarding property works and both Tenancy:FixedTenantId and Tenancy:DevelopmentTenantId land on the same value. Two specs bind a real ConfigurationBuilder and assert each key under both names. Caveat now documented in configuration.md and the XML docs: if a source supplies both keys, whichever the binder visits last wins, so supply only one.

Mutation evidence. Each apply→run→restore in one command with trap … EXIT, original bytes restored, verified by shasum -a 256 — all three matched.

  • Registration arm returns DevelopmentTenantIdResolver instead of FixedTenantIdResolverand_fixed_tenancy_is_configured RED on the exact-type assertion. SPECIFIC.
  • FixedTenantIdResolver.Resolve()"MUTANT"for_FixedTenantIdResolver RED 3/3, SPECIFIC.
  • DevelopmentTenantId setter made a no-op → the configuration-forwarding spec RED. SPECIFIC.

Two findings reported rather than papered over.

  1. A surviving mutation. The registration mutation's second assertion, should_resolve_the_configured_tenant_id, survives — with the arm pointing at DevelopmentTenantIdResolver, Resolve() still returns the right value because both types read FixedTenantId. Only the exact-type assertion distinguishes the two registrations, so ShouldBeOfExactType<FixedTenantIdResolver> is load-bearing; a ShouldBeOfType/is assertion there would be vacuous, since DevelopmentTenantIdResolver is a FixedTenantIdResolver.
  2. Killed by the wrong spec. Because DevelopmentTenantIdResolver no longer declares Resolve(), a mutation in FixedTenantIdResolver reddens the pre-existing for_DevelopmentTenantIdResolver specs collaterally. Those specs still pass and still have value — they pin the DevelopmentTenantId forwarding, which the setter mutation kills specifically — but they no longer pin anything unique to the derived type. That is inherent to one-implementation delegation, not something the specs can fix.

Gate. Debug --no-incremental, Arc.Core.Specs (builds Arc.Core plus both analyzer/generator projects): 0 errors, 0 warnings; 1936 passed / 0 failed, re-run after both commits. Release --no-incremental: 0 errors, 0 warnings for each of Arc.Core, Arc.Core.Specs, Arc, Arc.Specs, Chronicle, MongoDB, Testing, OpenApi, Swagger and TestApps/ArcCore. Zero generated-proxy churn — nothing under Source/JavaScript changed.

A full Arc.slnx -c Release build fails on ProxyGenerator.Specs with MSB3073: "yarn build" exited with code 1, which is environmental: that target shells out to yarn build and this worktree has no node_modules. No JavaScript was touched and yarn install was deliberately not run here.

Not verified: no runtime check that Fixed flows through to Chronicle namespace or Arc Mongo database resolution — only that the correct ITenantIdResolver is selected from DI. Other Cratis repos were not searched for consumers of DevelopmentTenantIdResolver that the new base class could affect. The two changed docs pages were not rendered.

@woksin woksin added minor and removed minor labels Aug 13, 2026
claude added 3 commits August 13, 2026 08:40
SSH.NET 2025.1.0, pulled in transitively by Testcontainers, has a
high-severity advisory (GHSA-q939-rpr3-3284) published 2026-08-12 that
fails restore under NU1903. Testcontainers 4.13.0 is the latest release
and still resolves the vulnerable version, so pin it here alongside the
existing transitive security pins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@woksin
woksin merged commit 1483c92 into main Aug 13, 2026
12 checks passed
@woksin
woksin deleted the feat/2478-fixed-tenancy branch August 13, 2026 07:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants