The R 100 000 "Maintenance" section never had a home. On the workbook's Cost
sheet it is cell I97 — an orphaned figure with no section header above it and
no breakdown under it, sitting inside the sheet's own grand total of
R 1 110 486,87. Round 3 gave it a section, a card, a page and a sidebar entry
(7194e78). It was never a cost: nothing was ever attached to it.
The client ruled on 2026-11-05 that the app works from LIVE data and the
workbook is only history. So Maintenance comes out completely:
- the live PocketBase row (section "Maintenance", R 100 000) is deleted
- the seed row is removed from data/source/overheads.json, so a re-seed
cannot bring it back
- the card entry, the /expenses/maintenance page and the sidebar link
are removed
- data/source/overheads-extra.json — a DEAD file nothing imported (zero
references anywhere in the repo; the seed reads overheads.json
explicitly and only globs invoice-*.json) holding the round-2
Insurance/Tracking sections plus a mislabelled duplicate
"{ section: Cash Floats, description: Maintenance, incl: 100000 }" —
is trashed
Both pages now read the same money: /expenses and /overheads both show
R 580 485,37, over 6 categories and 25 lines. The vehicle/overhead split
under the headline is R 143 326,36 tied to a vehicle and R 437 159,01
company overheads.
The reconciliation against the workbook gains a third itemised delta, and it
is a SUBTRACTION this time, because the workbook's grand total still counts
the orphan cell:
R 1 110 486,87 workbook grand total (Cost sheet row 98)
- R 415 523,94 machine instalments — they live on each asset
- R 19 800,00 the workbook's own Personnel total is R 19 800 too high
+ R 5 322,44 Santam — the app keeps it, the workbook's subtotal skips it
- R 100 000,00 Maintenance — an orphan cell, removed from the app
= R 580 485,37 the roll-up
scripts/verify-cost-reconciliation.mjs now carries KNOWN_MAINTENANCE_REMOVED
and still exits 0 with R 0.00 unexplained. The note on /overheads is
generated from the constants, never typed, so it cannot drift. The three
signs matter: Personnel comes OFF, Santam goes back ON, Maintenance comes OFF.
AGENTS.md records the live-data ruling and renames the section heading from
"the cards are built FROM the sheet" to "FROM THE ROWS", since the sheet is
history and the rows are the truth.
The Subscriptions VAT errors (Charl - CCS, Buildsmart Yearly update) are
untouched — still flagged, still failing the seed loudly, still the client's
to fix.
Verified: typecheck clean; 162 tests (156 pass, 0 fail, 6 skipped);
production build clean and /expenses/maintenance gone from the route list;
reconciliation exits 0 with R 0.00 unexplained; /expenses and /overheads
both read R 580 485,37 at 375x812 with no overflow and no dead links.
Part A — Round 3 regressions:
- A.1: verify-cost-reconciliation.mjs now loads .env.local, wraps in try/catch, exits 1 on any failure
- A.2: Vehicle Costs restored Santam — back to R 14,996.31 (matches row-by-row sum)
- A.3: incl<excl / VAT math / VAT>20% now FATAL — seed exits 2 with full report (4 anomalies on Charl-CCS + Buildsmart)
- A.4: Reconciliation target R 694,962.93 with both deltas itemised (Personnel +R19,800, Santam -R5,322.44, unexplained R 0.00)
- A.5: 'Lines' view uses actual lines (recharts LineChart), no fills; fills only in 'Cost stack'
Part B — Mobile standard:
- B.1: header wraps to 2 rows on mobile; YTD badge cards stack; page-wide overflow eliminated on /income, /charts, /overheads, /running, /expenses
- B.2: IncomeView card row layout stacked — name gets its own line on mobile, never truncated to single char
- B.3: Plant type filter is a <select> dropdown below lg (was 18 pills wrapping to 13 rows); same for sort on /income and /running
- B.4: Plant table first column sticky; -webkit-overflow-scrolling: touch; tables scroll inside their own container
- B.5: All buttons/links use min-h-[44px] tap targets; text below 12px is bumped where it matters
- B.6: Charts legend wraps below title; chart min-height h-44 → h-52 on mobile
- B.7: paddingTop env(safe-area-inset-top) on sticky header for notch/home indicator clearance
AGENTS.md created with the standing mobile checklist rule.
- A.1: fixed_expenses per month now threaded through to chart bucket
- A.2: null = no data (forecast), 0 = real plotted zero — distinguishable
- A.3: x-axis clipped to last actual month (IncomeAreaChart + IncomeBarChart + MachineLineChart)
- A.4: chart hues as CSS vars (--color-chart-income/fixed/running) — deeper steps of same hues, validated for CVD
- A.5: chart-type toggle (Lines / Cost stack / Profit) + localStorage persistence + dynamic legend
- A.6: profit on charts page comes from computeProfit()
- B.1: deleted /expenses/plant-overheads, /expenses/insurance, /expenses/tracking (all empty); added /expenses/vehicles; ExpenseSectionPage now has an empty state
- B.2: captured R 157,200 Personnel block, R 100,000 Maintenance, R 10,000 Petty Cash; roll-up reconciles to R 675,162.93 (= sum of non-Plant non-Santam rows; workbook grand total R 1,110,486.87 is internally off by R 14,477.56 vs its own rows)
- B.3: seed flags incl<excl, VAT>20%, broken VAT math — does NOT silently correct; 4 anomalies reported on Charl-CCS + Buildsmart
- B.4: noted that workbook Vehicle Costs subtotal skips Santam (app R 9,673.87 is correct; workbook is wrong)
- B.2 reconciliation assertion added to verify-cost-reconciliation.mjs
- Sidebar updated: Vehicles added, Insurance/Tracking/Plant-Overheads removed