Repository navigation
[C] #514 slice 14: Insights read facade for reports, exports and audit reads #856
Description
Activity
- addedsliceThin vertical work itemThin vertical work itemarea:apiAPI/endpoint layerAPI/endpoint layerpriority:tier4Deferred or speculativeDeferred or speculativesize:MA day or two; migration or a multi-state UIA day or two; migration or a multi-state UIepic-514Modular monolith architecture (#514)Modular monolith architecture (#514)
on Sep 14, 2026 - changed the title
[-]#514 slice 14: Insights read facade for reports, exports and audit reads[/-][+][C] #514 slice 14: Insights read facade for reports, exports and audit reads[/+]on Sep 14, 2026 - addedtrack:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation#514 Track C — moves production code; UNSCHEDULED, needs authorisation
on Sep 14, 2026 Audit 2026-09-26, against
mainat e47288cBody left as written. Two corrections and a scheduling change.
This slice should move near the front of Track C. Six endpoint files inject
IAuditEventRepositorydirectly to decorate responses with provenance: expenses, daily entries, egg grades, flocks, sales, and Insights' own audit endpoints. Five of those belong to other modules' slices, and one of them is the pilot's (ExpenseEndpoints, see the audit comment on #849). Until this facade exists, every one of those slices either keeps a direct injection it was supposed to remove, or blocks. Scheduling this at slice 14 of 17 means five later slices inherit the same unresolvable item. Recommended position is immediately after the pilot.Correction: this facade creates six declared edges, it does not remove any.
ReportQueries,ExportQueriesandAuditEventRepositorylive inCluckwork.Infrastructure.Repositories, which the ledger maps to Platform, the free hub. That is the only reason the Insights row is empty today. Move them into an Insights-owned namespace as scoped and the module ledger will require Insights to Flock Management, Egg Operations, Commerce, General Inventory, Finance and Farm. The empty row becomes a mostly-Rrow. That is arguably the point, since governed cross-owner reads become visible, but the issue does not say it and anyone sizing from the current text will be surprised.Correction: there are no cross-owner SQL joins. Every "cross-owner read" here is method-level assembly of several single-owner queries in C#, never a join spanning owner tables.
GetProfitAsyncruns one Commerce query, one Finance query and one Farm query and does the arithmetic in memory.ExportQueries.GetDatasetis twenty single-table switch arms. The audit provenance CTEs joinAuditEventsonly to itself. This matters for the design: the facade is composing reads, not hiding a join, so it can be a thin aggregator rather than a query-rewriting layer.Premises that hold. No writes anywhere in the three classes: no
SaveChanges, noExecuteSql. The repeatable-read export snapshot is stillExportQueries.BeginConsistentReadAsyncon a separate non-retrying context. The surface is about nine methods in three groups: four report methods, two export methods, two audit reads.
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.
Reports, exports and audit reads behind a read-only facade that may query across owners.
The principle
A controlled cross-owner read is a privilege, not a dependency from one writer on another
writer's repository. Insights may execute documented
AsNoTrackingSQL across owner tables through arestricted read session, including the current repeatable-read export snapshot.
It may not expose
IQueryable, entities or the context, and may not callSaveChanges. Theseam-surface guard (#847) already enforces the type half of that.
The alternative — Insights calling every module repeatedly to assemble a report — was rejected in the
2026-08 design and should stay rejected: shallow interfaces, poor query plans, and an inconsistent
snapshot across calls.
Scope
IInsightsModulereturning materialised or streamed DTOs.ReportQueriesandExportQueries— both insrc/Cluckwork.Infrastructure/Repositories/,not
Persistence/— without changing their SQL semantics.transaction; audit is an atomic write, not a decoupled event.
Done when
Report totals, tenant isolation, exact export datasets, the repeatable-read snapshot, cancellation
and stream-failure behaviour all pass unedited. SQL byte-identical where it moved. A guard
forbids writes and
IQueryableon the read session.Ordering keys must not shift:
AuditEventsorders byOccurredAtUtcthen the shadowSequence(#508), and business lists order by business date,
CreatedAtUtc, thenSequence(#819).Explicitly not in this slice
The audit writer. Projections or read models built from events — not needed, and not a way to
manufacture module purity.