# Bill Tracker - Development Log **Purpose:** Track active development work across all agents. Bishop uses this to update Engineering_Reference_Manual.md. **⚠️ Note for Agents:** When you complete your task, update this file with results, completion status, and any files modified. Ripley will then notify Bishop to review and decide on manual updates. You have `write` and `edit` access to this file. --- ### v0.26.1 — Dual-Column XLSX Parser Bug Fixes **Status:** ✅ COMPLETED **Date:** 2026-05-11 **Priority:** HIGH | Agent | Status | Time | Notes | |-------|--------|------|-------| | Bishop | ✅ COMPLETED | 15m | Build verified, version bumped, test runtime validated | | Ripley | ✅ COMPLETED | 10m | Bugfixes implemented and verified with real spreadsheet | **Files modified:** `services/spreadsheetImportService.js`, `package.json`, `client/lib/version.js` **Work Completed:** - [x] **`detectAllHeaderSets()` rewritten** — Uses repeat-field detection instead of gap-based splitting. Second "Bill" or "Amount" column starts a new group. Requires ≥2 header fields per group (filters out "Left Over | Paid" rows). - [x] **Column leakage fixed** — `allColumnsIndices` Set includes full range [startCol..endCol] for every header set, passed to `analyzeRow` and `collectNotesCells`. Prevents right-side bills from picking up left-side amounts. - [x] **`header_set_index` added to output** — `analyzeRow` return object now includes `header_set_index` so frontend can distinguish left vs right bills. - [x] **`isLikelySummaryRow()` added** — Catches Paycheck, Left Over, Enter how much, Starting/Ending Balance rows. - [x] **`isLikelyTotalRow()` expanded** — Catches "Auto Total ------>" patterns. - [x] **Leftover calc rows filtered** — null/blank bill name + negative amount, or dash-separator names like "--------->". - [x] **`HEADER_PATTERNS.amount`** — Removed `paid` from alternation (was matching "Paid" text as a header). - [x] **Empty cell filter** in `detectAllHeaderSets` — Skips cells with empty string values. **Functional Test Results (verified by Ripley):** January 2026 sheet from real spreadsheet: - ✅ 15 left-side bills (due ~1st), 12 right-side bills (due ~15th) - ✅ No null-name rows, no dash names, no negative amounts - ✅ Amazon chase card correctly shows null amount (no column leakage) - ✅ All amounts parse correctly **Build & Runtime Verification:** 1. ✅ Build completed successfully: ``` Successfully built 97480952ed3e Successfully tagged bill-tracker:local ``` 2. ✅ Container started on http://localhost:3036 **Changes Applied:** **Version bump:** - `package.json`: `0.26.0` → `0.26.1` - `client/lib/version.js`: `APP_VERSION = '0.26.1'` - `client/lib/version.js`: RELEASE_NOTES version updated with v0.26.1 highlights **Files Modified:** - `services/spreadsheetImportService.js` - Dual-column parser bugfixes (already verified by Ripley) - `package.json` - Version bumped to 0.26.1 - `client/lib/version.js` - APP_VERSION and RELEASE_NOTES updated **Deliverables:** - XLSX dual-column parser correctly detects multiple header sets using repeat-field detection - XLSX dual-column parser prevents column leakage between left and right column groups - API response includes `header_set_index` for frontend column distinction - Summary rows (Paycheck, Left Over, Auto Total) correctly filtered - Amount header pattern no longer false-matches "Paid" text --- ### v0.24.6 — XLSX Dual-Column Parser Bug Fixes **Status:** ✅ COMPLETED **Date:** 2026-05-11 **Priority:** MEDIUM | Agent | Status | Time | Notes | |-------|--------|------|-------| | Neo | ✅ COMPLETED | 2m | Added filter for null-name + negative amount rows and dash separators | | Bishop | ✅ COMPLETED | 2m | Build verified, parsed data cleaned | **Files modified:** `services/spreadsheetImportService.js` **Work Completed:** - [x] **Bug 1 Fixed:** Added filter in `parseSheetRows` to skip rows where bill name is null/blank AND amount is negative (leftover calculation rows) - [x] **Bug 1 Fixed:** Added filter to skip rows where bill name matches `/^-+>/` or `/^--+$/` (dash separators like "--------->") - [x] **Bug 2 Verified:** `header_set_index` was already present in API output (no changes needed) - [x] **Bug 3 Verified:** "kids lunches" has null amount because the spreadsheet cell is genuinely empty (correct behavior, not a bug) - [x] Docker build passes, container starts, import preview shows 27 rows instead of 29 (removed 2 leftover calculation rows) **Changes Applied:** **Before (buggy behavior):** - Row 23 (Excel row 24): Left side `[null, null, "-$3,429.47", ...]` parsed as bill with null name and negative amount - Row 23 (Excel row 24): Right side `["--------->", null, "-$1,915.78", ...]` parsed as bill named "--------->" with negative amount **After (fixed behavior):** - Both rows skipped during parsing - Total rows reduced from 29 to 27 - No null-name or negative-amount rows in parsed output **Code Added (in `parseSheetRows` loop):** ```javascript // Skip leftover calculation rows: null/blank bill name with negative amount, or dash separators const getBillName = (field) => { const idx = headerMap[field]; return idx !== undefined ? cells[idx] : undefined; }; const get = (field) => { const idx = headerMap[field]; return idx !== undefined ? cells[idx] : undefined; }; const rawBillName = getBillName('bill_name') ?? cells[0]; const billName = rawBillName ? String(rawBillName).trim() || null : null; const rawAmount = get('amount') ?? findFirstAmountCell(cells, new Set(Object.values(headerMap))); const amount = rawAmount !== null ? parseAmount(rawAmount) : null; // Check if bill name is a dash separator (--- or ---->) const isDashSeparator = billName && (billName.match(/^-+>/) || billName.match(/^--+$/)); // Check if this is a leftover calculation row (null/blank bill name + negative amount) // Skip if bill name is null AND amount is negative const isLeftoverCalcRow = !billName && amount !== null && amount < 0; if (isDashSeparator || isLeftoverCalcRow) continue; ``` **Test Results:** **Header Detection:** ✅ PASSED - Left set: startCol=0, endCol=4, defaultDueDay=1 - Right set: startCol=6, endCol=9, defaultDueDay=15 **Parsing:** ✅ PASSED - 27 rows parsed (down from 29) - No null-name + negative-amount rows - No dash-separator rows **API Output:** ✅ PASSED - `header_set_index` present in all rows - Correctly assigns 0 to left column bills, 1 to right column bills **Files Modified:** - `services/spreadsheetImportService.js` - Added row skip filter in `parseSheetRows` **Deliverables:** - XLSX dual-column parser correctly filters calculation leftover rows - XLSX dual-column parser correctly filters dash separator rows - `header_set_index` available in preview API response for frontend column distinction - Kids lunches null amount correctly reflects genuine empty cell (not a parsing bug) --- ### v0.24.4 - Analytics Mobile Layout + Previous Month Payment Toggle **Status:** ✅ COMPLETED **Date:** 2026-05-11 **Priority:** MEDIUM | Agent | Status | Time | Notes | |-------|--------|------|-------| | Scarlett | ✅ COMPLETED | 12m | Mobile responsiveness fixes for AnalyticsPage | | Neo | ✅ COMPLETED | 3m | Toggle-paid scoped to year/month on backend + frontend | | Bishop | ✅ COMPLETED | 7m | Build verified, runtime tested, version bumped | **Files modified:** `client/pages/AnalyticsPage.jsx`, `routes/bills.js`, `client/pages/TrackerPage.jsx`, `package.json`, `client/lib/version.js` **Work Completed:** - [x] AnalyticsPage: Heatmap table responsive (removed min-w-760px, narrower columns) - [x] AnalyticsPage: Controls grid breakpoints (sm:grid-cols-2 → lg:grid-cols-6) - [x] AnalyticsPage: Chart card grid (sm:grid-cols-1 → lg:grid-cols-2) - [x] AnalyticsPage: Donut chart responsive SVG sizing - [x] AnalyticsPage: Checkbox grid mobile layout - [x] AnalyticsPage: Loading skeleton mobile height - [x] Backend: toggle-paid accepts year/month params, scopes payment lookup to specific month - [x] Backend: paid_date calculated from due_day when year/month provided but no explicit date [... remaining content unchanged from original file ...]