Summary
Job.EstimatedLaborCost / EstimatedMaterialCost / EstimatedHours (the Job.Estimated* family) are never written by any production code path. Only the dev seeder populates them. POST /api/v1/jobs/{id}/recalculate-costs returns 204 and changes nothing.
Consequence
GET /api/v1/jobs/{id}/cost-summary returns a permanently-zero estimate on every job created through the API or UI, so every plan-vs-actual variance the product shows is meaningless. A costing feature that reads these fields (the planned standard-costing work) will inherit the zero.
Where
forge.api/Features/Jobs/CreateJob.cs: no estimate computed at creation, even when the part has routing (Operation.EstimatedMs) and a current BOM.
recalculate-costs handler: no write to the Estimated* fields.
- Sweep of
EstimatedLaborCost =/EstimatedMaterialCost =/EstimatedHours = outside tests and seeders returns only a report projection (Reports/GetTimeByOperationReport.cs).
Suggested fix
Compute the estimate at job creation and in recalculate-costs: hours from Σ routing EstimatedMs × quantity, labour from the work-center rate, material from the current BOM revision × standard cost. If the fields are not meant to ship yet, hide cost-summary's estimate columns rather than returning zeros that look like data.
Found by the 2026-08-30 arsenal-as-client gap audit; adversarially verified.
Summary
Job.EstimatedLaborCost/EstimatedMaterialCost/EstimatedHours(theJob.Estimated*family) are never written by any production code path. Only the dev seeder populates them.POST /api/v1/jobs/{id}/recalculate-costsreturns204and changes nothing.Consequence
GET /api/v1/jobs/{id}/cost-summaryreturns a permanently-zero estimate on every job created through the API or UI, so every plan-vs-actual variance the product shows is meaningless. A costing feature that reads these fields (the planned standard-costing work) will inherit the zero.Where
forge.api/Features/Jobs/CreateJob.cs: no estimate computed at creation, even when the part has routing (Operation.EstimatedMs) and a current BOM.recalculate-costshandler: no write to theEstimated*fields.EstimatedLaborCost =/EstimatedMaterialCost =/EstimatedHours =outside tests and seeders returns only a report projection (Reports/GetTimeByOperationReport.cs).Suggested fix
Compute the estimate at job creation and in
recalculate-costs: hours from Σ routingEstimatedMs× quantity, labour from the work-center rate, material from the current BOM revision × standard cost. If the fields are not meant to ship yet, hidecost-summary's estimate columns rather than returning zeros that look like data.Found by the 2026-08-30 arsenal-as-client gap audit; adversarially verified.