Repository navigation
Description
Amended 2026-10-03 (#854, PR #1030):
SetStepperUnitHandlerreads conversions through Commerce'sIEggUnitConversionLookup; confirm's in-transaction role and assignment reads andAccountProvisioner's default-conversion insert were left for this slice. See this comment.
Amended 2026-10-02 (#853, PR #1028): Egg Operations has a contract;
EggGradeFloorPolicy'sRolesread andAccountProvisioner's default-grade insert were left for this slice. See this comment.
Important
Track C is unscheduled. These slices move production code, and the 2026-08 design's own status
line still governs: "proposed architecture; no production refactor is authorized by this
document." Filed so the analysis is tracked work rather than a comment, not as a commitment to
run it.
The 2026-09-14 re-plan
recommends gating Track C on evidence from Tracks A and B: if the #842 ratchet fires repeatedly in
real pull requests, these become worth their cost; if it stays quiet, they do not. Track C is
57–95 focused engineering days and produces no user-visible change.
Read the re-plan before picking any of these up. Several assumptions in the 2026-08 plan are
corrected there.
Authentication, users, roles, assignments and refresh credentials behind a contract. Security hold
point — runs last, and only after everything else is proven.
Why last
Highest blast radius in the system. A mistake here does not surface as a failing test; it surfaces as
a revoked credential that still works. Access is deliberately sequenced after every lower-risk module
has proven the extraction pattern.
The invariant that must not weaken
CredentialEpochMiddleware performs a fresh database read per authenticated request (#364). The
round trip is the fail-closed guarantee. Do not cache it, do not move it behind a contract that
memoises, and do not change middleware order. Epoch 0 stays permanently retired, and a missing or
malformed claim stays always a mismatch.
Scope
Ordered, each independently revertible:
- Contracts around existing use cases, without rewriting Identity.
- Users and
meendpoints, and flock assignment, through contracts. - Auth endpoints, preserving cookies, JWT claims, and error and timing behaviour.
- Credential middleware onto an epoch-verifier port; prove the fresh read per request survives.
- Bootstrap, break-glass recovery and refresh purge behind Access; CLI and jobs call it.
- Internalise the Identity implementation and retire the exposed identity provider port.
Carries a known cross-module edge
SetStepperUnitHandler.cs:13 injects IEggUnitConversionRepository — a real Access → Commerce read
the 2026-08 matrix marks —. Contract call or declared edge; not accidental.
Done when
Every security suite passes unedited: credential epoch, suspension and the issuance window
(#579), retry boundary (#269), timing and lockout, bootstrap and break-glass drills (#265, #283),
per-farm email identity and cross-farm isolation (#530, #670). A dedicated security review before any
DI switch. Never dual-issue tokens during a switch.
- Change map in the PR (owner, 2026-10-01). The PR carries a before/after diagram of who calls whom, in the shape of the refactor(finance): put Finance behind an IFinanceModule contract #1010 and refactor(insights): read reports, exports and audit provenance through an Insights facade #1012 change maps: the adapters and module types before and after, any ledger edges added or removed, and the end state of any file another open slice also changes. Put it in the PR body or a PR comment as a fenced text block.
If OAuth (#788) is ever built
#796 re-establishes these same fail-closed checks on the OAuth path. Two authors in
CredentialEpochMiddleware at once is a blast radius #788 already declined — this slice runs after
#796, or #796 does not exist.
Activity
[-]#514 slice 15: Access contract — security hold point, runs last[/-][+][C] #514 slice 15: Access contract — security hold point, runs last[/+]mforce commentedon Sep 26, 2026
Audit 2026-09-26, against
mainat e47288cNo change needed. The invariant this slice is built around is intact.
CredentialEpochMiddlewarestill performs a fresh, uncached database read on every authenticated request except logout, and the comment above it still reasons explicitly about why it must stay uncached. No memoization has been introduced since the issue was filed.Census: Access is 59 files and 9 tables, the largest in the system, with four outgoing edges. The heaviest is Access to Farm at 19 symbols, of which 8 are provisioning or break-glass and 11 are ongoing runtime coupling, chiefly
IdentityProvidertaking an account-wide lock on every user-admin write. That edge is not retired by the Farm slice and lands here, which is consistent with this slice running last.test(arch): register Farm's compatibility exceptions after its contract
mforce commentedon Oct 2, 2026
Amendment from #853 (PR #1028). The body above is left as written.
Egg Operations now has a contract:
IEggOperationsModulefor adapters, and theIEggGradeLookupandIDailyEntryLookupread ports for peers. #853 deliberately left two Access-related callers alone:EggGradeFloorPolicy(Egg Operations) still resolves the caller's effective role throughDomain.Accounts.Rolesto decide who may set a grade's low-stock floor. The ledger's Egg Operations → FarmRedge names it. If this slice moves the role model, that caller moves with it.AccountProvisioner.ProvisionValidatedAsyncstill inserts a new farm's default grades (EggGrade.Defaults) inside the provisioning transaction, through the declared Access → Egg OperationsWedge. It does not go through the Egg Operations contract. Routing it there needs a write port that joins the provisioning transaction and never saves on its own, theIMortalityLedgershape from [C] #514 slice 10: Flock Management contract, eligibility and mortality ports #852.Rationale:
docs/decisions/853-egg-operations-contract.md, which lands with PR #1028.mforce commentedon Oct 3, 2026
Amendment from #854 (PR #1030). The body above is left as written.
Shipped:
SetStepperUnitHandlerreads packed-unit conversions through Commerce's newIEggUnitConversionLookupport, notIEggUnitConversionRepository. The port calls the sameGetByUnitAsync, so the query and the "active conversion only" check are unchanged. The body's "Carries a known cross-module edge" is now a contract call; theAccess -> Commerceledger edge stays declared with the same symbols.Left for this slice, unchanged:
ConfirmSaleHandler's fresh role read (IIdentityProvider.GetEffectiveRoleAsync) and the plain Worker's assignment read (IUserRoleAssignmentRepository.ListByUserAsync) stay a declaredCommerce -> AccessRedge. Any Access port that replaces them must keep both reads inside the confirm transaction, after the accountFOR SHAREand the orderFOR UPDATEand before the egg lot lock (Configurable Worker sales allocation scope by flock assignment #612, Sales: per-farm discount ceiling, with Owner/Manager approval above it #727).SaleAllocationPolicyTests.ConfirmSale_ParksOnTheAccountLock_…andConfirmSale_ParkedOnTheAccountLock_…pin that timing.AccountProvisionerstill insertsEggUnitConversion.Defaultsfor a new farm inside its own transaction, the declaredAccess -> CommerceWedge, as it still inserts default grades ([C] #514 slice 11: Egg Operations contract and the opaque stock seam #853).ApplicationUserstill storesDomain.Catalog.EggUnitfor the stepper preference.Rationale:
docs/decisions/854-commerce-contract.md, which lands with PR #1030.refactor(auth): refuse a default credential verdict and pin overlappi…
test(auth): run the fresh-read tests apart from every other test class
fix(users): refuse an unresolved tenant in IAccessLookup and pin the …
refactor(users): inject TenantContext into AccessLookup instead of ex…
test(auth): pin the refresh-cookie lifetime to configuration and on t…
test(accounts): parse the farm-directory caller guard with the build'…
refactor(access): centralize live reads and isolate lifecycle (#857 E…
refactor(access): declare fixture contracts and preserve actors (#857 …