build(arch): report module edges and namespace ownership at compile time - #1085
Conversation
…ributes ZzMapParity proves the reflected attributes equal RealModuleLedger.Owners and Edges, in order (local run; a one-character reason edit turns it red). Refs #859
RealModuleLedger.Owners and Edges now reflect src/Cluckwork.Domain/Common/ Architecture, so the rows exist once. The undeclared-edge fix prints the attribute row to paste there. Refs #859
…x inventories Refs #859
|
No actionable P1, P2 or P3 findings at I reproduced the following independently:
The Domain placement is acceptable for this slice. The published The new analyzer files are 284 and 107 lines, with direct responsibilities and no suppression machinery. Keeping its semantic walk independent of the authoritative tests is justified by the agreed boundary. Ponytail review: Lean already. Ship. The decision record and scoped instructions explicitly preserve test authority and permit CW suppression. Added documentation does not copy mutable issue state. GUI editor squiggles and the unfiltered suite remain unverified; no new timing claim is made. Reproduction scripts, patches, raw row snapshots and logs are retained locally in |
Refs #859
Adds a Roslyn analyzer that reports undeclared module edges, stale edge rows and unowned namespaces while you edit and build. It is developer feedback only. Every architecture test is unchanged and stays the CI authority; no test calls the analyzer.
Where the rows live now (one copy)
The analyzer cannot read the test project, so the owner and edge rows moved from
RealModuleLedger.Owners.cs/RealModuleLedger.Edges.csto assembly attributes insrc/Cluckwork.Domain/Common/Architecture/:ModuleMapAttributes.csdefines[ModuleOwner]and[ModuleEdge].ModuleOwners.csholds the nine whole owner rows (namespaces, contract, implementations, seam, types).ModuleEdges.csholds the 22 edge cells.Domain is the one assembly every module compilation references. The analyzer reads the attributes from source while compiling Domain and from metadata elsewhere, so an edited row takes effect in the editor without rebuilding the analyzer.
RealModuleLedger.OwnersandEdgesnow read the same attributes by reflection, so every test keeps its logic and assertions; only the data source moved. The adapter, table, tier and compatibility rows stay inRealModuleLedger.*.csbecause nothing outside the tests reads them.Rejected shapes: an
AdditionalFilesdata file (brings back the file format #859 removed, needs a parser on both sides); one C# rows file compiled into both the analyzer and the tests (bakes rows into the analyzer DLL, so an IDE shows stale diagnostics until it reloads the analyzer).Review guide
Commits are in dependency order; read them one at a time.
ModuleOwners.cs/ModuleEdges.cswere generated from the C# rows. A throwaway parity test (in this commit, deleted in the next) compared the reflected attributes with the old arrays by JSON serialisation, order included; a one-character reason edit turned it red. Ran locally, not in CI.RealModuleLedger.csreflection; old row files deleted. The undeclared-edge fix now prints[assembly: ModuleEdge(...)]and namesModuleEdges.cs. This is the one test-side text change:ModuleLedgerTestsasserts the new row format, because the oldnew(...)row no longer pastes anywhere.src/Cluckwork.Analyzers/(core). PortsModuleLedgerScanner's attribution and #1071's semantic walk. Wired into Domain, Application, Infrastructure and Api asOutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all". Platform claimsCluckwork.Analyzers(besideCluckwork.AppHost). Roslyn central pin 5.9.0 → 5.0.0.ModuleEdgeAnalyzerTestsinApplication.Tests(no new test project, no matrix leg). Microsoft.CodeAnalysis.Testing; runtime references, no reference-pack download.lockfix.test.mjs.859-typed-rule-registries.mdnew section,src/AGENTS.mdparagraph,514mechanism note, root layout line.Mechanical noise: the two moved row files (~460 lines out, ~460 in) and lock files.
Diagnostics
Cluckwork.ApiNot shipped: the spike's suppression barriers (CW1003-suppression,
ModuleAnalyzer.targets) and its measurement toggle.#pragma/NoWarn/.editorconfigcan silence a CW id; the tests still fail. Registry validation, the global-using rule and the file floor stay test-only.CI behaviour change: a module-edge violation now fails
dotnet build(and so every leg's build) with CW1001 beforeModuleLedgerRealTreeTestsruns. The test still fails the same change when built with analyzers off (table below).Proofs
ModuleLedgerRealTreeTests'LiveEdges: 73 and 73,diffidentical5.0.0-2.26422.108)5.9.0-1.26423.113)--target build, pinned SDK image (10.0.401)dotnet format analyzers Cluckwork.sln --verify-no-changes --diagnostics CW1001 CW1002 CW1003 --severity errordotnet build src/Cluckwork.Api --no-incremental, 6 alternating pairs after a warm-up pairCluckwork.Domain.dllReleasedotnet build Cluckwork.slnclean; every Roslyn-using class passes: Architecture + TenantBypass +EggLotLockSqlTests+TransactionDelegateShapeTests445/445, integrationTrackedMutationReadTests2/2Application.Testscount--list-testsTenancyDocsFreshnessTests/Documentation 20/20, ImagePin 2/2lockfix.test.mjsMutations
dotnet build src/Cluckwork.Api-p:RunAnalyzers=false)CreateExpenseHandlergetstypeof(Domain.Sales.DiscountCeiling)ModuleLedgerRealTreeTestsred: undeclared edgeEggGradeFloorPolicyremoved from EggOperations -> Farm while in useUpdateEggGradeHandler(no Farm reference) added to that rowGoneHandler(exists nowhere) added to that rowCluckwork.Application.Features.NowhereAnalyzer unit tests, each red under a mutation of the analyzer:
UndeclaredEdge_IsCW1001,MemberAccessOnlyReference_IsAnEdgeMemberAccessOnlyReference_IsAnEdgeDeclaredEdge_IsCleanStaleRow_IsCW1002UnownedNamespace_IsCW1003UsingWithNoUse_IsClean(plain +using static)Not verified