Repository navigation
test(arch): guard module-to-module calls against non-contract types #1023
Description
Activity
- addedenhancementNew feature or requestNew feature or requestepic-514Modular monolith architecture (#514)Modular monolith architecture (#514)track:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation#514 Track C — moves production code; UNSCHEDULED, needs authorisation
on Oct 2, 2026 - added a commit that references this issue
on Oct 3, 2026 PR #1033 ships the Access-independent part of this issue. That covers the
seamandtypesledger schema with their errors,AdapterReachScanner.ScanPeers, andPeerContractRealTreeTestsover every module, replacing the Commerce-only check from #1030. It also adds Farm's seam (IAccountRepository,Domain.Accounts.Account,Domain.Accounts.UserRoleAssignment), the fixtures, and the decision recorddocs/decisions/1023-peer-contract-guard.md. The PR saysRefs #1023, so this issue stays open for the remainder.Remaining, after #857 E declares Access's contract:
- Claim
Cluckwork.Application.Common.IIdentityProviderandCluckwork.Application.Common.IStepUpGrantServicefor Access throughowners.Access.types. Measured onmainat 7bb45cc, the claim adds 4 adapter rows (Auth Logout and StepUp,MeEndpoints.GetMe,UserEndpoints.ListUsers) and changes the coupling matrix. With a placeholder Access contract, it finds 2 violations between modules, both inConfirmSaleHandler(IIdentityProviderandIUserRoleAssignmentRepository), and 7 adapter bypasses (Auth Login, Logout, Refresh and StepUp,GetMe,ListUsers,SimulationDataSeeder.ctor). [C] #514 slice 15: Access contract — security hold point, runs last #857 B, C and E remove all of these. - Precondition: [C] #514 slice 15: Access contract — security hold point, runs last #857 E moves
SimulationDataSeeder'sCreateUserAsyncontoIAccessOperations(owner decision). Otherwise the seeder becomes an adapter bypass once Access ownsIIdentityProvider. - Before claiming, re-run the measurement at E's head. With the two claims in the ledger,
AdapterReachRealTreeTestsandPeerContractRealTreeTestsmust stay green. - Mutation to prove it: with the claim and Access's contract in place, adding
IIdentityProviderto a Finance handler must turnPeerContractRealTreeTestsred. Removing thetypesrow must turn it green.
- Claim
Amendment from #857 E2 (PR #1060)
PR #1060 implements the remaining Access work from the 2026-10-03 handoff. It claims
Cluckwork.Application.Common.IIdentityProviderandCluckwork.Application.Common.IStepUpGrantServicethroughowners.Access.types, with Access's final 28-entry contract. Simulation creation now usesIAccessOperations.CreateUserAsync, so its constructor no longer bypasses the claimed identity port.Measured at E2 head
3731894a, integrated with main20198660: the full Application suite passes 722/722; the peer walk covers 1070 members, and the adapter walk covers 402 adapters with 139 non-empty reaches. The generated matrix is current. Each claim is tested separately: a Finance member taking that port fails the named peer-contract assertion; removing only its claim makes the same probe pass. Removing either claim also fails its named ownership-presence assertion. Generic Access bypass and recursive aggregate-leak probes fail too.Remaining: no implementation acceptance criteria remain after this PR merges. #1033 supplied the schema, peer walker, Farm seam and original mutation proof; #1060 supplies the deferred Access claims and their precondition/proofs. GitHub's closing-issues API confirms that #1060 closes #1023 as well as #857. The PR is open and awaits CI and independent Astra plus Opus security review; it has not been merged.
Deliberate limits: method-body uses, return/property/field uses and Platform-helper indirection remain the accepted limits documented in
docs/decisions/1023-peer-contract-guard.md. This issue's parameter/service-resolution acceptance criteria do not promise those additional checks.
Gap
The contract guards from #849 (and #851, #852) check adapters only: endpoints, CLI verbs, jobs and seeders. Nothing checks module-to-module calls. After #852, no handler outside Flock Management injects
IFlockRepositoryorIBirdMovementRepository. But nothing stops a future peer handler from injecting one again, and CI stays green when it does.The same holds for Finance, Farm and every later contracted module.
Why it was deferred
A constructor-parameter check over all of
Cluckwork.Applicationis cheap. But Farm deliberately keepsIAccountRepositoryandDomain.Accountsoutside its contract, as the stable seam every module calls directly (#851, its 2026-09-26 audit). A blanket rule would fail on that. The ledger schema needs a per-owner opt-in, or a per-owner list of seam types peers may use, before the check can be written. Recorded as a known gap indocs/decisions/852-*(PR #1021).Done when
module-ledger.jsoncan declare, per owner, which of its types peer modules may reach (its contract plus any deliberate stable seam).IFlockRepositoryinto a Finance handler goes red, and Farm'sIAccountRepositoryseam stays green.