Repository navigation
refactor(access): name flock assignments through IFlockLookup, removing the last compatibility exception - #1070
Conversation
Owner, flock-scoped Worker, another farm and a user with no assignments, over farm-wide, Active, Archived, Depleted, missing and other-farm flock rows, in assignment-id order. Passes against the current left join.
AccessModule reads the user's assignments, then names their flocks with one IFlockLookup.GetDisplayNamesAsync read bounded to the list's flock ids. The repository's left join to the filtered Flocks set is gone, and with it the last compatibility exception. Access -> FlockManagement gains AccessModule; the coupling matrix is regenerated.
|
Review of record, including security, at One P3 documentation finding. No correctness, security, or structural code-quality findings. [P3] Update the canonical rules that still retain the deleted exception. src/AGENTS.md:28 still says the assignment repository's filtered Flocks LEFT JOIN retains #859; line 30 says the one remaining compatibility exception belongs to #859. Root AGENTS.md requires agents to read these rules before backend work, so the next routine assignment or registry change encounters instructions that contradict this implementation. Update both statements alongside the decision records, and correct the PR body's claim that I independently checked the following:
I reproduced both requested mutations myself:
Final targeted runs, one class at a time:
All runs used Applied |
|
Fixed the P3 in
Verification: Application.Tests |
Why
The last #850 compatibility exception:
UserRoleAssignmentRepository.ListByNameByUserAsync(Access) left-joined Flock Management's filteredFlocksset to name a user's flock assignments. The owner approved the fix on 2026-10-04. Access reads the assignments itself, then names them through Flock Management's contractIFlockLookup.GetDisplayNamesAsync, accepting one extra round trip. There is no name cache: names are per farm and per viewer, renames must show at once, and #271 allows one serving instance.Closes #859
Change map
Start with
AccessModule.ListFlockAssignmentsAsync, then the pin test.src/Cluckwork.Infrastructure/Identity/AccessModule.csListFlockAssignmentsAsyncreadsListByUserAsync, then callsIFlockLookup.GetDisplayNamesAsynconce with the list's flock idssrc/Cluckwork.Infrastructure/Repositories/UserRoleAssignmentRepository.cs,IUserRoleAssignmentRepository.csListByNameByUserAsyncand its join deletedsrc/Cluckwork.Application/Features/Users/IAccessModule.csUserFlockAssignmentmoves here, beside its only producer. Its old comment said a hidden flock's row gets a null flock id; it never did, and the pin now asserts the id is keptReferenceMarkers.cs,ShapeProbe.cs,UserEndpoints.csAssignmentProjectiontag and the comment about the single join removedRealModuleLedger.Exemptions.csCompatibilityExceptions = []RealModuleLedger.Edges.csAccessModule.UserFlockAssignmentleaves Access → Farm because its new file does not importDomain.AccountsArchitecture/Data/coupling-matrix.mdNamedRowProjectionTests.csFlockAssignmentList_IsIdenticalForEveryViewer;AssignmentProjection_IsASingleLeftJoinStatementbecomesAssignmentNames_AreOneBoundedFlockReferenceRead; the Worker-scope test callsIAccessModuledocs/decisions/859-*.md,858-*.md,src/AGENTS.mdsrc/AGENTS.mdlines 28 and 30 named the join and the remaining exception; both now state thatAccessModuleusesIFlockLookupand thatCompatibilityExceptionsis empty. No #632 registry changes: neitherBypassAllowListnorFilterFreeSetSitesnames either method, and the join used noIgnoreQueryFilters.Output parity
The pin is commit 1 and passed against the join before anything moved. It asserts exact rows in assignment-id order, with ids that sort in neither insertion nor flock order:
Another farm reading the same user gets no rows, and a user with no assignments gets no rows. Today's join and the new lookup both return a hidden flock's row with its flock id and a null name, never no row.
SQL, base and head
Captured from the pin's Owner and Worker calls.
Base, one statement:
Head, two statements:
Every difference:
AccountId AND flock-scopepredicate.GetDisplayNamesAsyncis plain LINQ over the filtered set with noIgnoreQueryFilters, so the visibility matches. It addsId = ANY(@ids), bound to the list's distinct flock ids (5 in the pin's Owner case).GetDisplayNamesAsyncreturns early on an empty id set.Mutations
Each was red, then restored.
GetDisplayNamesAsyncdrops only the flock-scope filter (IgnoreQueryFilterswith the tenant reinstated)AssignmentProjection_RespectsFlockScopeForAWorkerSkip(1))AssignmentNames_AreOneBoundedFlockReferenceRead,FlockAssignments_NameEachAssignmentAndLeaveFarmWideBlank, Worker-scope testAssignmentNames_AreOneBoundedFlockReferenceReadCompatibilityExceptionRealTreeTestsstale compatibility exception … (its trigger was #859)db.FlocksagainCompatibilityExceptionRealTreeTestsundeclared compatibility exception …ListByNameByUserAsync -> FlockManagementVerification
All runs were local, one class at a time:
NamedRowProjectionTests: 25/25 at base with the pin, and 25/25 at head.AccessAuthorizationContractTests,FlockScopeMiddlewareTests,FlockScopeTests,NamedEntityDiscoveryTests,RoleMatrixTests,SaleAllocationPolicyTestsandStepUpAuthTests(every class that calls the route): 6, 13, 16, 53, 18, 13 and 45 passed, none failed.Architecture,TenantBypassandDocumentation: green (417 and 381 tests across the two filtered runs).dotnet build Cluckwork.sln: 0 warnings.No UI change, so there are no screenshots. The response body is byte-identical.