{"slug":"AbramsOS","total":117,"limit":100,"offset":0,"since":null,"commits":[{"hash":"dbe3376","date":"2026-09-11 10:42:28 -0700","author":"Steve","subject":"reviews: arm launcher (sets REVIEWS_EXECUTORS_LIVE + openclaw path)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"2822732","date":"2026-09-11 10:21:43 -0700","author":"Steve","subject":"reviews: native Amazon review + seller thank-you feature over AbramsOS purchases","body":"- migration 0017 review_draft (keyed to purchase, additive, UNIQUE(purchase,target))\n- lib/reviews/{drafts,model,executors}: draft generation (short product names, variety, deslop) from purchase.raw_extract, DB-backed, content-seal on approve\n- routes/reviews.js + views/reviews.ejs: /reviews batch-review page (read all, select, approve-in-one-pass), paced per-item gated posting (no bulk fire, no auto-approve)\n- gated edge: openclaw resolves product by search (email receipts carry no ASIN) -> needs_verify (human submits); George sends approved seller emails; REVIEWS_EXECUTORS_LIVE + approve + seal + confirm required\n- nav link, review id prefix\n- 74 drafts generated from 37 real Amazon purchases; posted:0; executors OFF by default\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01QA2Se1HgCv8KSZbUQD6w2p"},{"hash":"144d126","date":"2026-09-01 18:08:41 -0700","author":"Steve","subject":"chore: lint, refactor, v0.6.0 (session close) — nav-bar chat feature","body":""},{"hash":"cf095c7","date":"2026-09-01 18:00:40 -0700","author":"Steve","subject":"chat: guard registry+chat fetch against creds-in-URL (resolve vs location.origin) — fixes empty pickers on user:pass@host bookmarks (TK-11084)","body":""},{"hash":"ee61b91","date":"2026-09-01 17:57:19 -0700","author":"Steve","subject":"chat: move Model/Agent pickers to a centered top-middle nav bar; panel drops from it (was a corner bubble) (TK-11084)","body":""},{"hash":"8205414","date":"2026-09-01 17:50:25 -0700","author":"Steve","subject":"chat: drop dead Gemini models from picker (Google prepay $0); adapter kept for easy re-enable (TK-11084)","body":""},{"hash":"a0e1b82","date":"2026-09-01 17:49:20 -0700","author":"Steve","subject":"chat: default lane = GPT-5.2 (funded external) instead of ollama-qwen3 (TK-11084)","body":""},{"hash":"fe14679","date":"2026-09-01 17:43:15 -0700","author":"Steve","subject":"chat: add funded OpenAI GPT-5.2 external lane (Responses API) through the redact+NER boundary (TK-11084)","body":""},{"hash":"9098e9c","date":"2026-09-01 17:22:08 -0700","author":"Steve","subject":"chat: guard nav-bar chat behind authed userId (hide on unauth pages) — DTD verdict A, TK-11084","body":""},{"hash":"4acae37","date":"2026-09-01 17:06:54 -0700","author":"Steve","subject":"chat: local-Ollama NER scrub as 2nd layer on external lanes (fail-closed) — closes free-text PII gap (TK-11084)","body":""},{"hash":"7fca2fd","date":"2026-09-01 15:12:11 -0700","author":"Steve Abrams","subject":"Harden nav-bar chat PII redaction boundary (TK-11084)","body":"1. grounding: extract person-names uniformly for every scope + merge household\n   roster; grounded payees/providers/merchants/reminder titles now redacted\n   on external lanes (was vitals-only).\n2. redact: whitespace/separator/case normalization before name pass; broadened\n   address (apt/city/ST/ZIP, PO Box, short/hyphenated), SSN (spaces+labeled),\n   intl phone, odd-grouped card, unlabeled+extra-labeled IDs. Benign order#/\n   merchant/product still survive.\n3. routes/chat.js: fail CLOSED — no req.userId -> 401; removed DEV_USER_ID.\n4. claude-cli: wrap user/context in <user_input> delimiters + neutralize forged\n   role-label lines so injected turns are inert (Gemini unaffected).\n5. claude-cli: spawn with minimal allowlist env instead of full-inherit-then-\n   delete; DB_/AWS_/OPENAI secrets no longer leak to the child.\n6. chat.js: persistent 'informational only' disclaimer on every assistant bubble\n   + interstitial confirm before first external-model send on the vitals agent.\n\n11 new golden regression tests; npm test 126/126 (1 pre-existing skip).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d683961","date":"2026-09-01 14:56:03 -0700","author":"Steve Abrams","subject":"Add nav-bar chat: model+agent pickers, grounded RAG, PII-redacted external lanes","body":"- lib/chat-registry.js: single source of truth for models (Gemini 2.5 Flash/Pro,\n  Ollama qwen3/hermes3, Claude CLI) + 7 agent personas with data-scopes\n- lib/chat-redact.js: PII redaction boundary (email/phone/SSN/card/NDC/MRN/\n  address/named-people) applied to every external-lane prompt\n- lib/chat-grounding.js: read-only per-agent record grounding via lib/db.js\n- lib/chat-providers/*: gemini (Generative Language API), ollama (local, graceful\n  degrade), claude-cli (spawn with ANTHROPIC keys stripped, no shell injection)\n- routes/chat.js: POST /api/chat + GET /api/chat/registry, one audit_log row per\n  call, read/answer only (no state-changing or external actions)\n- views/partials/chat.ejs + footer wiring: chat inherits on every page\n- public/js/chat.js + app.css: pickers, spec cards, lane badge, session history\n- tests/chat.test.js: redaction golden fixture + registry + route auth-gate\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"b5d614e","date":"2026-09-01 14:52:15 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-01T14:52:01 (2 data files) — views/partials/chat.ejs views/partials/footer.ejs","body":""},{"hash":"b998091","date":"2026-08-25 12:47:42 -0700","author":"Steve","subject":"abramsos: watch loads .env so launchd corpus-refresh has GEORGE_URL (TK-10506)","body":"The launchd plist injects only PG_*; without GEORGE_URL/GEORGE_BASIC_AUTH the\ncorpus refresh skipped as 'GEORGE_URL unset' even after a token reconnect.\nLoading .env (WorkingDirectory=repo root) gives the headless watch the George\ncreds; dotenv never overrides the plist-set PG_* vars.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"f55aae8","date":"2026-08-25 12:44:27 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-25T12:44:18 (4 data files) — data/claim-fill-queue/claim_01kzvyhm9k5hjmkhvyjbdg0xsg.json data/claim-fill-queue/claim_01kzvyhm9m9h86b92zqg40fbjj.json data/claim-fill-queue/claim_01m0x5ww33ct24tkakjexqtc21.json data/claim-fill-queue/claim_01m0x5ww3cghqycd6prtvm03zj.json","body":""},{"hash":"801e623","date":"2026-08-25 12:43:38 -0700","author":"Steve","subject":"abramsos: make Mode claims watch autonomous via George-corpus path (TK-10506)","body":"Removes the watch's hard dependency on a connected Gmail OAuth *connector*\nrow (connector_account has 0 rows), which made it SKIP silently and never\ningest. New autonomous design:\n- lib/mode-corpus.js: best-effort refresh of the local Mode corpus from Gmail\n  via the George HTTP bridge; NON-BREAKING (George down / stale bridge auth /\n  steve-personal invalid_grant all return {ok:false,reason} + surface a gated\n  reconnect signal, never throw).\n- mode-claims-watch.js: refresh -> ALWAYS run the corpus ingester (idempotent)\n  -> stage fill-not-submit briefs for eligible claims. No OAuth required.\n- ingest-mode-claims-corpus.js: glob every data/mode-emails-*.js shard so\n  George-refreshed mailers are picked up without editing the script.\n- Fixes the person.address bug: query selected a non-existent 'address'\n  column (silently caught -> empty profile, so briefs lost name/email/phone\n  too). Now reads metadata_jsonb->>'address'. Never submits; never fabricates.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c1f4023","date":"2026-08-25 12:43:27 -0700","author":"Steve","subject":"abramsos: fix test suite — disable app Basic Auth + single-user autologin in test env","body":"Commit 8aca6e8 (app-level Basic Auth) intercepted the session-auth/CSRF\nsmoke tests with 401 before route logic, and .env's SINGLE_USER_AUTOLOGIN\nauto-logged-in the owner so the 'auth-gated' tests saw 200 instead of 302.\nBoth are production/fleet concerns, not what these tests validate. Overriding\nAPP_BASIC_AUTH=0 + SINGLE_USER_AUTOLOGIN= in the test script restores the\nsuite to green (110/111 pass, 1 pre-existing pdf-fixture skip). Production\nkeeps Basic Auth (NODE_ENV=production) + nginx + autologin unchanged.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d5366d1","date":"2026-08-19 23:17:51 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-19T23:16:54 (1 data files) — backups/medications-backup-20260712-232800.sql","body":""},{"hash":"3db1e3c","date":"2026-08-19 23:14:43 -0700","author":"steve","subject":"abramsos: skip-medical guard in mode-claims ingest so purged medical/pharmacy settlement claims are never re-seeded (TK-10723)","body":""},{"hash":"8aca6e8","date":"2026-08-19 22:50:17 -0700","author":"Steve","subject":"abramsos: add app-level defense-in-depth Basic Auth (401 without creds, 200 with admin:DW2024!)","body":""},{"hash":"fca604e","date":"2026-08-19 04:17:19 -0700","author":"Steve Abrams","subject":"abramsos: CNCP parking-lot fallback fires only when George unavailable","body":"Cody catch: unconditional post 4x/day would flood the parking-lot board.\nAlso replace global fetch() with http.request() for Node 18 compat."},{"hash":"6f0e9fc","date":"2026-08-19 04:06:16 -0700","author":"Steve Abrams","subject":"abramsos: fix claims-autopilot scheduler — separate alert error from fill error","body":"- George 401 (missing GEORGE_BASIC_AUTH) no longer masks fill as 'FAILED'\n- Alert failures log as WARN so fill success is visible\n- Add CNCP parking-lot fallback: urgent claims (deadline ≤7d) posted to\n  http://localhost:3333 automatically, no George cred needed"},{"hash":"00f2109","date":"2026-08-18 16:40:51 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-18T16:40:41 (2 data files) — public/claim-shots/claim_01kzvyhm96rdqm58r3g4w00m5k.png public/claim-shots/claim_01kzvyhm9qb454383b6q0rsfqz.png","body":""},{"hash":"451cbcf","date":"2026-08-18 16:17:38 -0700","author":"Steve","subject":"claims: George-backed backup-doc finder (home-sale/closing paperwork, receipts, breach notices) + /docs endpoint + surfaced in prep panel amber lane","body":""},{"hash":"9676eb8","date":"2026-08-18 16:15:02 -0700","author":"Steve","subject":"claim-fill: combined First/Middle/Last name field -> full name","body":""},{"hash":"c5337bf","date":"2026-08-18 16:13:46 -0700","author":"Steve","subject":"claim-fill: positional last-name + Mode->official->form navigation + save discovered admin_url for re-runs","body":""},{"hash":"b3c0e91","date":"2026-08-18 16:09:52 -0700","author":"Steve","subject":"settlements: live openclaw auto-fill runner (dedicated tab, gateway click-through, proximity+single-use matcher, state-select) + /run endpoint + Go button triggers zero-click fill w/ screenshot; paste-card fallback","body":""},{"hash":"61ad5f4","date":"2026-08-18 16:08:42 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-18T16:08:33 (8 data files) — data/claim-fill-queue/claim_01kzvyd76483jpdh3pd86ak9c0.json data/claim-fill-queue/claim_01kzvyhm9k5hjmkhvyjbdg0xsg.json package-lock.json package.json public/claim-shots/demo-kellerwilliams.png","body":""},{"hash":"b3ececa","date":"2026-08-18 16:08:01 -0700","author":"Steve","subject":"AbramsOS sidebar: group nav into Money&Claims / Health / Home&Life / System sections (labels hidden in mobile row layout)","body":""},{"hash":"889a18c","date":"2026-08-18 16:04:41 -0700","author":"Steve","subject":"AbramsOS nav: move 18 top tabs into a left sidebar panel + add missing Settlements link","body":"- header.ejs: topbar -> sticky left .sidebar (vertical scroll nav, brand top, sign-out+theme bottom); adds /settlements (+ Claims) to the nav; client-side active-link highlight\n- footer.ejs: close the new content-wrap/applayout wrappers\n- app.css: .applayout flex + .sidebar/.sidenav rules + <=860px row-wrap fallback"},{"hash":"ae4ebab","date":"2026-08-18 15:55:02 -0700","author":"Steve","subject":"settlements: remove duplicate claim-and-go (keep the payment+fieldMap version)","body":""},{"hash":"103c476","date":"2026-08-18 15:53:38 -0700","author":"Steve","subject":"settlements: 3 views (grid/list/table sortable) + one-click Claim it & Go (marks eligible + stages openclaw prefill + opens portal + prep card w/ identity+PayPal values); payment default PayPal->steveabramsdesigns","body":""},{"hash":"f450f59","date":"2026-08-18 15:32:54 -0700","author":"Steve","subject":"amazon-orders: 30-min Gmail poller (George bridge) → purchase + reorder_item, word-order-safe dedup; wired into scheduler","body":""},{"hash":"b2fd5b1","date":"2026-08-18 15:02:52 -0700","author":"Steve","subject":"deploy: abramsos vhost with fleet Basic Auth (admin/DW2024!) + SSL + ACME exemption","body":""},{"hash":"6106895","date":"2026-08-18 14:50:36 -0700","author":"Steve","subject":"deploy: serve ACME challenge locally in abramsos vhost (proxy was swallowing it → certbot fail)","body":""},{"hash":"6e958d9","date":"2026-08-18 14:44:58 -0700","author":"Steve","subject":"deploy: nginx vhost for abramsos.agentabrams.com (Kamatera reverse-proxy to tailnet AbramsOS :9774; app self-auths)","body":""},{"hash":"ad64537","date":"2026-08-18 14:38:27 -0700","author":"steve","subject":"Add priority ranking + per-item ratings view to settlement-claims dashboard","body":"Mirror the :9801 approvals scoreGate engine on the AbramsOS settlements page:\nper-claim 0-5 ratings (value/urgency/ease/win-likelihood), composite priority\n(value*2.4 + urgency*2.4 + ease*1.0 + win*0.4), high/med/low tier. Rank open\nclaims priority-first with rank badge, tier chip, rating bars, $/deadline/proof\npills; show recently-expired separately. New lib/settlement-score.js (pure/\ndeterministic); scores computed server-side in the route, passed to the EJS view.\nEligibility controls + fill-not-submit staging button unchanged.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"68dc94c","date":"2026-08-18 14:17:11 -0700","author":"Steve","subject":"claims-alert: fix daysTo — pg returns DATE as Date obj (was NaN, urgency never fired); compare calendar days","body":""},{"hash":"b757b9f","date":"2026-08-18 14:15:09 -0700","author":"Steve","subject":"AbramsOS: settlement-claim autopilot — auto-fill every open claim + deadline alert (never lapse); wired into in-process scheduler; never submits","body":""},{"hash":"5cf00ea","date":"2026-08-12 14:52:05 -0700","author":"Steve Abrams","subject":"chore: lint ✓, refactor ✓ (no code changes), v0.5.0 (session close)","body":""},{"hash":"6fb5271","date":"2026-08-12 14:39:35 -0700","author":"Steve Abrams","subject":"AbramsOS claims: apply migration 0016, fix parser (Estimated/embedded-$/no-claim headings), id kind, George-path corpus ingester; ingest Aug 8-12 Dan mailers (42 claims/22 open); stage 5 no-proof breach fill-not-submit briefs","body":""},{"hash":"7ff4ec3","date":"2026-08-07 19:05:01 -0700","author":"Steve","subject":"AbramsOS: class-action settlement claim-filler (Mode feed → openclaw fill-not-submit)","body":"- lib/mode-claims.js: deterministic parser for the Mode Class Actions newsletter\n  (handles Mode's swapped-URL quirk via slug↔name token matching)\n- 0016_settlement_claims.sql: settlement_claim + settlement_fill_job tables\n- ingest-mode-claims.js: native Gmail-connector ingester (idempotent, $0)\n- openclaw-claim-filler.js: builds a portal-driver brief that PREFILLS and STOPS\n  at the perjury attestation/submit; only for eligibility_state='eligible'\n- mode-claims-watch.js + launchd (15m): 'as soon as email lands' trigger\n- /settlements dashboard: eligibility toggle + gated 'Prefill via openclaw'\n- 6-month Dan backfill artifact: 19 distinct settlements, 12 open\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"e62bbd3","date":"2026-08-07 19:03:35 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-07T19:03:19 (2 data files) — data/mode-claims-backfill.json db/migrations/0016_settlement_claims.sql","body":""},{"hash":"4f346d9","date":"2026-08-03 07:27:28 -0700","author":"Steve Abrams","subject":"chore: v0.4.0 (session close — claims tracker + auth hardening)","body":""},{"hash":"254e60d","date":"2026-07-31 16:21:27 -0700","author":"Steve Abrams","subject":"Rate-limit auth endpoints + fix audit-log IP integrity","body":"- middleware/rate-limit.js: hand-rolled in-memory limiter (no dep), 12/15min/IP,\n  429+Retry-After. Applied to POST /signin, /signup, /step-up. Verified: 12 ok then 429.\n- clientMeta now logs req.ip (trust-proxy-resolved real client) not the raw leftmost\n  X-Forwarded-For (attacker-controllable) — audit-log IP integrity.\n/yoloforever cycle 3. Cody's 'XFF-spoof bypasses the limiter' CRITICAL was DISPROVEN\nempirically (spoofed XFF still 429 — nginx appends real peer right; trust proxy:1 picks\nit); only the audit-log (leftmost-XFF) read was the real issue. HSTS already sent by helmet.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"8c7f239","date":"2026-07-31 15:49:00 -0700","author":"Steve Abrams","subject":"Stop unauth info-disclosure on public surface (/healthz + error handler)","body":"- /healthz returns {ok} only (drop pid + err.message); full error logged server-side.\n- Global error handler renders a generic client message; internal err.message no\n  longer leaks (Postgres schema etc.) to the internet. Full detail still logged.\nKeepalive still gets 200. /yoloforever cycle 2; verified healthz+auth intact.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d08f146","date":"2026-07-31 15:20:29 -0700","author":"Steve Abrams","subject":"Harden auth cookies for public HTTPS surface + record claims subdomain deploy","body":"- trust proxy=1 (behind Kamatera nginx) so req.secure reflects X-Forwarded-Proto.\n- Session + CSRF cookies now secure:req.secure — Secure over HTTPS subdomain,\n  plain over direct tailnet http (no lockout). Closes cookie-in-transit leak on\n  the now-public claims.abramsos.agentabrams.com PII surface.\n- Records the deployed nginx vhost (45.61.58.125:80/443 -> 100.82.17.107:9774).\nFound by /yoloforever cycle-1 contrarian gate; verified Secure-on-https / plain-on-http.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"69b613c","date":"2026-07-31 14:58:47 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-31T14:58:42 (1 files) — deploy/claims.abramsos.agentabrams.com.nginx.conf","body":""},{"hash":"2f71185","date":"2026-07-31 14:47:49 -0700","author":"Steve Abrams","subject":"Add California unclaimed-property tracker to claims dashboard","body":"- New unclaimed_property table (migration 0015) + seed of 5 CA properties\n  found for Steve on claimit.ca.gov ($131.55, status=staged). Public data\n  only, no SSN.\n- /claims now renders a Tracked Properties table (amount/holder/address/\n  property-id drill hrefs, found date+time chip, per-row status select).\n- API: GET /api/unclaimed, POST /api/unclaimed/:id/status (session-scoped\n  write, audited, CSRF-exempt like other /api routes).\n- server.js: claims.abramsos.agentabrams.com/ -> /claims host redirect.\n- DNS + live deploy left gated (memo in pending-approval).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"fc3c624","date":"2026-07-13 12:25:39 -0700","author":"Steve","subject":"chore: lint (node --check 21/21) + version bump v0.3.0 (session close)","body":"Overnight life-optimizer build: health (vitals/insights/BP-meds/reminders), savings\n(advisor/coupons/bill-audit/reorder-timing), Assets+RentCast, Neighborhood Watch,\nBiometrics, Plaid+CSV banking, Withings device sync, 38 real Amazon purchases ($8,966.66)."},{"hash":"5671885","date":"2026-07-13 09:56:02 -0700","author":"Steve","subject":"withings: device-vitals auto-sync connector (OAuth2 → weight/BP/HR/SpO2 into Health)","body":"- lib/withings-client.js: Withings Health API OAuth (authUrl/exchange/refresh) + getMeasures mapping (type 1=weight kg→lb, 9/10=BP pair, 11=HR, 54=SpO2); handles refresh-token rotation; graceful no-creds\n- routes/withings.js: /auth/withings → consent → /api/withings/callback stores encrypted token in connector_account (provider=withings) + initial sync; /api/withings/sync re-syncs (dedup via health_reading external_id); reuses vitals.insertReadings; audited medical\n- connectors.ejs: 'Health devices' section — Connect Withings / Sync now / setup-needed\n- server.js wiring + .env WITHINGS_* (gated). One-set-of-creds-from-working"},{"hash":"ba2e47d","date":"2026-07-13 09:49:42 -0700","author":"Steve","subject":"assets: RentCast home-value estimate wiring (one-key-from-working) + all 37 Amazon totals collected","body":"- lib/home-value.js: RentCast AVM lookup by address; graceful no-key/no-address (never fabricates a value); Zillow API retired so RentCast is the free option\n- routes/assets.js: POST /api/assets/:id/estimate → fills current_value + value_source='estimate' + range; valuationConfigured flag\n- views/assets.ejs: 'Estimate' button on property cards (prompts for key if absent)\n- .env: RENTCAST_API_KEY= (gated)\n- DATA: opened remaining 17 older Amazon orders via George → all 37 now have real totals; complete all-time spend $8,966.66 (2022-2026)"},{"hash":"957c215","date":"2026-07-13 01:57:07 -0700","author":"Steve","subject":"docs: savings track done; overnight run complete, loop stopped","body":""},{"hash":"5387c7d","date":"2026-07-13 01:56:43 -0700","author":"Steve","subject":"savings: subscription/bill audit + reorder-timing nudges","body":"- lib/bill-audit.js: category-aware — NEVER flags essentials (tax/rent/utility/loan); insurance/phone/internet get 'shop your rate'; discretionary/subscriptions ALWAYS surface with annual cost ('still using this?'), LLM (gemma3:12b) refines cancel/downgrade/overpriced; rule-based duplicate detector\n- lib/reorder-timing.js: deterministic 'reorder soon' nudges from cadence + last_ordered (Paper towels due-now verified)\n- wired both into /api/savings/run + nightly runner; kind='bill-audit'/'reorder-timing' surface on /savings\n- honest: est_savings only for cancel/downgrade, never for 'review'; verified on synthetic sub then wiped"},{"hash":"a0f0e6a","date":"2026-07-13 01:52:07 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-13T01:52:00 (1 files) — lib/bill-audit.js","body":""},{"hash":"4c949de","date":"2026-07-13 01:23:32 -0700","author":"Steve","subject":"docs: roadmap — health track done; Assets/Neighborhood/Biometrics shipped; savings track next","body":""},{"hash":"5ab1c97","date":"2026-07-13 01:22:58 -0700","author":"Steve","subject":"biometrics: per-person ID/emergency profile for kids + adults (child-ID record + growth BMI)","body":"- 0014_biometrics.sql: person_biometric (1:1 per person) — DOB, sex, height, weight, blood type, eye/hair, marks, allergies, conditions, emergency contact, prints-on-file (child-ID)\n- lib/biometrics.js: exact age + BMI; adult CDC BMI category; kids link to CDC percentile calculator (NO fabricated percentile)\n- routes/biometrics.js upsert-per-person + views/biometrics.ejs profile cards w/ inline edit, child badges\n- nav + wiring. Sensitive PII, audited; works for whole household"},{"hash":"a4f7803","date":"2026-07-13 01:21:56 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-13T01:21:52 (4 files) — lib/ids.js db/migrations/0014_biometrics.sql lib/biometrics.js routes/biometrics.js","body":""},{"hash":"42d431f","date":"2026-07-13 01:18:32 -0700","author":"Steve","subject":"neighborhood: Neighborhood Watch — address+radius, Leaflet map, Ring cameras + Neighbors, Nextdoor/Citizen/crime maps/registries","body":"- lib/neighborhood.js: free OSM Nominatim geocoder (no key) + categorized real deep-link builder (Your Ring first: cameras live-view + Neighbors feed), geo-aware SpotCrime/OSM links\n- routes/neighborhood.js: /neighborhood resolves address from ?asset/?address/first property; /api/neighborhood/geocode\n- views/neighborhood.ejs: address+radius controls, Leaflet map w/ radius circle, property picker, link cards\n- nav + wiring; links to Assets property addresses"},{"hash":"a78d4ae","date":"2026-07-13 01:15:52 -0700","author":"Steve","subject":"assets: net-worth tracker — enter home by address + current value, finance category, details/notes","body":"- 0013_assets.sql: asset table (property/vehicle/account/valuable) w/ address, current_value, category, details, notes, geocode\n- routes/assets.js + views/assets.ejs: /assets dashboard (net worth total + by-category stat row, add form, created date+time chips, per-property 'Watch' link to neighborhood)\n- nav + wiring. Values user-entered (auto-valuation API is a later gated add). Feeds upcoming neighborhood-watch via property address"},{"hash":"8598287","date":"2026-07-13 01:13:32 -0700","author":"Steve","subject":"health: lapsed-habit BP reading reminders (only nudges prior loggers, never new users)","body":"- lib/vitals-reminders.js: fires 'time for a BP check' only if a person logged BP before but not in 14d; deduped via reminder engine\n- reminder-engine.generateForUser hooks it in alongside returns/warranty/recall reminders\n- verified: lapsed(20d)=fire, recent(2d)=skip, never-logged=skip"},{"hash":"355389e","date":"2026-07-13 01:10:57 -0700","author":"Steve","subject":"health: BP-medication context — show active meds that raise/lower blood pressure alongside the trend","body":"- lib/bp-meds.js: deterministic classifier (curated generic stems + names -> effect/class; never LLM-guessed) — ACE/ARB/beta-blocker/CCB/diuretic (lowers) + NSAID/decongestant/steroid/stimulant (raises)\n- routes/vitals.js: read-only join to medication table, bpRelevant() into /health\n- views/vitals.ejs: 'Medications that affect blood pressure' panel (color-coded lowers/raises), informational-not-advice\n- works when med list is populated (currently empty)"},{"hash":"8f58fa2","date":"2026-07-13 01:04:48 -0700","author":"Steve","subject":"health: vitals trends + insights (weight/RHR sparklines, local-LLM 'what changed this month', gentle Stage-2 recheck flag)","body":"- lib/vitals-insights.js: computeTrends (this-30d vs prior-30d avgs), recheckFlag (deterministic rule, gentle), summarize (gemma3:12b narrates ONLY computed deltas — never invents readings; no medical advice)\n- routes/vitals.js: fast trends+recheck on /health load; /api/health/insights for async LLM narrative\n- views/vitals.ejs: 'This month' panel (trend chips + delta arrows + recheck banner + async narrative) + weight/RHR sparklines\n- verified end-to-end on synthetic data then wiped; empty-state safe (no fabricated readings)"},{"hash":"4a02cce","date":"2026-07-13 00:56:16 -0700","author":"Steve","subject":"purchases: collect 38 real Amazon orders from info@ (via George) into purchase table","body":"- scripts/collect-amazon-orders.js: parsed order confirmations 2022-2026, dedup on order#, total NULL (not fabricated — needs body enrichment)\n- validates savings seeds: Nespresso Vertuo = top recurring buy (4x), shipping labels (2x)\n- feeds home purchase count + /purchases + savings advisor with REAL data"},{"hash":"ded692c","date":"2026-07-13 00:49:05 -0700","author":"Steve","subject":"connectors: no-signup bank import — Wells Fargo CSV upload (headerless + negative-debit aware)","body":"- csv-parser: headerless fallback (WF/Quicken export: date,amount,*,,description) + signMode — WF debits are negative=spend, so normalize to positive + drop deposits; header CSVs unchanged\n- upload.js: dedup key (csv:date:amount:merchant) + ON CONFLICT so re-uploading overlapping statements doesn't double\n- connectors.ejs: 'Import CSV' upload form with WF download instructions — zero third-party signup path\n- answers Steve: Plaid needs a free web signup (no CLI); CSV upload needs nothing"},{"hash":"a71abd4","date":"2026-07-13 00:38:34 -0700","author":"Steve","subject":"docs: roadmap — banking (Plaid) track shipped; real-bank keys gated to Steve","body":""},{"hash":"954cc77","date":"2026-07-13 00:38:05 -0700","author":"Steve","subject":"connectors: Plaid 'Connect a bank' UI (Wells Fargo etc.) + incremental/idempotent sync","body":"- views/connectors.ejs: Plaid Link flow — connect button, secure login (Plaid handles bank creds), exchange, per-bank sync; graceful 'setup needed' state when keys absent\n- routes/plaid.js: GET /api/plaid/status; sync now resumes from persisted cursor + ON CONFLICT dedup (was re-pulling everything each sync -> dup purchases)\n- lib/plaid-client.js: isConfigured() helper\n- 0012_plaid_sync_cursor.sql: connector_account.sync_cursor + unique(user_id,order_number) on purchase\n- Plaid (not Stripe) is correct for reading a bank; sandbox works now, real Wells Fargo needs Steve's prod keys"},{"hash":"f4e4da6","date":"2026-07-13 00:31:36 -0700","author":"Steve","subject":"docs: roadmap adds health track (vitals trends, BP↔meds, reminders) as co-priority","body":""},{"hash":"2d8848c","date":"2026-07-13 00:30:52 -0700","author":"Steve","subject":"health: Vitals module — BP history + Apple Watch/device readings + sync","body":"- 0011_health_readings.sql: health_reading (BP pairs, HR, weight, SpO2, glucose...) w/ dedup index\n- lib/health-metrics.js: AHA BP categorization + Apple Health export.xml streaming parser (pairs systolic/diastolic by timestamp; scales SpO2)\n- routes/vitals.js + views/vitals.ejs: /health dashboard w/ inline SVG BP chart, AHA color categories, per-person filter, manual add, delete; /api/health/readings JSON ingest for the Health Auto Export app\n- scripts/import-apple-health.js: one-command import of an Apple Health export\n- nav + wiring. NO fabricated readings — table ships empty w/ 3 real sync paths. Verified end-to-end on synthetic data then wiped."},{"hash":"0256450","date":"2026-07-13 00:23:59 -0700","author":"Steve","subject":"savings: nightly advisor runner + launchd plist (draft, Steve bootstraps)","body":"- scripts/run-savings-advisor.js: dotenv-loaded, dedup-safe regen (local, $0)\n- deploy/com.steve.abramsos-savings.plist: daily 6:05am, RunAtLoad off; bootstrap surfaced in header comment"},{"hash":"3e9efe5","date":"2026-07-13 00:22:21 -0700","author":"Steve","subject":"docs: life-optimizer roadmap (overnight)","body":""},{"hash":"a121be0","date":"2026-07-13 00:21:17 -0700","author":"Steve","subject":"savings: home dashboard tile + honest merchant-savings leads","body":"- home: 'Savings ideas' stat tile (open count + est $ total) linking to /savings\n- seeded 3 real verifiable savings mechanisms (Amazon Subscribe & Save, camelcamelcamel price alerts, Costco coupon book) — real URLs, no fabricated codes"},{"hash":"db2fd48","date":"2026-07-13 00:19:09 -0700","author":"Steve","subject":"savings: life-optimizer module — cheaper/better substitutes from what you buy (local gemma3:12b, $0)","body":"- 0010_savings.sql: savings_suggestion + merchant_coupon tables\n- lib/savings-advisor.js: grounded strategy suggestions (no hallucinated SKUs/prices)\n- routes/savings.js + views/savings.ejs: dashboard w/ created date+time chips, sort+density, save/dismiss\n- seeded reorder items from real Amazon email history; 4 suggestions generated\n- switched local model qwen3:14b->gemma3:12b (qwen3 thinking-mode returns empty under format:json)"},{"hash":"367d888","date":"2026-07-12 23:51:27 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-12T23:51:20 (1 files) — backups/","body":""},{"hash":"ef35bef","date":"2026-07-11 10:05:02 -0700","author":"Steve Abrams","subject":"claims: add California Unclaimed Property (claimit.ca.gov) resource card with per-household-name copy-to-search","body":""},{"hash":"1192d3c","date":"2026-07-09 09:08:42 -0700","author":"Steve","subject":"chore: lint (node --check ✓), untrack runtime state, v0.2.0 (session close)","body":""},{"hash":"4ccbda5","date":"2026-07-08 15:58:17 -0700","author":"Steve","subject":"recall-watch: apply contrarian FIX-FIRST — E-known+in-date=>POSSIBLE regardless of FDA publish date (fix false-clear); assess ALL recalls per NDC (worst class never hidden); EXP-anchored expiry parser (no MFG/ref-date mis-parse); local notification on new alerts; per-NDC worst-status UI + N-of-M affected fills","body":""},{"hash":"362c884","date":"2026-07-08 15:47:47 -0700","author":"Steve","subject":"recall-watch.js: nightly-ready FDA recall watcher with PROGRAMMATIC lot/date relevance (per-fill), new-alert detection + state baseline; caught fluticasone D-0326-2024 overlapping a 2026 fill","body":""},{"hash":"a1038a6","date":"2026-07-08 15:40:00 -0700","author":"Steve","subject":"Recall lot/date check: mark all 4 NDC recalls CLEARED (recalled lots expired before your fills); green Clear badges + plain-English assessment on Recalls + Rx tabs","body":""},{"hash":"2caebba","date":"2026-07-08 15:35:54 -0700","author":"Steve","subject":"Recalls: add curated article/FDA links per NDC-recalled product (Read-about-it column)","body":""},{"hash":"b02722e","date":"2026-07-08 15:30:08 -0700","author":"Steve","subject":"NDC-precise FDA recall match: flag fills whose actual dispensed product is recalled","body":"- scripts/ndc-recall-check.js: match each fill's 11-digit package NDC to openFDA product NDC\n  (leading-zero-normalized labeler|product, exact-confirmed); 4/46 NDCs on a recall list, 40 fills\n- recalls tab: prominent 'Your actual products on an FDA recall list' NDC section (headline signal)\n- Rx tab: red RECALL badge on flagged fills (CREON, Fluticasone, Nystatin, Atorvastatin)"},{"hash":"78b80e6","date":"2026-07-08 15:22:33 -0700","author":"Steve","subject":"FDA recall cross-reference: clean drug names, derive generics, openFDA match into Recalls tab","body":"- scripts/check-fda-recalls.js: cleans OCR junk from fill drug names (95 fixed), rebuilds\n  deduped medication list (83->36 real meds) with generic names, cross-references each vs\n  openFDA drug-enforcement (free); 33/36 matched, 300 recall records into medication_recall\n- routes/recalls.js + views/recalls.ejs: new 'Medication recalls (FDA)' section, grouped per\n  med, Class I/II/III severity badges, worst-first; labeled advisory (ingredient-level match)"},{"hash":"542a632","date":"2026-07-08 14:38:12 -0700","author":"Steve","subject":"Import Shangoo Pharmacy tax profile: prescription_fill table + 277 fills + 83 meds + /prescriptions viewer","body":"- 0009_prescription_fills.sql: fill-history table (date/drug/rx/qty/days/doctor/plan/plan-paid, raw OCR audit)\n- scripts/load-abrams-rx.js: OCR-parsed loader, reconciled to doc totals (Natalia 220 fills / $136,254.86 of $136,328.74)\n- creates person Steve (self) + Natalia (spouse); dedupes 83 medications into medication table\n- routes/prescriptions.js + views/prescriptions.ejs: read-only viewer w/ per-person totals + filter; nav 'Rx'"},{"hash":"6589647","date":"2026-07-08 14:36:18 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-08T14:36:14 (5 files) — lib/ids.js db/migrations/0009_prescription_fills.sql routes/prescriptions.js scripts/load-abrams-rx.js views/prescriptions.ejs","body":""},{"hash":"63d53f9","date":"2026-07-07 08:04:56 -0700","author":"Steve","subject":"feat(health): Household (people/spouse) + Medications with FDA recall check","body":"- migration 0008: person, medication, medication_recall\n- /household — add spouse/dependents (records + meds attach to them)\n- /medications — self-entered meds per person; every write audit-logged with a medical-consent marker (AGENTS.md gate)\n- lib/fda-fetcher.js — openFDA drug-enforcement lookup; per-med + \"check all\" recall scan stores matches, surfaced on the card\n- nav links, recall styling\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"ab337c2","date":"2026-07-07 08:01:02 -0700","author":"Steve","subject":"fix(tests): isolate suite to abrams_os_test DB","body":"pretest builds/migrates/seeds a throwaway abrams_os_test (schema + all migrations\n+ seed.sql + rights-rules); test script pins PG_DATABASE=abrams_os_test. Stops the\nauth-e2e test from wiping the real owner account on every `npm test` (AGENTS.md rule).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"3a31291","date":"2026-07-07 07:53:10 -0700","author":"Steve","subject":"feat(digest): suppress zero-deadline days","body":"sendDigest short-circuits when there are no upcoming deadlines, so empty days\ndon't put a \"nothing due\" email in the inbox.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"8cf03df","date":"2026-07-07 07:45:29 -0700","author":"Steve","subject":"feat(digest): opt-in daily deadline digest email via George","body":"- lib/digest.js builds an HTML digest (overdue / this week / coming up) and self-sends to Steve's own inbox via George /api/send (internal recipient, no external-send token)\n- scheduler cron 08:00 America/Los_Angeles is DOUBLE-GATED: off unless DIGEST_ENABLED=1 AND GEORGE_BASIC_AUTH set; committed code is dormant and never auto-sends on its own\n- .env.example documents all digest keys (default DIGEST_ENABLED=0)\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"61b2dca","date":"2026-07-07 07:45:18 -0700","author":"Steve","subject":"feat(warranties): manual Warranties page + Deadlines coverage","body":"- /warranties + /api/warranties CRUD (audit-logged) writing to service_commitment (source='manual')\n- reminder-engine scans service_commitment.refund_window_ends_at -> 'coverage_window_closing' deadlines\n- nav link, warranty id prefix\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"43aea4b","date":"2026-07-07 07:32:16 -0700","author":"Steve","subject":"feat(ui): Bills, Reorders, and unified Deadlines pages","body":"- /bills, /reorders CRUD (audit-logged) + /deadlines over the reminder engine\n- add-forms, mark-paid/ordered (rolls due date forward + refreshes deadline), created date+time chip on every admin card\n- generalized sort-density.js to any [data-grid]; nav links; liquid-glass styles\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"5693d23","date":"2026-07-07 07:32:16 -0700","author":"Steve","subject":"feat(schema): bill + reorder_item tables, recurrence lib, reminder-engine wiring","body":"- 0007 migration: bill (Bills to Pay incl. tax/government/CRA via category) + reorder_item (frequently-ordered + best-price savings)\n- lib/recurrence.js: pure cadence date math (+ 5 passing unit tests)\n- reminder-engine emits calendar_reminder rows for upcoming bill due-dates and reorder points (unified Deadlines)\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"7db85d7","date":"2026-07-07 07:31:44 -0700","author":"Steve","subject":"chore: move dev port 9931 -> 9774 (9931 held by japan-enrich/viewer-local) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>","body":""},{"hash":"2e0044d","date":"2026-05-31 16:32:22 -0700","author":"Steve","subject":"Add per-site favicon (kills /favicon.ico 404)","body":""},{"hash":"551b5ea","date":"2026-05-19 17:27:14 -0700","author":"Steve","subject":"ignore + 404-guard backup/snapshot files","body":".gitignore now excludes *.bak, *.bak.*, *.pre-*, *.orig, *~ so editor\nswap files and pre-edit snapshots can't be accidentally committed and\nend up served from /public. Backstop in server.js: any GET whose path\nmatches those patterns short-circuits to 404 before express.static,\nso even a stray on-disk *.bak.html can never leak.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"20f8f36","date":"2026-05-19 17:26:42 -0700","author":"Steve","subject":"add noreferrer to target=_blank external links","body":"CPSC notice + cited-rule source links now use rel=\"noopener noreferrer\"\ninstead of bare rel=\"noopener\" so we don't leak Referer to upstream pages.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"3458fe1","date":"2026-05-10 12:05:26 -0700","author":"Steve","subject":"tick 18: receipt extractor regression fixtures + heuristic fixes (item 19)","body":"- 7 new golden-file fixtures in tests/fixtures/receipts/ (Amazon, Best Buy,\n  DoorDash, Apple, Etsy, Uber, Walmart, plus a non-receipt newsletter)\n- tests/extractor-regression.test.js auto-discovers fixtures and asserts\n  merchant + total + orderNumber + minConfidence per file\n- lib/receipt-extractor.js: 2 surgical heuristic fixes uncovered by fixtures\n  · extractTotal now picks the highest-rank label (\"order total\" >\n    \"grand total\" > \"total charged\" > plain \"total\"); allows up to 40 chars\n    between label and $amount (handles \"Total charged to Visa ••1234: $18.42\");\n    rejects \"subtotal\" via 3-char lookbehind\n  · extractOrderNumber adds Best-Buy-style \"Order #: BBY01-806589123456\"\n    (alpha prefix + digits) and a tighter Amazon-only pattern\n- 15/15 tests across the regression + base extractor suites"},{"hash":"fe7ffca","date":"2026-05-10 11:06:07 -0700","author":"Steve","subject":"tick 17: manual CSV upload route (backlog item 15)","body":"- npm install multer\n- lib/csv-parser.js: parses statement-style CSVs\n  · auto-detects date / amount / merchant columns (case-insensitive)\n  · handles quoted fields with embedded commas, dollar signs, parens-as-negative\n  · skips refunds (negative amounts) and bad rows; reports first 5 errors\n- routes/upload.js: POST /api/upload/csv (multer in-memory, 10 MB cap)\n  → parser → INSERT purchase rows + audit_log entries\n- 10/10 csv-parser tests"},{"hash":"1666967","date":"2026-05-10 10:26:42 -0700","author":"Steve","subject":"tick 16: Compliance Guardian (URL allowlist + injection scan + PII redact)","body":"- lib/compliance-guardian.js: 4 layered defenses\n  · isAllowedDestination(url) — outbound HTTP allowlist (gov, NIH/FDA,\n    Plaid, Google APIs, local LLM endpoints; subdomain-confusion safe)\n  · detectPromptInjection(txt) — 16 patterns (ignore-prior-instructions,\n    chatml fragments, role injection, exfiltration phrasing, code-eval)\n  · redactPii(text) — masks card #, SSN, phone, email local-part, ZIP,\n    Stripe/OpenAI-style API keys; keeps domain hints\n  · guardForLlm(text) — one-shot redact+scan combiner\n- lib/receipt-extractor.js: enrichWithLlm now runs guardForLlm() on body+from+\n  subject before sending to Ollama; refuses enrichment if injection detected\n  (returns heuristic with llmError='compliance_guardian_blocked')\n- tests/compliance-guardian.test.js: 19/19 across allowlist (6), injection (4),\n  redactor (6), one-shot guard (3)"},{"hash":"a5da75e","date":"2026-05-10 10:01:01 -0700","author":"Steve","subject":"tick 15: auto-parse Drive PDFs on sync (backlog item 17)","body":"- routes/connectors.js syncDrive(): after a Drive PDF lands in document table,\n  immediately calls pdf.parseFile() + receipt-extractor (heuristic + LLM).\n  Inserts a purchase row when extractor returns a draft.\n- Images skipped (OCR is a later tick); failed parses flip parsed_status='failed'\n- audit_log gets document_parsed + purchase_extracted (source: 'drive_pdf')\n- 34/35 tests still green"},{"hash":"8adb083","date":"2026-05-10 09:51:01 -0700","author":"Steve","subject":"tick 14: unit tests for lib/crypto.js + lib/ids.js (backlog item 16)","body":"- tests/crypto.test.js (7): AES-256-GCM round-trip, IV uniqueness, tamper\n  detection on ciphertext + auth tag, unicode, empty-string, env-var validation\n- tests/ids.test.js (6): prefix per kind, unknown-kind throws, distinctness,\n  ULID monotonic-over-time, lowercase invariant\n- 13/13 green; pure unit tests, no PG, no server"}]}