Seven Core/Interfaces cannot be replaced by a module, and the failure is silent
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 21m
- Merged PRs (30d)
- 307
Description
Section 6 of `CLAUDE.md` names `Core/Interfaces/*` as part of the package surface, so all of it reads as a module contract. Most of it is not one.
Module `ConfigureServices` runs at `ServiceCollectionExtensions.cs:113`. Core's own service registrations run from line 764 onward. In `Microsoft.Extensions.DependencyInjection` the last registration wins a single-service resolve, so core overwrites the module, not the other way round, unless core used `TryAdd`. It does that three times.
| Interface | Registration | Replaceable |
| --- | --- | --- |
| `IWorkflowAction` | multi, resolved with `GetServices` | yes, additive |
| `IContentLifecycleHook` | multi, no core implementation | yes, additive |
| `IEmailService` | `TryAddScoped` (766) | yes |
| `ISmsService` | `TryAddScoped` (767) | yes |
| `IDeviceGate` | `TryAddScoped` (830) | yes |
| `ISensitivityService` | `AddScoped` (768) | no |
| `IContentSourcingPolicy` | `AddScoped` (769) | no |
| `IContentWriter` | `AddScoped` (774) | no |
| `IOtpService` | `AddScoped` (796) | no |
| `IEmailVerificationService` | `AddScoped` (805) | no |
| `IEmailSettingsProvider` | `AddScoped` (811) | no |
| `ITemplateVariableExtractor` | `AddScoped` (858) | no |
A module author can implement one of the bottom seven, register it, ship it, and core silently wins. It compiles, it vets clean, nothing is logged, and their implementation is never called. That is the same silent-binding shape BaryoVM's `CLAUDE.md` records for `--sudo`: a registration that is used, by the binding, and by nothing else.
### What to change
Not `TryAdd` everywhere. Some of these should stay sealed, and the sealing should be deliberate rather than accidental.
1. **Decide per interface** whether it is a seam or an invariant, and say which in the XML doc. `IContentWriter` is the clear case for staying sealed: D6 says every write to content goes through one writer, and a module substituting its own drives straight through that invariant. `ISensitivityService` is arguably the same.
2. **`TryAdd` the ones that are seams.** One word each, no signature change, so no section 6 event.
3. **Log at startup when a module registers against a sealed one**, rather than discarding it silently. An author finding out at startup is the entire argument of `ModuleContract`.
4. **Add the test that would have caught this.** For each interface declared replaceable, register a fake through a module and assert `GetRequiredService()` returns the fake. That test fails today for seven of them, which is the point of writing it.
### Relationship to existing issues
#557 covers a module not being able to touch the request pipeline. This is the same class of gap one level down: not "a module cannot add middleware" but "a module cannot replace a service the contract says it can". D18 commits to what module authors are promised, and this is a case where the promise is currently implied by the file layout and not delivered by the container.
### Where I checked
`barakoCMS/Extensions/ServiceCollectionExtensions.cs`, lines 113 and 764 to 858, cross-checked against `barakoCMS/Core/Interfaces/`. `grep -c TryAdd` returns 3. Searched open issues for module service override and dependency injection. Nothing open covers it.
Contributor guide
Research direction
Start in barakoCMS/Extensions/ServiceCollectionExtensions.cs at lines 113 and 764-858, then compare those registrations with barakoCMS/Core/Interfaces/ and their XML documentation. Decide which interfaces are seams or invariants, update registrations and startup handling accordingly, and add module-fake tests asserting that replaceable services resolve to the fake.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend-api-design, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100