Repository navigation
refactor(arch): put module rules beside the code they describe - #1086
Conversation
Each of the 160 contract types declares its owner beside its own declaration. RealModuleLedger collects the marks of Domain, Application and Infrastructure by reflection, so the owner rows lose their Contract lists. A mark naming no owner is a registry error. The contract sets are identical to the old lists; their order is now ordinal, which no test reads. Refs #859
Each owner's [ModuleOwner] row and its outgoing [ModuleEdge] cells move from the assembly-level ModuleOwners.cs and ModuleEdges.cs onto one internal static class <Owner>ModuleRules in src/Cluckwork.Domain/Common/Architecture/Modules/<Owner>.cs. The analyzer finds the classes by walking Domain's types; RealModuleLedger reads them by reflection and fixes the order (owners by design 3.4, edges by from, to), so every row and the coupling matrix are unchanged. A cell on another owner's class is a registry error. CW1001, CW1002 and the test's undeclared-edge message name Modules/<from>.cs and print [ModuleEdge(...)]. Refs #859
|
No P0–P3 findings at I independently compiled the base commit's
Reproduced mutations, each restored:
CW1001 points to The analyzer never read contract lists on main and still does not. Test-only enforcement of removed marks matches #1085's stated scope: edge and namespace diagnostics, with architecture tests retaining authority. The decision docs explain this and introduce no copied issue-state claims. Two wording clarifications fall below P3: RealModuleLedger's introductory comment should limit “the analyzer reads too” to owner/edge rows; the decision record's diagnostic sentence should distinguish CW1001's insertion attribute from CW1002's deletion guidance. Applied Verification: solution build passes with zero warnings; all 377 architecture tests pass across 22 separately run classes; all 20 documentation tests pass. Every test command used Reproduction scripts, independent dumps, individual logs and detailed review notes are retained locally in |
Refs #859
Follow-up to #1085. Owner decision (2026-10-05): a module rule lives next to the code it describes, and stays central only when it describes a relationship. Behaviour does not change. The analyzer and every architecture test give the same results as on
main.Change map
Read the commits in order.
refactor(arch): mark module contract types with [ModuleContract][ModuleContract("<Owner>")]marks, placed by a codemod.RealModuleLedgercollects them from Domain, Application and Infrastructure by reflection. The owner rows loseContract. A mark naming no owner is a registry error.Inherited = falsekeeps nested subclasses of a marked record (FlockNameResolution.Foundand its siblings) out of the contract. Global using ofCluckwork.Domain.Common.Architecture(Platform) in Domain and Application.refactor(arch): one rules class per module beside its edgesModuleOwners.csandModuleEdges.csare deleted.Modules/<Owner>.csholdsinternal static class <Owner>ModuleRules { }with that owner's[ModuleOwner]and its outgoing[ModuleEdge]cells (text moved verbatim). The analyzer walks Domain's types instead of reading assembly attributes.RealModuleLedgerfixes the order: owners by design 3.4 (matrix layout), edges byfrom, thento(the old order). A cell on another owner's class is a registry error. The CW1001, CW1002 and CW1003 messages and the test's undeclared-edge message nameModules/<from>.csand print[ModuleEdge(...)].docs(arch): ...859-typed-rule-registries.md.src/AGENTS.mdparagraphs and one line in514updated.Mechanical noise: the marks (about 100 files, one line each) and the moved row text.
Names.
<Owner>ModuleRulesavoids the existing facade classesAccessModule,FarmModuleand the others. The classes live inCluckwork.Domain.Common.Architecture, so no new namespace segment is added (#985). A repo-wide grep found no*ModuleRulestype before this change.Not done, by design. The analyzer reads no contract today, on
mainor here. It reads only namespaces, exact namespaces, claimed types and edges. A removed[ModuleContract]is therefore red only in the tests. An analyzer contract rule would be new behaviour, so it belongs in a separate decision. Contract lists are now ordinal-sorted. The hand order is gone, and no test reads it. No contract type sits in an assembly the tests cannot see.Proofs
mainand on each headModuleLedgerRealTreeTests'LiveEdges(temporary CW1099 dump,dotnet build src/Cluckwork.Api --no-incremental)CLUCKWORK_REGENERATE_MATRIX=1dotnet build Cluckwork.slnApplication.Testscount (--list-tests)Mutations
Each mutation was applied, then restored.
dotnet build src/Cluckwork.Api-p:RunAnalyzers=false[ModuleContract]removed fromIFlockLookupmain)PeerContractRealTreeTestsandAdapterReachRealTreeTestsred: contract bypass[ModuleContract("Farm")]onIFlockRepository(a Flocks type)mainAdapterReachRealTreeTestsandPeerContractRealTreeTestsred: not in a namespace 'Farm' owns[ModuleContract("Fram")](no such owner)ModuleLedgerRealTreeTestsred: names owner 'Fram', which has no ModuleOwner rowModules/FlockManagement.csModuleLedgerRealTreeTestsred: undeclared edge, same fileUpdateEggGradeHandleradded to EggOperations -> FarmModules/EggOperations.csModuleLedgerRealTreeTestsred: stale ledger rowFarmModuleRulesModuleLedgerRealTreeTestsred: cell sits on the wrong class[ModuleOwner]ModuleLedgerRealTreeTestsred: sits on OrphanRulesNot verified
The full test suite was not run (host rule: filtered runs only); CI is the authority. Live IDE squiggles were not observed.