Commit Graph

43 Commits

Author SHA1 Message Date
null a31a774f95 chore: dead-code sweep — knip now clean and blocking in CI (Phase 5)
- delete root test-functional.js (imported uninstalled 'playwright' —
  could never run) and run-functional-test.js (legacy pre-Playwright)
- delete postcss.config.js + autoprefixer/postcss devDeps (tailwind v4's
  vite plugin owns the pipeline; the config had no plugins left)
- client/lib/money.ts: drop unused SUPPORTED_CURRENCIES; keep
  dollarsToCents exported + @public-tagged (API symmetry with
  centsToDollars — the sanctioned unit crossing)
- client/lib/billDrafts.ts: drop unused BillDraft type export
- move the two runtime seed JSONs (advisory filters, merchant-store
  matches; 7 MB) out of docs/ into db/data/ next to their loader;
  fresh-DB seed verified (5000+5000 rows); Dockerfile COPY . covers it
- knip.json: apply config hints, ignore the two CSS-side tailwind deps;
  npx knip exits 0 — added to CI as a BLOCKING step so unused
  files/exports/deps fail instead of rotting (this is how the WebAuthn
  ghost feature survived four QA cycles)

NOT deleted: client/public/img/doingmypart.jpg — flagged unused by the
code grep but actually referenced from HISTORY.md markdown that the
release-notes renderer serves (user catch). Lesson encoded in the QA
plan: asset-reference sweeps must include rendered markdown content.

Full ci green (252 server / 52 client / build / knip 0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:15:04 -05:00
null 10eaf73e90 chore(deps): pin typescript at 6.x (4k deferred) + oidc header comment v5 -> v6
TypeScript 7 typechecks this codebase clean, but typescript-eslint (peer:
'>=4.8.4 <6.1.0') cannot parse under it — eslint hard-fails. Pinned back
to the TS 6 line per the plan's incompatibility contingency; revisit when
typescript-eslint ships TS 7 support (tracked in the QA plan backlog).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:08:06 -05:00
null 1627e75b29 chore(deps)!: openid-client 5 -> 6 (4j)
v6 replaced the Issuer/Client classes with a functional API. The four
touchpoints rewritten: discovery() + ClientSecretBasic/Post for the cached
configuration, serverMetadata() for the admin config test,
buildAuthorizationUrl() (returns URL -> .href), and authorizationCodeGrant()
with pkceCodeVerifier/expectedNonce/expectedState for the callback exchange —
same validation surface (JWKS signature, iss/aud/exp/nbf, PKCE, state, nonce),
now enforced by the library's single grant call.

scripts/test-oidc-smoke.js: 44/44. Server suite 252/252.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:05:57 -05:00
null 522eaaf101 chore(deps)!: tailwind 3 -> 4 via @tailwindcss/vite (4i)
Official codemod (run in a clean worktree; docker-owned data/ blocks it
in-tree): tailwind.config.js inlined into client/index.css as CSS-first
@theme/@utility (config file deleted — v4 auto-detects content, which
also retires the .tsx-content-glob failure class from the TS migration),
33 template files' class renames applied, tailwindcss-animate loads via
the v4 @plugin bridge. Build runs through @tailwindcss/vite; postcss
config is now plugin-free. tailwind-merge -> 3 (v4 class model).

Visual-regression gate: only diffs were a ~2px global preflight shift +
the version string (baseline predated 0.41.1); re-baselined both login
screenshots in this commit after review. webauthn.probe confined to the
probe project (it enables WebAuthn for the seeded user, racing the
parallel UI projects' password logins).

e2e 27 passed, probe 18/18, PROD SMOKE PASS, typecheck/lint/tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:03:15 -05:00
null d8365d0e22 chore(deps)!: vite 8 (rolldown) + @vitejs/plugin-react 6 (4h)
Build drops 6.5s -> 0.65s. Three breakages found by the prod-smoke gate,
all fixed:

- rolldown removed object-form manualChunks -> function form in
  vite.config.mjs (same vendor split, all @radix/@tanstack grouped)
- the 4a 'send' bump rejects bare absolute paths: SPA fallback sendFile
  and both download routes (admin backup, user-db export) now use the
  root-option form
- rolldown minifies inline HTML scripts, breaking any CSP hash approach:
  the pre-paint theme script moved to client/public/theme-init.js so
  script-src 'self' holds with no inline hash to maintain

@testing-library/dom added (peer no longer auto-installed). Server 252,
client 52, probe 18/18, PROD SMOKE: PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:53:27 -05:00
null b76772b8eb chore(deps): eslint 10 stack — @eslint/js 10, react-hooks 7, react-refresh 0.5 (4f)
eslint 10's recommended set adds no-useless-assignment; fixed the three
real dead initial assignments it caught (version.cts updatedAt,
simplefinService claimUrl/accessUrl). Lint 0 errors; ratchet rules from
the standards batch carry over unchanged. Server 252, client 52.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:36:50 -05:00
null 8248575b1e chore(deps): low-risk majors — bcryptjs 3, sonner 2, lucide-react 1.x, concurrently 10 (4e)
bcryptjs 3 verifies existing $2a/$2b hashes (authService tests 7/7);
sonner 2 + lucide 1.x compile clean (typecheck + build); no icon renames
in our set. Server 252/252, client 52/52.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:35:39 -05:00
null af20d8de07 fix(deps)!: clear both high-severity vulns; CI audit gate raised to high
- nodemailer 8 -> 9 (GHSA-p6gq-j5cr-w38f, raw-option file-read/SSRF; we
  never use the raw option — notificationDelivery tests 13/13)
- xlsx -> official SheetJS dist 0.20.3 (prototype pollution
  GHSA-4r6h-8v6p-xvw6 + ReDoS GHSA-5pgg-2g8v-p4x9; npm registry line is
  abandoned at 0.18.5 with no fix). API-identical for our 8 call sites;
  import/export round-trip tests green. NOTE: the lockfile now pins
  https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz — CI runners and
  Docker builds need egress to cdn.sheetjs.com
- CI: npm audit --audit-level critical -> high (0 vulns as of this
  commit; highs can no longer pass silently — QA-B0-02)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:34:51 -05:00
null 94ed0b63f3 chore(deps): semver-safe update sweep (4a)
npm update across the Wanted column: Radix x11, react-query 5.101,
better-sqlite3 12.11, node-cron 4.6, express-rate-limit, framer-motion,
postcss/autoprefixer, fast-check, knip, lefthook, typescript-eslint,
vitest 4.1.10, @types/*. Full ci green (lint 0 errors, server 252,
client 52, build + PWA emit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:33:34 -05:00
null 35a0e4262e feat(db): Kysely typed-SQL POC — sync builder, proven parity (Pillar E)
De-risks the flagship. The app is synchronous (better-sqlite3) but Kysely
executes async, so db/kysely.cts uses Kysely as a TYPE-SAFE BUILDER only:
build the query (columns + user_id/deleted_at scoping + types checked at
compile time), `.compile()`, then run via sync helpers (all/get/run) on the
shared connection. No async ripple, no change to the sync model.

- Schema typed for `categories` (extend as adopted).
- tests/kyselyPoc.test.js proves byte-identical rows vs the raw SQL and
  that compiled SQL is parameterized (injection-safe).
- Pattern documented in CODE_STANDARDS; adopt for new/refactored queries;
  it type-enforces user-scoping (the biggest data-isolation win) and
  removes Row=any at the call site.

typecheck:server + check:server clean; 248/248 server tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:23:50 -05:00
null 0cad1626b1 test(money): property-based proof of cent-exact invariants (fast-check, Pillar F)
A financial app must prove its money math, not sample it. tests/
moneyProperties.test.js fuzzes utils/money.mts: toCents always integer;
dollars→cents→dollars == roundMoney (no drift); sumMoney is cent-exact;
integer cents round-trip; classic float-drift cases exact; invalid inputs
never yield a bogus number. 6 properties, all hold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:08:50 -05:00
null e7196abbdf fix(security): patch react-router open-redirect (non-breaking audit fix)
`npm audit fix`: react-router/react-router-dom 6.30.3 → 6.30.4 (moderate
open-redirect via protocol-relative URL), nodemailer 8.0.9 → 8.0.11.
Verified: server 232/232, client 50/50, build green.

Remaining audit findings are assessed non-actionable/mitigated (see
SECURITY.md): nodemailer `raw` send option is not used; xlsx is confined
to the import service with raw:false + size/content-type limits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:36:18 -05:00
null 5aaf292b2e ci(enforce): run the full gate in CI + add lefthook pre-commit + Knip
Pillar A enforcement so standards can't drift:
- .forgejo CI now runs format:check + lint (client+server) + typecheck
  (client) + typecheck:server, in addition to the existing check:server /
  tests / build. Previously lint and typecheck were NOT run in CI.
- lefthook pre-commit auto-formats staged files (prettier) + lints;
  pre-push typechecks. Auto-installed via the `prepare` script.
- Knip config (advisory `npm run knip`) for dead-code/unused-export
  review; the design-system ui/** is ignored to keep signal clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:27:03 -05:00
null f65138a114 chore(tooling): add Prettier + editorconfig, extend ESLint to the server
Standardization foundation (Pillar A):
- Prettier (.prettierrc.json, .prettierignore) + `format`/`format:check`
  scripts; style tuned to the house style (single quotes, semicolons,
  2-space, printWidth 100).
- .editorconfig for editor-level consistency.
- Extend the flat ESLint config to lint server `.cts` (Node globals, TS
  parser, "focused signal" rule set mirroring the client) so `eslint .`
  now covers both halves; `lint` → `eslint .`.
- Wire `format:check` into the `ci` script.

`npm run lint` is green (0 errors; pre-existing intentional patterns kept
as advisory warnings to ratchet later).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:23:53 -05:00
null f909e8f587 fix(docker): regenerate lockfile with npm 10 so `npm ci` works in build
The lockfile had drifted (written by local npm 11 / Node 25) into a form that
node:22-alpine's npm 10.9.8 reads as incomplete — `npm ci` failed with
"Missing: esbuild@0.28.1 from lock file", breaking the Docker image build
(Dockerfile now uses `npm ci`, not `npm install`). Regenerated the lock inside
node:22-alpine with its own npm so `npm ci` resolves cleanly (887 packages,
lockfileVersion 3). No dependency version changes — lockfile metadata only.

Verified: clean `npm ci` in node:22-alpine, full image build, container boots
migrations + workers, full QA (auth/CSRF/admin/security) and a live SimpleFIN
sync all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:44:23 -05:00
null 3973560e92 refactor(tracker): remove dead code and unreferenced files (plan A1)
- Delete unreachable in-file dialogs: MonthlyStateDialog block (setShowMbs
  never called) and inline PaymentModal in TrackerRow + MobileTrackerRow
  (setEditPayment only ever null; editing works via PaymentLedgerDialog).
- Delete fully-unreferenced files: tracker/EditableCell.tsx,
  ui/confirm-dialog.tsx, ui/separator.tsx (+ orphaned @radix-ui/react-separator).
- Simplify OverdueCommandCenter dead branch (early-returns null when empty).
- Correct stale roadmap/FUTURE docs: tracker keyboard nav is implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:35:13 -05:00
null 200c6373f5 feat(server-ts): migrate the money boundary to TypeScript on Node's native runtime (Phase 2)
Establishes the server-TypeScript foundation and migrates utils/money →
utils/money.mts with branded Cents/Dollars (mirroring the client), so the
cents↔dollars boundary — the origin of the reconciliation bug class — is a
compile error for any typed server caller.

Approach (no build step): Node 25 runs .ts natively (type-stripping).
Migrated modules use the explicit-ESM .mts extension (the project is
"type":"commonjs", which disables .ts ESM auto-detection) and are required
from the CJS callers via Node's require(esm) — verified working. Infra:
tsconfig.server.json (allowJs + checkJs:false → incremental like the
client), npm run typecheck:server, check:server extended to .mts, wired
into ci. 28 require sites repointed to money.mts.

typecheck:server clean; full server suite 225 + e2e probe 17/17 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:44:04 -05:00
null 1d3b7435a0 feat(server): upgrade Express 4.18 → 5.2 (Phase 2)
The maintained line, and it forwards rejected async handlers to the error
middleware natively — closing the Express-4 gap where a throw in an async
route became an unhandled rejection that hung the request (this is why no
throwaway asyncHandler wrapper was needed). Small surface: only three
bare-* routes needed the path-to-regexp-v8 named-wildcard form
(/api/*splat, /legacy/*splat, /{*splat}); no removed APIs in use, all
req.query uses are reads. Suite 225 + probe 17/17 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:25:00 -05:00
null 62a99fcaa4 build(lint): extend ESLint to .ts/.tsx via typescript-eslint
As files convert to TypeScript they must keep the react-hooks enforcement.
Added a .ts/.tsx config block using the typescript-eslint parser with the same
rules-of-hooks/exhaustive-deps/react-refresh rules; swapped core no-undef +
no-unused-vars (type-blind) for TS-aware equivalents. Verified 'npm run lint'
traverses .ts and flags issues there; 0 errors on the existing .ts files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:44:35 -05:00
null 907a407399 build(ts): TypeScript foundation — tsconfig + typecheck in CI (T1)
Adopting TypeScript incrementally (the move off plain JS). Upgraded jsconfig ->
tsconfig with strict + noUncheckedIndexedAccess, but allowJs + checkJs:false so
every existing .js/.jsx keeps resolving and running untouched — only .ts/.tsx
get type-checked. Added 'npm run typecheck' (tsc --noEmit) and wired it into
'npm run ci'. Installed typescript 6 + @types/react/react-dom 19. Client-scoped
(Vite resolves the '@/' paths); the CommonJS server is a separate TS story.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:33:26 -05:00
null 47c031f1e4 build(react): enable React Compiler (React 19 auto-memoization) (F2)
Added babel-plugin-react-compiler to the Vite React plugin. The compiler
auto-memoizes components/hooks at build time, so manual useMemo/useCallback
become largely unnecessary going forward (existing ones are left in place —
harmless, and the compiler adds the rest). Safe here because the codebase is
rules-of-hooks clean (enforced by eslint-plugin-react-hooks).

Validated: production build succeeds and the e2e probe passes 17/17 with the
compiler on — every authed page renders axe-clean and Tracker/Summary/Analytics
reconciliation holds. (Build time ~2.2s -> ~6.2s from the compiler pass; the
runtime win is automatic memoization.) The compiler ESLint rule needs
eslint-plugin-react-hooks v6 — a future upgrade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:20:26 -05:00
null c679022592 build(lint): add ESLint 9 + eslint-plugin-react-hooks/react-refresh (R1)
There was no linting at all — nothing enforced rules-of-hooks (conditional-hook
crashes) or exhaustive-deps (stale closures) across 125 client components/pages.
Added an ESLint flat config (eslint.config.mjs) scoped to client/, an 'npm run
lint' script, and the devDeps. First run: 0 rules-of-hooks violations (good),
6 errors + 13 exhaustive-deps warnings to work through next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 19:41:24 -05:00
null 127b69ffc2 chore(qa): vendor chunk splitting, remove unused markdown deps, remove dead totalInterestPaid (batch 0.41.0 QA cleanup) 2026-07-02 20:47:50 -05:00
null 029c227685 fix(qa): seed demo data amounts, bill amount validation, negative USD format, a11y aria-labels, Playwright E2E setup (batch 0.41.0 QA) 2026-07-02 20:36:09 -05:00
null d9a441dff6 feat(settings): auto-save preferences with live save status (batch 0.39.0)
Replace all Save buttons on the Settings page with debounced auto-save:

- useAutoSave hook: debounce with latest-payload-wins, flush() for blur,
  pending-edit flush on unmount, status machine (idle/saving/saved/error)
  with saved fading back to idle. Covered by 6 Vitest tests (fake timers).
- SaveStatus pill (framer-motion) in the page header and notification card
  headers — Saving…/Saved/Save failed.
- Timing per control: toggles/selects/channel ~150-400ms; typed inputs
  (email, URLs, grace period, drift pct) 900ms + flush on blur.
- Push token never auto-saves mid-type: saves on blur only, so a partial
  token can never overwrite a working one.
- Notification cards no longer refetch parent settings on save (would
  clobber in-flight edits under auto-save).
- Decision: no undo toast — settings are non-destructive and instantly
  re-editable; undo would add noise without safety.
- vitest include now picks up .jsx tests; jsdom + @testing-library/react
  added as devDependencies.
2026-06-12 02:08:42 -05:00
null dc49eb9633 feat(cashflow): safe-to-spend projection with timeline, vitest setup, package upgrades 2026-06-12 01:32:28 -05:00
null ec7869abbc feat: framer-motion page transitions and UI polish 2026-06-07 15:14:09 -05:00
null 6d60eebe1a chore: dependency updates and UI fixes (batch) 2026-06-07 14:23:19 -05:00
null 4f5a3d0cff feat: bank sync section, data sources route, subscription page updates, package updates 2026-06-07 02:03:00 -05:00
null 83e6afa9e6 feat(subscriptions): simplified SubscriptionsPage, inline actions, improved matching card, Service Catalog route
- Extracted known-service catalog to dedicated /subscriptions/catalog route
- Simplified main Subscriptions page to focus on tracked services + bank-backed recommendations
- Replaced inline Pause/Resume with Edit + MoreHorizontal dropdown on subscription rows
- Added 'Improve Matching' card linking to Service Catalog
- Vite proxy respects API_PORT env var for dev flexibility
- Added top_200_us_subscriptions_researched dataset
- Updated HISTORY.md with v0.35.0 changes
2026-06-06 22:09:34 -05:00
null 653dd72e12 feat: TOTP 2FA for login & profile setup flow 2026-06-04 04:10:14 -05:00
null 71dfbe36cc refactor: component splits, PWA support, CommandPalette
Component Splits:
  - AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
  - DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
  - TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)

PWA:
  - vite-plugin-pwa installed with NetworkFirst caching for API routes
  - Square PWA icons (192x192, 512x512, apple-touch-icon)
  - theme-color, apple meta tags, touch icon in index.html
  - Build generates dist/sw.js + Workbox runtime

CommandPalette:
  - Navigation commands, Add bill action, month jumps
  - Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
null 8cab248959 security fixes 2026-05-28 03:59:35 -05:00
null 9d933f70cc v0.28.01 2026-05-16 20:26:09 -05:00
null 0ba315bd32 v0.28.0 2026-05-15 22:45:38 -05:00
null 2ce5328fd2 v0.25.0: roadmap redesign, import CSRF fix, AdminDashboard removed
- RoadmapPage: kanban-style priority lanes, shadcn Collapsible/Tabs,
  lazy-loaded activity log, admin-only /api/about/roadmap + /dev-log endpoints
- Import CSRF fix: added x-csrf-token header to importAdminBackup,
  previewSpreadsheetImport, previewUserDbImport raw fetch() calls
- Removed AdminDashboard.jsx, replaced by RoadmapPage
- Added @radix-ui/react-collapsible + collapsible shadcn component
- Security audit by Private_Hudson: PASS (CSRF fix verified,
  admin endpoints gated, path traversal mitigated, XSS safe)
2026-05-11 21:42:36 -05:00
null d67fe6e61d v0.22.0: React Query Migration
- Added @tanstack/react-query and @tanstack/react-query-devtools
- Created useTracker, useBills, useCategories custom hooks (useQueries.js)
- Migrated TrackerPage from manual useState/useEffect to useQuery
- Added QueryClientProvider with 2min staleTime, 1 retry, refetchOnWindowFocus: false
- Added ReactQueryDevtools for development
- Fixed error handling: useRef pattern prevents duplicate toast notifications
- Replaced load() callback with refetch() from useQuery
- Hudson security audit: 4/5 PASS (1 FAIL fixed: error handling toast duplication)
2026-05-10 03:10:43 -05:00
null 6c7d481494 feat: add admin about page with security hardening
- Add /api/about-admin endpoint (admin-only, path traversal protection, content redaction, error sanitization)
- Add /admin/about route with RequireAuth admin guard
- Add adminActionLimiter rate limiting on about-admin endpoint
- Add rehype-sanitize XSS prevention in AboutPage.jsx
- Add aboutAdmin API client endpoint
- Create HISTORY.md with version bump convention (patch/minor/major)
- Update Engineering Reference Manual with about-admin docs and security measures
- Add INIT_REGULAR_USER/INIT_REGULAR_PASS env vars to docs
- Update FUTURE.md with critical regular user env var item
2026-05-09 16:25:12 -05:00
kaspa 4d1709aea3 push 2026-05-09 13:03:36 -05:00
_null 3228332e8c push 2026-05-04 23:34:24 -05:00
_null d1efeece04 push 2026-05-04 20:12:57 -05:00
_null 969139251d calendar 2026-05-04 13:14:32 -05:00
_null b9d1366d46 initial commit 2026-05-03 19:51:57 -05:00