Portal Deep Dive · read-only analysis

Your home base, tile by tile — what's live, what's stale, what to rethink

Every tile on home.html and every surface it links to, with its data source verified against the live ops-api Worker this morning. Paired with the twin-engine mockup and the rebuild handoff.
Generated Thu, Jul 2 2026 · 12:19 PM EDT (NY) · read-only, no live pages touched
Repo hookstreet-workspace @ 4c45402 · source: outputs/home.html (1,206 lines) + 8 linked surfaces
Endpoints probed live: ops-api.sam-0f0.workers.dev /health /plaid/balances /mis/peek /hospitable /calendar
Requirements: docs/PORTAL_RETHINK_SPEC.md (Sam's 7/2 walkthrough)

How to read this

KEEP — works, leave it FIX — a bug or stale value, small change RETHINK — the presentation itself is the problem
LIVE real feed STATIC baked into HTML MIXED live + hardcoded BROKEN feed dead / wrong
The one-line finding: the portal's plumbing is real — Plaid cash, the queue, calendar, Hospitable and MIS quotes all answered live this morning. The problem is presentation: the home page opens as a wall of 20+ launchers and 6 data tiles, so the ONE thing that matters today is buried, and a few tiles show hardcoded or mis-computed numbers that read as fact. Fix the honesty bugs, then re-lead the page with today's single move.

A · The stat strip + top banners

Stat strip — 4 live numbersKeep

cash now · p0/p1 open · forced today · next 7 days  ·  LIVE
Does
Four tap-to-jump numbers under the hero. Each scrolls to its parent tile.
Fetches
No own fetch — reuses the tile loaders (one source of truth). Good pattern.
Verdict
Honest and cheap. Keep it. Carry this "one loader, many displays" pattern into the rebuild.

Today banner + "What's New" stripFix

two stacked full-width links above the fold  ·  STATIC
Does
"📋 Today →" links to today.html; a gold "NEW" strip links to everything.html.
Issue
The NEW strip text is hardcoded ("Today page, Week view, MIS cockpit… just shipped") and dated from the Jun-25 build — it will read "just shipped" forever. This is the everything.html hand-maintained problem leaking onto home.
Verdict
Fold both into the proposed single "today's goal" strip; kill the frozen NEW copy or drive it off the auto-index.

"Your next move" cardFix

client-side ranker picks #1 + #2 from the queue  ·  LIVE
Does
Scores every card by priority + $-at-stake + waiting-on-others + age, shows the top one with Done / Defer / Skip and a slim "Then:" second move.
Good
This is already the seed of the "lead with the ONE thing" rethink — the logic exists and is honest about its ranking.
Issue
It sits fourth on the page (under stat strip, Today banner, NEW strip). The best asset is buried. It answers "what to do" but not "what's the goal / rest of my day" — Sam's A-to-Z ask.
Verdict
Promote to the top of the page and widen its job: one move + today's goal + the timed rest-of-day. See mockup right column.

B · The six data tiles

Cash · on handFix

/plaid/balances (KV-cached, cron-warmed)  ·  MIXED (live Plaid + hand-entered block)
Fetches
LIVE — probed 12:16 PM: 9 institutions, 18 accounts, total $1,992.09, cache_hit, zero failed. Real.
Live loans now in Plaid
Fifth Third mortgage ···1858 ($380,468) and USALLIANCE HELOC ···2650 ($217,000) are in the feed — render under "Loans · live".
"Manual obligations"
Hand-entered block = Shellpoint 9332 /mo $3,161.51 · Selene 9312 /mo $3,379.91 · Affirm total $4,105.02 (tagged "as of Apr 28 — verify"). These are the loans Fable found that Plaid can't see. Naming is wrong: it mixes monthly mortgage payments with a total Affirm balance under one "obligations" header, and Affirm is 2+ months stale.
Chase gap
Confirmed — no Chase account anywhere in the 9 Plaid institutions. Chase 5609 (the inflow hub) and 5007 are invisible to "cash on hand." Sam's instinct to add Chase back is correct; it's a genuine completeness hole, not a display bug.
Verdict
Rename the block "Mortgages & loans (not in Plaid)"; split monthly-payment rows from balance rows; date-stamp Affirm and flag it stale; add Chase (re-link via link.html or hand-enter with an "as of" date). The tile's data-honesty framing (⚠ on incomplete totals) is excellent — keep it.

Open loops · liveRethink label

/inbox QUEUE_JSON via ops-api  ·  LIVE
The 228 vs 92 confusion
Both numbers are real and measure different things. The tile's big number (~228) = everything open that isn't forced-today or overdue (the backlog). The stat-strip 92 = P0 + P1 only (the cash-now/high-priority subset). Neither is wrong; they're just unlabeled, so they look contradictory.
How it splits
Cards are bucketed: forced today (P0 or due exactly today) → the Today tile; overdue → its own red bucket pointing at Rethink; the rest → this tile's count. Honest logic, but the raw scale (228 open) signals a backlog that needs a triage pass, not a display fix.
Verdict
Label every number with its scope on the tile ("228 open backlog · 92 are P0/P1 · N forced today"). The deeper fix is the 263→sane data pass the code itself flags — dedupe/triage the queue so the backlog number stops scaring.

Markets · MISRethink

/mis/peek?sym=NVDA  ·  BROKEN (hardcoded ticker + wrong % + blank verdict)
Hardcoded NVDA
Confirmed — the fetch is literally /mis/peek?sym=NVDA and the tile hardcodes the string "NVDA". It is not Sam's real top holding; it's a pinned bellwether. Sam's ask: show the real top holding (or the market), not a fixed name.
The % is a real bug
peek returns day_pct:-1.76 and day_change:-3.48 (dollars). The tile reads q.day_change_pct||q.day_change — but day_change_pct doesn't exist, so it falls through to day_change (−3.48) and prints it as "↓3.48%". The true move was −1.76%. It's showing the dollar change as a percent.
Verdict blank
The verdict/SACS/basis rows depend on d.universe, which the live peek isn't returning (and /health shows schwab_set:false — the live-holdings link is off), so "verdict mark" renders "—". Sam asked what the verdict mark even is: it's the MIS ADD/STARTER/WATCH/TRIM call for that ticker — currently unfed here.
Missing
No VIX, no gold, no market index. Sam wants the market + VIX + gold always visible. (The mis-cockpit page does show VIX — home doesn't.)
Verdict
Replace with a real "truth tile": market/regime + VIX + gold + your top holding by weight (from /mis/brief, which the cockpit already uses), correct the pct field name, and define "verdict" inline. This is a rethink, not a patch.

Groceries · open listKeep

/inbox GLIST + GROCERY + BOUGHT  ·  LIVE
Does
Live list, aisle auto-categorization, mark-bought-by-name (not position), on-device "regulars" learning with Thu/Fri Shabbos staples.
Verdict
Mature and well-built. Per the per-surface-personality memory, its home-portal look is deliberately distinct from the kids' kiosk — keep both. No change.

Calendar · today + weekFix

HS.calendar('business') + ('family') merge + queue overlay  ·  MIXED
Fetches
LIVE — merges business (Airbnb 9312/9332 + TripIt) and family scopes, then overlays dated queue cards onto their due-day. Probed live: business scope returns real 9312/9332 reservation spans. The Monsey event Sam saw is the wiring working.
Static remnant
A KNOWN_PERSONAL[] array hardcodes late-June personal events (yahrzeit, farbrengen) because the iCal feed didn't carry them — all now past, harmless but dead weight.
The consolidation question
Sam's confusion is real: "Today" pill (calendar count) vs "next 7 days" (stat strip) vs the Today tile (forced tasks) vs the P0-due-today footer are four different day/week presentations. They don't conflict but they aren't unified.
Verdict
Collapse the day/week story into ONE place in the rebuild (see today.html below). Drop the stale KNOWN array once the feed carries personal events.

Today · forced list (the big tile)Keep logic

same QUEUE_JSON, filtered to P0 + due-today + captured-today  ·  LIVE
Does
The "FORCED" list — P0s and anything due exactly today, plus a "captured today" band so nothing you logged today is invisible. People-tagged calendar rows ride on top.
Verdict
Logic is sound and honest. In the rebuild this becomes the spine of the top "today" strip rather than one tile among six.

C · The launcher wall

Do / Money / Life / Build lanesRethink

~20 tiles across 4 lanes + a Command-center block  ·  STATIC links
Does
The grouped launcher: one "Command queue" card (5 queue views as pills) + Money/Life/Build combs. Replaced the old flat 17-tile honeycomb.
Command queue repoint
Verified in _redirects/start /loops /queue now 200-rewrite to home.html (the live queue), not the legacy paste-token start-here page. The "▶ Open all" pill still points at start-here.html (the legacy manager, still reachable). Repoint is done; the pill label is the last stale pointer.
Issue
This is the "launcher wall" Sam wants demoted. 20 doors of equal weight is a menu, not a cockpit. Everything here can live behind ONE search/all-apps door.
Verdict
Keep the lanes as a collapsed "everything else, one tap away" drawer below the fold. Lead the page with truth-tiles + today's goal instead. Fix the "Open all" pill to say what it opens.

D · Linked surfaces

today.htmlKeep

/today  ·  LIVE (queue + calendar)
Does
Ranks the day into Must-do → timed → anytime → coming-up (8 days). Reads live QUEUE_JSON + business/family calendar.
Verdict
This is the real day-flow page and it's live. In the rebuild, the home "today" strip should be a compact read of exactly this — one source, two surfaces. Trim the stale KNOWN[] array (all June dates).

week.htmlRethink

/week  ·  STATIC — reads no feed at all
Confirmed
100% hardcoded. No hs-core.js, no /calendar, no fetch — the only JS reads the day-of-week to highlight "TODAY". "Your Week · synced" is a human sync (Sam↔Mildred), nothing is data-synced.
Risk
It duplicates the daily-routine schedule that today.html already encodes in routine(dow) — two hand-maintained copies that drift apart silently.
Verdict
Wire it to the live /calendar feed (same call today.html makes) so the week ahead is real, or fold "the week" into today.html's "coming up" and retire the separate page.

Obligations PWA (pwa-deploy/index.html)Rethink

/obligations → pwa-deploy/  ·  LIVE but old-style + wrong source
Does
Warm-skinned table of obligations with a top pill: Next 7 / Next 14 / Next 30 / This month / Show raw JSON. Per-persona skin (chanie/mildred/sam). Weather + Hebcal greeting strip. "Ping Zee" SMS/relay buttons.
Source fragmentation
This PWA reads the v1 Monthly Obligations sheet via its own legacy Apps Script endpoint (AKfycbw8…/exec, API key embedded in the file). But cashflow.html reads a different sourceops-api /bos/cashflow (the v3 tabs). Two obligations surfaces, two backends, two truths. This is the biggest structural issue in the money layer.
Top-pill
Sam's "old style" read is right: it's a horizontal button row over a data table — a 2023 pattern. The 7/14/30/month/raw windows are useful data cuts but presented as raw filters, not as a story.
Missing "still paying this?"
No per-row confirm flow. Sam wants each obligation to carry a "still paying this or not?" prompt that messages Sam / Chanie / both via the bot. The rails exist (the file already POSTs to /mis/notify for "Ping Zee") — it needs a per-row Yes/No/Ask-Chanie control wired to that relay + a write back to the sheet.
Verdict
Rebuild on ONE obligations source (pick v3/ops-api, retire the embedded-key legacy endpoint), rethink the top-pill into a "what's due / this month / everything" story, and add the per-row "still paying?" → bot-message control.

cashflow.htmlRethink (his favorite)

/cashflow  ·  LIVE (/bos/cashflow)
Does
Dark, sophisticated: the "tilt" (Daily/Weekly/Monthly/Quarterly/Biannual/Yearly), cadence-normalized outflow, category bars, and a debt avalanche (highest-APR-first) view. APR display already fixed (fraction→percent).
The gap
It shows outflow only. Sam wants income blended in and tagged by whose it is (Sam / wife / children), for the whole in-and-out picture. Income data exists (Chanie payroll ≈$19.7K/mo per memory) but isn't fed here.
Verdict
Add an income layer (net = in − out at each tilt window), owner-tag every inflow, and rethink the layout around net cashflow rather than a spend list. This is the money surface Sam cares most about — treat as a real design pass.

mis-cockpit.htmlFix small

/mis/brief + /mis/quotes  ·  MIXED
Does
The real MIS surface: regime + VIX, reconciled equity/cash/#1-risk/2.5%-clock, "the one move," live top movers from the 237-name universe. Reporting-only (engine freeze honored).
Live
Both feeds live with honest stale/unreachable banners.
Fix
A hardcoded #booknote ("basis fixes pending NVDA 5692, SPCX 4073", dated 7/2) and a stale default "Jun 24 close" label that persists if the brief fails. VIX yes, gold no (gold is only the UI accent color, not a price).
Verdict
This page already has what the home markets tile lacks. Home should read a compact slice of THIS (/mis/brief), not a separate hardcoded NVDA peek. Strip the frozen booknote.

str.html (check-ins)Rethink

/str  ·  LIVE (/hospitable/reservations?days=45)
Does
Today (check-ins OR checkouts matching today) + Upcoming (next 45 days). Live from Hospitable — probed 12:17 PM: 3 real reservations with guest names (Deztany Padilla 9332 7/7–7/21, etc.).
Missing "currently in-house"
Confirmed — it only matches check_in===today or check_out===today. A guest mid-stay (checked in earlier, leaving later) shows in neither section. For an ops board that's the main hole. The calendar feed does carry the spanning reservation (9312/9332 "Reserved" spanning today), so "who's in right now" is derivable.
Also missing
No last-7-days, no open maintenance/tasks per property. Sam wants all three (in-house now, last 7, open maintenance) in one view. The reservation feed alone can't do maintenance — needs the queue (cards tagged per property) merged in.
Reserved in/out labels
Home calendar shows "9312 · Reserved" without saying check-in vs checkout vs in-house. Derive the state from the span vs today and label each.
Verdict
Add a "Currently in-house" section (span contains today), a "Last 7 days" section, and merge per-property open maintenance cards from the queue. Label every reservation with its state.

everything.html (the index)Rethink (durable fix)

/everything  ·  STATIC — hand-maintained
Sam likes it
"Actually pretty cool." Keep the concept.
His questions, answered
How often does 🟢 NEW update? Never automatically — it's a hand-typed <span class="new"> that stays until someone edits it. How long does NEW stay? Until manually removed (some Jun-25 items still show NEW). Are all links working? Mostly, but dated-filename links rot as snapshots archive.
Contradictions found
Hero claims "by real file date, so it never guesses" — but nothing reads file dates. Hero says NEW is green + "refreshed Jul 1"; footer says NEW is red; CSS renders red; top entry is dated Jul 2. All hand-drift.
Verdict
Build the auto-index generator (loop 38-K6): a script that walks outputs/, reads real file mtimes, sorts newest-first, and auto-expires NEW after N days. That fixes "how often does it update" permanently and kills the everything.html + home NEW-strip staleness in one move.

E · Cross-cutting findings

  • The plumbing is real; the presentation buries it. 5 of 6 tiles fetch live. The home page's job — "what deserves attention right now" — is answered by a card sitting 4th on the page under two static banners.
  • Two obligations backends. The PWA reads the v1 sheet (legacy GAS + embedded key); cashflow.html reads v3 via ops-api. Consolidate to one source before adding features, or the "still paying?" writes land in a sheet the other surface doesn't read.
  • Three honesty bugs to fix first (cheap, high-trust): markets tile prints dollar-change as a percent; markets tile hardcodes NVDA; Affirm balance is 2+ months stale without a loud flag. These make live pages look wrong even though the feeds are right.
  • Chase is genuinely missing from Plaid — a data gap, not a display bug. Re-link or hand-enter with an "as of" date.
  • Per-surface personality is intentional (memory feedback_per_surface_personality_intentional). The rebuild unifies the SYSTEM (tokens, honest numbers, no dead links) without flattening Sam's dark command feel vs Chanie's warm vs the kids' kiosk. Consistency ≠ sameness.
  • Static pages that drift: week.html, everything.html, the home NEW strip, the today.html KNOWN[] array — all hand-maintained schedule/index content. The auto-index generator + wiring week.html to /calendar retires most of this class of rot.

Proposed build order

#ChangeWhy first
1Honesty fixes: markets % field, real top-holding not NVDA, Affirm stale-flag, open-loops scope labelsCheap, restores trust in the numbers
2Auto-index generator (walk outputs/, real dates, auto-expire NEW)Kills everything.html + NEW-strip rot permanently
3Home rethink: lead with ONE move + today's goal + truth-tiles; launcher → collapsed drawerThe core ask — "so I don't miss it"
4Cashflow: blend income, owner-tag (Sam/wife/kids), net in-outHis favorite surface; real design pass
5Obligations: one source, rethink top-pill, "still paying?" → botAfter source consolidation
6STR: in-house-now + last-7 + open maintenance; label reserved statesOps completeness
Single most important change: re-lead home.html with the ONE thing that matters today (move + goal + timed rest-of-day) and demote the 20-tile launcher into a one-tap "everything else" drawer. Everything else is a fix; this is the rethink Sam actually asked for.
Source trail
Artifact: C:\Users\ztrei\OneDrive\2. Hook Street\05. 2026 BH\outputs\2026-07-02_home-portal-deep-dive.html
Repo: hookstreet-workspace · last commit 4c45402 · working dir C:\Users\ztrei\OneDrive\2. Hook Street\05. 2026 BH
Live probes 2026-07-02 ~12:16 PM EDT: /health (ok, plaid production, schwab_set:false) · /plaid/balances ($1,992.09, 9 items, 0 failed) · /mis/peek?sym=NVDA (day_pct −1.76 vs day_change −3.48) · /hospitable (3 reservations) · /calendar?scope=business (live 9312/9332 spans)
Read: outputs/home.html · today.html · week.html · everything.html · pwa-deploy/index.html · cashflow.html · mis-cockpit.html · str.html · _redirects · docs/PORTAL_RETHINK_SPEC.md · docs/CONNECTIONS.md