Skip to content

test(arch): guard module-to-module calls against non-contract types #1023

Description

@mforce

Amended 2026-10-04 (#857 E2, PR #1060): E2 implements both deferred Access types claims and their independent mutation proofs. #1060 completes this issue on merge; the original body is retained below. See this comment.

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 IFlockRepository or IBirdMovementRepository. 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.Application is cheap. But Farm deliberately keeps IAccountRepository and Domain.Accounts outside 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 in docs/decisions/852-* (PR #1021).

Done when

  • module-ledger.json can declare, per owner, which of its types peer modules may reach (its contract plus any deliberate stable seam).
  • A guard fails when an Application handler or service in one module takes a non-contract, non-seam type of another contracted module, as a constructor parameter or a service resolution.
  • Each rule is mutation-proven per AGENTS.md "Writing a guard": re-injecting IFlockRepository into a Finance handler goes red, and Farm's IAccountRepository seam stays green.

Activity

  1. added this to the Modular monolith milestone on Oct 2, 2026
  2. added
    enhancementNew feature or request
    epic-514Modular monolith architecture (#514)
    track:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation
    on Oct 2, 2026
  3. mforce commented on Oct 3, 2026

    @mforce
    OwnerAuthor

    PR #1033 ships the Access-independent part of this issue. That covers the seam and types ledger schema with their errors, AdapterReachScanner.ScanPeers, and PeerContractRealTreeTests over 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 record docs/decisions/1023-peer-contract-guard.md. The PR says Refs #1023, so this issue stays open for the remainder.

    Remaining, after #857 E declares Access's contract:

    • Claim Cluckwork.Application.Common.IIdentityProvider and Cluckwork.Application.Common.IStepUpGrantService for Access through owners.Access.types. Measured on main at 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 in ConfirmSaleHandler (IIdentityProvider and IUserRoleAssignmentRepository), 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's CreateUserAsync onto IAccessOperations (owner decision). Otherwise the seeder becomes an adapter bypass once Access owns IIdentityProvider.
    • Before claiming, re-run the measurement at E's head. With the two claims in the ledger, AdapterReachRealTreeTests and PeerContractRealTreeTests must stay green.
    • Mutation to prove it: with the claim and Access's contract in place, adding IIdentityProvider to a Finance handler must turn PeerContractRealTreeTests red. Removing the types row must turn it green.
  4. mforce commented on Oct 4, 2026

    @mforce
    OwnerAuthor

    Amendment from #857 E2 (PR #1060)

    PR #1060 implements the remaining Access work from the 2026-10-03 handoff. It claims Cluckwork.Application.Common.IIdentityProvider and Cluckwork.Application.Common.IStepUpGrantService through owners.Access.types, with Access's final 28-entry contract. Simulation creation now uses IAccessOperations.CreateUserAsync, so its constructor no longer bypasses the claimed identity port.

    Measured at E2 head 3731894a, integrated with main 20198660: 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestepic-514Modular monolith architecture (#514)track:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions