| Status | Item | Round | Evidence | |
|---|---|---|---|---|
Round 108-27 first batch — shipped pre-tracker— | ||||
| Shipped | Funnel history skeleton flicker on the prospects list | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) The “By month of arrival” ghost rows now compute the same keys/labels the real rows land with, so React updates count cells in place instead of remounting the whole table when the fetch resolves. | ||||
| Shipped | Funnel collapse after switching tiles via the row-tab strip | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) The exact integration path (open tile A → switch to B → click B to close) had zero test coverage; a ProspectsClient-level regression test was added and passes — the 08-26 CardExpandGroup fix already handles it at the DOM level. A purely visual morph-animation repro stayed open (jsdom can't see it; no live-browser auth in that worktree). | ||||
| Shipped | Message delivery chip reveals its label on hover | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) DecisionLineChip (“Delivered” etc.) shows just the status dot at rest; hover/focus reveals the label with a circular hover ground. Single render site through ConversationThread, so this is the app-wide standard. Review round caught two real defects (label truncation at max-width:12ch; both fixed against the real render). | ||||
| Shipped | Call-card polish on the Clara audio player | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) ElevenLabs button gets rounded corners in the expanded player; play/pause gets the same circular HOVER_GROUND the ElevenLabs button already used. | ||||
| Shipped | Classification panel scrolls itself into view on open | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) ChatSurface's per-message details panel uses scrollIntoView({block:'nearest'}) — minimal scroll, none if already visible. | ||||
| Shipped | Prospect Summary phone number is no longer a tel: link | Round 1 | #6401 | |
Round 1 · Gera's 08-27 review, first batch (#6401, merged 08-27 — before this tracker existed) A click was placing a call, not just showing the number. | ||||
Round 2shipped 08-28— | ||||
| Shipped | “It should be broken up — there should be an open space” | Round 2 | #6421 | |
Rail · The progress rail The bar read as one line because each segment was width:100% of a column with zero side padding, so four bars abutted at 0px. Four segments with no gap are one line. Now 12px each side (the mock’s own value) → a 24px seam. Inherited by every horizontal rail. | ||||
| Shipped | “Canceled … should also be a horizontal line like the other ones, maybe half the length, but it’s red” | Round 2 | #6421 | |
Rail · The progress rail Replaced the ✕ dot. Needed no new vocabulary — it is an ordinary step with a failed fill. The declined stage now reads reached, because it happened; that is the whole argument for the shape, since recolouring in place cannot tell toured-then-cancelled from never-toured, and cannot show two cancellations. | ||||
| Shipped | “Maybe pull faster. It’s a little slow on how it moves” | Round 2 | #6421 | |
Rail · The progress rail Spring 300/30 → 600/48: ~2× faster and now essentially critically damped, so it lands instead of wobbling. Raising stiffness alone would have made it faster and bouncier. No new library — it is already framer’s shared-layout mechanic, shared with the landing page’s tab rail. | ||||
| Shipped | “The detail section under the leasing progress is hard to read … a lot of that stuff isn’t needed” | Round 2 | #6421 | |
Rail · The progress rail The six pasted lines are three: label and stamp on one line, one stamp per entry instead of two, and a description that merely restates its own label is deleted outright. | ||||
| Shipped | “The background gray is tight … the right is touching the right of the text” | Round 2 | #6421 | |
Rail · The progress rail The row band had no horizontal padding. Restored symmetric 8px bleed. The guard that banned it was written when this axis had row dividers to overhang — those were later deleted, so it was protecting a premise that no longer held. Re-scoped, not removed. | ||||
| Shipped | “Make sure all of them have the same title styling” | Round 2 | #6421 | |
Cards · Cards, titles and status Found the mechanism: “Prospect info” renders at 16px through a shared component; “Leasing progress”, “Follow-ups”, “Signals” and thirteen others were hand-rolled <h3> at 14px. Different size reads as a different font. All sixteen now route through the one row — including Clara’s Summary, shared by four detail pages, so it was small on all of them. | ||||
| Shipped | The title is now REQUIRED — no generic “Summary” left | Round 2 | #6421 | |
Cards · Cards, titles and status A card headed “Summary” names its shape, not its subject. Requiring it also retired a hand-written list of “files allowed to override the default” that had gone stale from 4 entries to 16 without noticing one. The replacement caught a real defect immediately: the tenant ghost imported the shared row and hand-rolled a second one. | ||||
| Shipped | “The prospect info card is way too white” | Round 2 | #6421 | |
Cards · Cards, titles and status Fields grouped onto one surface-soft inset with hairline separators. The real defect was the sparse case: three section headings over a lone em dash. | ||||
| Shipped | “On the renewed tenant page … it’s inconsistent with the prospect info” | Round 2 | #6421 | |
Cards · Cards, titles and status Correct, and verified before building: the prospect card got the grey body and rules; the tenant card had none of it. Now matched byte-for-byte, ghost included. The four AppFolio record tables sit outside the panel — they draw their own container, so panelling them would be a box in a box. | ||||
| Shipped | “Lease signed, green circle” — and out of the header | Round 2 | #6421 | |
Cards · Cards, titles and status Static Current status row on prospect and tenant, state in words with a small dot after it. No pill, no badge. Two lanes built the same field two different ways the same afternoon (8px no-tooltip vs 7px with one) — that duplication was itself the cohesion complaint, so it is now one shared primitive. | ||||
| Shipped | “Instead of Flag for collections … Current status … Flagged for collections” | Round 2 | #6421 | |
Cards · Cards, titles and status Same row, tenant page. | ||||
| Shipped | “The follow-up looks a lot smaller too” · “make it a little smaller and remove that horizontal line” | Round 2 | #6421 | |
Follow-ups · The follow-ups card Title onto the shared row; the “View messages sent” disclosure lightened and its rule removed. | ||||
| Shipped | “Not sent, they replied, not sent, they replied … just close all those and say follow-up dismissed” | Round 2 | #6421 | |
Follow-ups · The follow-ups card Dead rows collapse out of the rail entirely; one “Follow-up dismissed” card with its own background, moved below the touches. Each end state gets an honest sentence — notably a lead closed as not a fit does not say “dismissed”; nobody dismissed them. | ||||
| Shipped | “I don’t like that caution one, the yellow one … fold those into the dropdown and say held back” | Round 2 | #6421 | |
Follow-ups · The follow-ups card The repeated “Held back” rows fold into one default-collapsed group with the reason on a tooltip; the yellow banner goes. A suppressed send is the system working correctly, so a warning tone is the wrong register. | ||||
| Shipped | “We see 82, 38, 11, and 8 … we also need to see those when they’re collapsed” | Round 2 | #6421 | |
List · The prospects list page The tiles count who is currently sitting in a stage; the funnel counts everyone who ever reached it. Sean: “those are still tours that have been booked … they should still get credit for being.” Tiles now read the funnel’s totals. Applications also gains its real “N awaiting review” subtext — the field existed and was unused by the strip. | ||||
| Shipped | “Remove both of them and bring the calendar back to the data table” | Round 2 | #6421 | |
List · The prospects list page Two calendars, zero on the table. The second was added by mistake — “I was telling Claude, put it on the top right of this card … This is a bug.” Note the tension: the table’s chip was removed in an earlier round because he could not find it there, so it comes back visible and labelled rather than as a bare glyph. | ||||
| Shipped | “Remove the outcome filter … only show that filter when it’s selected from the close-up … you can exit out, but you can’t change it” | Round 2 | #6421 | |
List · The prospects list page 19 combinatorial options. Sean: “that looks like AI pushed that.” The chip goes, the predicate stays — deleting the filter entry would kill the funnel click-through too. A second redundant chip, “Funnel”, was found in recon and goes the same way. | ||||
| Shipped | “It should stay expanded unless you click collapse … everywhere” | Round 2 | #6421 | |
List · The prospects list page Reverses a dated decision (“exit is everywhere”). Outside-click collapse removed app-wide and its predicate deleted rather than left dormant; Escape kept, after verifying every host renders a real close button. Two consumers, not one. | ||||
| Shipped | “People have several conversation items where it should just be one” | Round 2 | #6421 | |
Deep · Conversations, dispatch, navigation Diagnosed: one-thread-per-person shipped as a presentation-layer grouping, not a physical merge — the rows stay separate and the LIST collapses them. The tenant page never used that lens. So this is a presentation fix on existing machinery, not a data migration. Threads stay as separate entry points, the clicked one highlights, plus a “view all conversations”. | ||||
| Shipped | “Clicking back here is a broken experience … it should be like a stack” | Round 2 | #6421 | |
Deep · Conversations, dispatch, navigation The reader’s back reaches the tenant; the tenant’s back then walks history into the conversation just left. Canonical parent href, not history.back(). | ||||
| Shipped | “Show the message that was sent out to the handyman … I do see it in the messages” | Round 2 | #6421 | |
Deep · Conversations, dispatch, navigation Dispatch is reported but the message that went out is not shown. Constraint given to the lane: never reconstruct what “would have been” sent — if the body is not retrievable, link to where it lives. | ||||
| Shipped | “Change that to the dark blue … also can we put a tooltip on it” | Round 2 | #6421 | |
Deep · Conversations, dispatch, navigation The AppFolio deep-link button onto --color-primary, with an arsenal tooltip (never a native title — it waits ~1s and reads as “nothing is there”). | ||||
| Shipped | “133 inquiries seems strange” — the 12-month number | Round 2 | #6421/#6448/#6463 | |
Open · Not started — and why Cause found: applied prospects bypass the date window entirely, everyone else is windowed by last activity rather than arrival, and signed leases use a third clock — three populations under one “Inquiries · Last 12 months” label. A fourth rule is used by the history table one component below. Deliberately kept OUT of this PR: it is a data-semantics change with reach beyond this page, and changing it while restructuring the tiles would make it impossible to tell which change moved a number. RESOLVED 08-29: Absorbed by the funnel rebuild: windowing is one documented predicate (prospect-date-window.ts — the APPLIED bypass is now a deliberate, documented rule) and every windowed surface labels the window it actually counted, so no label claims a window the query didn't apply. | ||||
| Shipped | The “Active Prospects” tile — keep it or drop it? | Round 2 | b88033739: 4 tiles ✓ | |
Open · Not started — and why Gera gave four numbers; the strip has five tiles. Building it as four (Inquiries / Tours / Applications / Signed) because in the code inquiries IS the cohort, so Active and Inquiries would print the same number side by side — the duplication behind his own recorded question, “Is that what Inquiries is? But then we also have inquiries.” One word reverses it. RESOLVED 08-29 (b88033739): Gera confirmed four tiles — Active IS Inquiries; the strip already ships as four (the Active compartment left with statChrome.ts, 08-27). No change needed. | ||||
| Parked | Fede’s quality/judgment desk | Round 2 | b88062780 | |
Open · Not started — and why Standing open invitation from the standup: “the UI is not great, like I said. If you want to help me improve it.” Plus a backlog item of his to unify the agent-trace debug view with the client-facing one. Never scoped. RESOLVED 08-29: Raised as b88062780: scope now vs park; recommendation is park, per Fede's own standing objection to more review UI. 08-29 update: PARKED per the b88062780 resolution (auto-resolved on Gera's relayed pre-authorization, recorded as answered by this session): Fede's own standing objection to more review UI governs; revisit when he brings a concrete ask. | ||||
| Withdrawn | A funnel-view / current-stage-view toggle | Round 2 | declined 08-27 | |
Open · Not started — and why Sean proposed it; Gera declined — “I think it’s just gonna confuse people.” Recorded so it does not get built later by someone reading only the first half of the thread. | ||||
Round 308-28 pass— | ||||
| Shipped | “The leasing policy should always be at the very top right where the calendar used to be, not with the data table” | Round 3 | #6476 · b87932878 | |
Standards · Across the board, not one page “It’s more like of a page setting. This should be across the board.” A page-level setting does not belong in a table toolbar — the toolbar is for things that filter rows. Needs a named slot on the page shell so every list page puts its settings in one place. RECON: There is no top-right region to move it back into — the prospects page has no PageHeader and no h1 at all; #6448 collapsed the row that held the calendar. The region must be created. And the standard pulls against Gera’s own 08-27 call to move the date control into the table toolbar, which a guard now pins. Needs his ruling before code. RESOLVED 08-29: Gera (b87932878): create a page-settings slot on the shared page shell — SHIPPED in #6476 across all six policy pages (this row was a stale duplicate of that shipped item). | ||||
| Shipped | “All these changes you’ve made have been incredible but they’re not all very mobile friendly” | Round 3 | #6463 · #6476 · #6535 | |
Standards · Across the board, not one page “We gotta make sure that things are adapting as they should to make things consistently mobile friendly.” This is the honest cost of the last two PRs: six lanes asserted on rendered markup, and jsdom lays nothing out — so no viewport was ever exercised. Needs an actual narrow-viewport pass over everything shipped in #6421/#6448, not a spot fix. RECON: The hypothesis was mostly wrong — nearly every grid in the batch already carries a breakpoint, and much of it is deliberately mobile-aware with the reasoning in the source. The real damage is nowrap flex rows and one unwrapped table. The genuinely big finding is app-wide and predates this batch: the side nav never hides on a phone, so every page pays 56px of a 390px screen. Scoped separately. CLOSED 08-30 (verified in origin/main, not assumed): The named residual damage (nowrap rows, unwrapped table) shipped in #6463's narrow-viewport pass; the side nav's phone tier in #6476; the vendor hint reaching a phone in #6535. Gera's promised MOBILE feedback round is future input, not this row. | ||||
| Shipped | “When it comes to bodies we should lay things out consistently the same” | Round 3 | #6536 | |
Standards · Across the board, not one page Selected-state bodies (e.g. “Tour requested” + the date) should take their layout from Prospect info, which he singled out as the one that reads right. Same label/value rhythm, same rules, same spacing. RECON: Copying the Prospect info rhythm literally would redden two drift guards written from Gera’s own earlier rulings — one bans the uppercase micro-label off a chip, the other bans the exact label→value gap that pattern uses. RESOLVED 08-29: Superseded by Gera's own Round-4 reversal (labels lighter, values stronger) and made consistent structurally: the shared Field primitive shipped in #6536 reaches every entity card, so bodies lay out identically by construction. | ||||
| Shipped | “That gray card could be slightly less gray — it’s a little too gray” | Round 3 | #6536 | |
Standards · Across the board, not one page The surface-soft inset introduced in #6421 to fix “way too white” overshot. One token, but it is shared, so it moves every card that adopted the inset — which is the point. RECON was right that a bare nudge wasn’t one token — the fix wasn’t a nudge. SHIPPED in #6536: the token was split by ROLE instead — a lighter --color-surface-soft for text/inset grounds (#F2F2F7 → #F5F5F9), with the button-ground role (--color-surface-control, including the identity ChatSurface was reasoning about) held at the original value. Contrast recomputed against every quiet ink in both themes, not assumed. | ||||
| Shipped | A cancelled tour does not appear in the leasing progress at all | Round 3 | #6463/#6533 · b87924840 | |
Progress · The leasing progress rail — what it shows, and what it is missing “People like this, they canceled the tour but we don’t see that in the leasing progress… it should go to like the small little line saying tour canceled without thread, and it should be on that state, still unclicked for the progress.” #6421 built the half-width red step; this is the case where the event never reaches the rail in the first place. RECON: The row already exists and already works (ProspectDetailClient.tsx:806-818, with a passing test). It is blank on this record because a duplicate-prospect merge stranded the Tour row on the tombstoned person — and worse, the closed duplicate is rendering the survivor’s live tour as its own. That data bug is fixed in #6463. The “unclicked” half reverses funnel-reach.ts:106 and is on the Decisions tab. CLOSED 08-30 (verified in origin/main, not assumed): Data bug fixed in #6463 (stranded-tour join); the rail step itself shipped in #6533; and Gera DECLINED the unclicked reversal — b87924840: “Keep it filled — booking is the funnel event, as the code already decided.” Every half of this row is terminal. | ||||
| Shipped | The rail should show which message drove the state — and the state itself may be wrong | Round 3 | #6463/#6533 | |
Progress · The leasing progress rail — what it shows, and what it is missing “I want that leasing progress to show what message led to that decision state, because reading the messages it doesn’t look like the tour was cancelled — but maybe I’m missing something.” Two things in one: a linkable evidence affordance on each state, and a check of whether this record’s cancellation is real. If the messages do not support it, the rail is not the bug — the derivation is. Read the data before building anything. RECON: Gera’s self-doubt was correct — that prospect never cancelled. A human did, during a dedup merge. The cancel reason is structurally unreachable today: it lives only in tour.history[].detail, and the only loop over it sits behind if (tour), which is dead for a cancelled tour because applyCancel nulls prospect.tourId. CLOSED 08-30 (verified in origin/main, not assumed): The structural unreachability is fixed: buildActivityLog now takes the full tourHistory (not the nulled live tour), extracts cancelledTours, and renders TourEvent.detail — the cancel reason and source reach the activity log (parseTourEventDetail / stripTourTrigger). The false-cancellation on the anchor record was a human dedup action, fixed in #6463. | ||||
| Shipped | Kill the “Check-in scheduled” card; put the schedule on the rail | Round 3 | #6476/#6520 · b87933653 | |
Progress · The leasing progress rail — what it shows, and what it is missing “We don’t need that card, right? It would just be… we have that vertical line to the next open dot, and it should show the whole schedule of the follow-up — when it’s gonna happen — but those that are pending maybe have like a little pending note.” Future touches become dots on the existing rail with a pending marker, instead of a card restating them in prose. RECON: Largely already built for stage 1 — the rail renders future touches with “Scheduled Today / Scheduled Sep 1”, a grey ringed dot and a “Next” chip. “Check-in scheduled” is a Badge pill in the header, not a card, so “we don’t need that card” has no unique referent. The real gap is that the stage-2 nudge cycle contributes zero rows to the rail. Also: showing un-attempted touches on a stopped cadence reverses #6421 and its guard. RESOLVED 08-29: Gera's full answer (b87933653) — consolidate into the vertical stepper with collapsible dismissed cycles — IS the brief that shipped as the follow-ups fold (#6476) and the nested dismissed chain (#6520). | ||||
| Withdrawn | The tour card duplicates the tour section in Prospect info — consolidate | Round 3 | 08-28 | |
Cards · Prospect detail — consolidation and labels “We have a tour card that says added to calendar automatically, but we also have a tour section in the prospect info, so we don’t need the tour card.” Replacement he described: a “Prospect events” block — a small table, first row the tour and its date, with the icon carrying “added to calendar automatically” on hover rather than as a line of copy. RECON: The fold-in already happened 2026-08-27, on Gera’s own instruction. What remains in the card is not duplicated: a reminder stamp, a Google Calendar link, an .ics download and past-tour history. And “added to calendar automatically” is one of three states — the other is “manual”, which is those two buttons, so an icon-with-tooltip silently deletes them. On the Decisions tab. | ||||
| Shipped | “Following up automatically” reads nothing like the other cards | Round 3 | #6476/#6520 | |
Cards · Prospect detail — consolidation and labels Six stacked lines — Check-in scheduled · Clara will check back in if Raul goes quiet · Checking back in · Today · Stops the moment Raul replies, books a tour, or opts out · Phone call · Sent · Aug 25 — where the equivalent elsewhere is one row. “It looks very different from the other ones… I feel like here we should have said a phone call.” Pairs with the rail item above: most of this block becomes rail dots. CLOSED 08-30 (verified in origin/main, not assumed): The six stacked lines are gone from the code (the “Clara will check back in / Stops the moment” strings no longer exist) — the block consolidated into the stepper fold (#6476) with the dismissed cycle's nested chain (#6520). | ||||
| Shipped | Drop “from the funnel” from the filter label | Round 3 | #6463 | |
Cards · Prospect detail — consolidation and labels “It says ‘from the funnel · Toured’. I don’t think we need to say from the funnel — the icon you added is good enough. And then if you hover over it, it should say from the funnel.” The dismissible non-editable label from #6448 keeps its icon and its X; the words move to the tooltip. RECON: The icon is aria-hidden and the house Tooltip’s aria-describedby lands on the wrapper, not the child — so deleting the words removes the pill’s only accessible statement of where the narrow came from. Shipping with an sr-only replacement. (The “·” is a 1px rule, not a middot.) | ||||
| Shipped | Say “Last six months” — no dates in the column | Round 3 | #6463 | |
List · The window label “I used to do last 30 days. For now, the one we have, we don’t need to say date on our column — so just say like last six months. I think it’s fine that it’s fixed to the right.” The explicit range resolves to a plain phrase, and its right-alignment is confirmed as intended rather than something to change. RECON: The window is 90 days today and the chip reads “Last 3 months”; captioning it “Last six months” would state a window the query never applied. So both halves ship together — the default moves to six months and the label follows. Also corrected: the chip normally prints a phrase, not a date, and dropping label="Date" yields “Range”, not nothing. | ||||
| Shipped | “Whichever one we click is highlighted, kind of like the work order handyman is highlighted” | Round 3 | #6462 evidence | |
Convos · The conversation highlight — greenlit The pattern exists — ConversationThread’s focus band (focusWorkOrder → highlightScope) — but it is keyed to a work order via messagePertainsToWorkOrder. Making it mean “the messages from thread X” means widening highlightScope to a predicate inside a ~2,000-line component mounted on every conversation surface in the app, plus a prop through ConversationDetailView and a field on OwnedConversation. Own PR — the blast radius is why it was not folded into #6421. RECON: Blast radius was overstated — by me. highlightScope is a local const, not a prop; the band primitive is already generic; ?person=1&c= already resolves; and Message.sourceConversationId — the exact field needed — is already stamped on every row of a merged read. The residual is one additive prop plus row hrefs. | ||||
Round 408-28 desktop review— | ||||
| Shipped | The person in a conversation should be a hyperlink, resolved by a role waterfall | Round 4 | 08-28 | |
People · Person links, and which page a person belongs to “I thought we had hyperlinks for the person — so if I click the ‘from’ it takes me to the prospect page.” The waterfall he described, in his order: property manager → vendor → tenant → prospect. PM is a no-op for now — “that would just be continue, right, because we don't have that person rule yet.” The load-bearing case: “if a person is clickable and they're only a prospect then obviously they're gonna be the prospect, but if the person has prospect information AND tenant, we're gonna take them to their tenant page.” Stated future direction, not this task: “the right goal is to consolidate everything from their prospect into their tenant.” Not reconned. Note this sits directly on the Person spine — one person, several role rows — so the waterfall is a query over role rows, and the interesting cases are the ones with two.IN REVIEW — #6537. Shipped as a view-only join stamped onto the conversation read and rendered through the shared | ||||
| Shipped | Replace the dock's expand button with “go to conversation close-up” | Round 4 | 08-28 | |
Convos · The conversation surfaces — one of these may be a real bug “We can remove the expand button in places like this, because I mean we're never really gonna be expanding — if anything it should take us to a place like the conversations close-up page. So if we're trying to share a specific conversation in depth it'd be something like /tenants/<id>/conversation. I want to replace the expanded with go-to-conversation-close-up, something like that.” Not reconned. Worth checking against the existing “Open as page” affordance and the view-full-href builders, which already spell every entity's conversation url — this may be a re-point rather than a new link.IN REVIEW — #6537. Shipped — | ||||
| Shipped | The bare conversation page shows LESS than the same page with query params | Round 4 | 08-28 · two live urls | |
Convos · The conversation surfaces — one of these may be a real bug “It does make me feel like there's more conversation because I see this one ?scope=person&thread=<id> and it has more context. So there's definitely something not going right in the conversation, because all of it should just be in the /tenants/<id>/conversation page directly without having to pass in additional fields.” The one item in the round with its own evidence attached — two urls, same tenant, different amounts of history. Not reconned.IN REVIEW — #6537. Shipped — the entity route ( | ||||
| Shipped | What the conversation page should do, stated end to end | Round 4 | 08-28 | |
Convos · The conversation surfaces — one of these may be a real bug Given as a clarification of the row above, and it settles the question that row leaves open — merged by default, not merged on request. In his words: “In the tenant page I do expect to see five conversations, that's fine. But if I click one of them, I expect to go to the conversation page and then it just shows thread and maybe the highlighted area… I can also see the entire conversation of that individual. So it's focused and highlighted on the one that was selected, but it shows the entire conversation — that's the idea.”So the row list stays, and each row is an ENTRY POINT rather than a door to an isolated thread. Three consequences, all his: the entity route reads the whole person by default; the clicked thread is banded inside it; and the url stops carrying the lens. IN REVIEW — #6537. Shipped as specified — the row list stays, each row is an entry point, the clicked thread is banded inside the full merged read, and the URL stops needing | ||||
| Shipped | Drop ?scope=person from the entity route — the url already says whose it is | Round 4 | 08-28 | |
Convos · The conversation surfaces — one of these may be a real bug “It says scope equals person in the URL. That feels like something's wrong in there — like why do you need to scope it to person? I feel like just being part of the tenant URL and then conversation, it should be pretty clear that it's the person because it's part of the URL. Same tenant.” He is right about the redundancy: /tenants/<id>/conversation already names exactly one person, so a param re-stating it is noise on a url people paste to each other. Not reconned. Two things to keep straight when this is built: the vocabulary shipped in #6476 is accept-old, emit-new and never break a link, so ?scope=person must keep RESOLVING on old shared urls even once nothing emits it; and the param is not redundant on the global /conversations/<id> route, which names a thread rather than a person and genuinely needs to be told. This ask is about the entity routes only.IN REVIEW — #6537. Shipped — the param is gone from the entity routes (old links still resolve); the global | ||||
| Shipped | Adjacent highlighted messages are ONE band, not one per message | Round 4 | 08-28 | |
Convos · The conversation surfaces — one of these may be a real bug “If it's a message, let's say message 21 and message 22, I shouldn't see two highlighted items — I should just see one grouped highlighted, right? So everything gets highlighted together.” A contiguous run of banded messages should read as a single region. Not reconned. This lands on the band shipped in #6462, so the question is whether the band is painted per-row today (N separate tints stacking into a striped block) or per-run — and if per-row, whether grouping is a render change or a change to how the run is computed.IN REVIEW — #6537. Shipped — a new pure | ||||
| Shipped | Make “forward to property team” a collapsed, expandable card | Round 4 | 08-28 | |
Convos · The conversation surfaces — one of these may be a real bug “Kind of like the phone calls — could we make that forward-to-property-team also a card that's collapsed and then we can expand it, and it looks a little bit more well formatted? Because right now it looks a little choppy.” The phone-call row is the named reference for the treatment. Not reconned.IN REVIEW — #6537. Shipped — the forward event now renders through the same extracted | ||||
| Shipped | Remove the two “Clara passed a question to your team” cards | Round 4 | 08-28 | |
Header · The conversation close-up — what belongs at the top “I don't know what this is, but we should remove these cards — I didn't really ask for these… let's remove those two top cards because they don't quite belong here.” The cards restate a handoff (“Clara passed a question from [a prospect] to your team… once someone has taken care of it, mark it handled so the reminders stop”) with a Mark handled button. His reasoning is that the handoff is already represented: “in places where they hand it to the team, we already have ‘forward to property team’ and information like that — so that's already set.” Not reconned. Careful here: the Mark handled button is the stop-authority writer for a reminder cadence, so removing the card must not remove the only way to stop the reminders.IN REVIEW — #6537. Shipped, and done carefully rather than deleted outright: the banner is gone, and its “Mark handled” stop-authority button — the only human way to end a live reminder cadence — was relocated onto the forward-to-property-team card above rather than removed with the banner. | ||||
| Shipped | Move “Started <date>” into the details; keep only “Last activity” | Round 4 | 08-28 | |
Header · The conversation close-up — what belongs at the top “This one says ‘Started Jul 18, 2026, 2:44 PM · Last activity 3d ago’. I feel like we can move the started into the details, and then just leave the last activity three days ago — I feel like that's the more important one.” Not reconned. The details disclosure already exists on this surface, so this is a relocation rather than a new affordance.IN REVIEW — #6537. Shipped — moved into the Details panel with a relative-time wrap that keeps the existing zone-coverage guard honest. | ||||
| Shipped | Conversations are grouped inconsistently, and the grouping rule may be wrong | Round 4 | 08-28 · live url #6534 | |
Model · What IS a conversation — the definition question “How we define conversations is a little tricky. In some examples they are together, but in others the conversation is scattered throughout… it's not really one conversation, it's several. For the most part I did group one chunk and then I had a couple breaks and then another chunk — so it seems like we're grouping by maybe subtopic or something, which isn't really what a conversation is. We can have many conversations about the same topic but they still should be separate conversations.”His working definition, offered and immediately doubted — quote it, do not resolve it for him: “a conversation is a grouping of messages within a time frame, or in a sequence, about a similar topic even if it changes topic. I really don't know what the definition of a conversation is in this context, because it's not just time and it's not just topic — but we gotta have some standard. Right now it's not bad, but it does feel like there's room to grow.” The concrete confusion: “especially that one where it's like the resident inquiry / leasing access — I click it expecting to see one conversation but I see a bunch of stuff just highlighted, and that's confusing.” That symptom is the same one as the grouped-band row above, seen from the other side: scattered bands read as scattered conversations. Not reconned — but one thing is already known and cuts against the hypothesis. A Conversation is a STORED container with a required personId, minted by channel and lifecycle events — voice mints one per call, renewal cycles get their own, email and SMS live apart. Topic is classified onto conversations by a nightly cron; it is not what creates them. So “we're grouping by subtopic” is probably not what the code does — which makes the symptom more interesting, not less: if the containers are minted per call and per channel, then a single human back-and-forth that crosses a call and a text is already several conversations by construction, and no amount of display grouping fixes that without a rule about what to merge.SHIPPED in #6534. The hypothesis was right and the diagnosis was sharper than expected: there is NO topic-based grouping anywhere — topic was removed as a segmentation boundary back on 2026-07-18. The cards only looked topic-grouped because the header is labelled topic-first. The real scatter traced to two causes: the boundary was the calendar day, so a Monday-night-to-Tuesday-morning text exchange shredded into separate cards; and voice minted a fresh conversation row on every single call at ring, with no lookup for a repeat caller. Fixed by replacing the day boundary with an 18-hour idle-gap rule (his own words — "a grouping of messages within a time frame" — describe an idle gap, not a day), with day dividers kept as in-flow chrome so date navigation is unaffected, and routing voice through the existing canonical-conversation resolver so a repeat caller lands in their own thread. 2472 tests green. | ||||
| Shipped | A conversation card that expands into its sub-conversations | Round 4 | 08-28 #6534 | |
Model · What IS a conversation — the definition question “Maybe in the conversation card in the tenant close-up page, if it has many sub-conversations it has a dropdown and it expands, and then I can choose the more specific one.” His proposed shape for living with the above rather than solving it — the card admits it contains several and lets a reader pick. Not reconned. Note this is structurally the same affordance as the dismissed-follow-up chain in #6520 — a row that folds a set and opens into its members. SHIPPED in #6534 — the tenant page's Conversations card now groups its rows, sorts newest-first, and a group with more than one sub-conversation expands to choose a specific one, through the same house Disclosure the rest of the app uses. | ||||
| Shipped | Labels lighter, values stronger — not a grey body | Round 4 | 08-28 #6536 | |
Type · Info-card bodies — a reversal, stated as one “Something I've changed my mind on. On the body of the info cards, like tenant info, we had made it gray. I actually don't want to make it gray. I kind of want to make it look like the Vercel page, where the titles are more grayish and more light, and the values are a little harder — the values are a brighter color.” He supplied the reference markup: a Geist <dl> where the dt sits at --ds-gray-900 and the dd at --ds-gray-1000 with font-medium — so label recedes, value advances, which is the inverse of a uniformly grey body. Not reconned. Two things to check before building: this reverses “that gray card could be slightly less gray” from round 3 (still open, still blocked), and it pulls against the round-3 standard “when it comes to bodies we should lay things out consistently the same” whose recon found that copying the Prospect-info rhythm literally would redden two drift guards written from Gera's own earlier rulings. SHIPPED in #6536 — the shared Field primitive now renders the label at a lighter, normal-weight ink and the value at the stronger one, in one edit reaching every entity card that imports it (tenant, prospect, vendor, work order). The round-3 "slightly less gray" ask below is the SAME PR's other half. | ||||
Round 508-29 desktop pass — done = all 15 complete; mobile is a later round— | ||||
| Shipped | The four prospect top-card numbers render black regardless of value | Round 5 | #6616 · 3f7d3b13 | |
New · Raised live, 2026-08-29 — desktop pass On the prospects list, the four top metric cards — Inquiries, Tours, Applications and Signed Leases — must print their number in the plain text colour whatever the value is. The numeral carries no good/bad semantic tint. Scope note for whoever picks this up: this is about the numeral itself, not the rest of the tile’s chrome. | ||||
| Shipped | The prospect pipeline’s default date window becomes 60 days | Round 5 | #6616 · 3f7d3b13 | |
New · Raised live, 2026-08-29 — desktop pass What you get on login is a 60-day window — not 14. This answers and supersedes the long-open dateWindow=14 vs the 60-day window row in the Slack backlog, which is now closed pointing here. Conflict to be aware of: that row had been resolved on 08-29 under decision b88046872 as “keep 180”. Gera’s 2026-08-29 dictation overrides that: the default is 60 days. Build to 60; the earlier 180 ruling is history. Decision lineage (read before building): earlier on 08-29 Gera answered "keep 180" (decision b88046872) when asked about the stale 14-day premise — this Round-5 instruction is NEWER and supersedes it: he wants 60 as the default now, knowing the history. Build 60; do not "correct" it back to 180 citing b88046872. The 60-day ladder row and the window-chip ghost width (WINDOW_CHIP_GHOST.measuredFor in tableDefaults.ts) must move together. | ||||
| Shipped | Clicking the already-active stage on the leasing-progress rail should do something coherent | Round 5 | 08-29 · our call | |
New · Ours to decide — raised 2026-08-29 On prospect detail, you cannot click the active stage — e.g. Inquiry when Inquiry is already active — to un-activate it, so the click lands nowhere. Gera explicitly handed the resolution to us: “I’ll leave that up to you — if you feel like collapsing is fine then we could collapse it too.” Recorded as ours to decide, not an open question for him. Collapsing the stage is the leading candidate — pick it or a better answer and ship, do not route this back to Gera. | ||||
| Shipped | Remove the Clara avatar from the Clara summary — everywhere it appears | Round 5 | #6613 · 495da897 | |
New · Raised live, 2026-08-29 — desktop pass The Clara summary should carry just the title, reading the way the plain “Prospect info” heading does. Gera called the avatar/icon out on both the prospect detail and the tenant detail and then generalised it: “remove it everywhere.” Treat this as app-wide — sweep every surface that renders the Clara summary, not only the two he named. | ||||
| Shipped | One date standard app-wide: show the date, put the exact time in the hover tooltip | Round 5 | #6614 · 8a771250 | |
New · Raised live, 2026-08-29 — desktop pass Render the date — e.g. “August 29” — and reveal the precise timestamp only on hover. Applies to the inquiry bodies, the follow-up cards, the activity log and the other cards. The concrete offender Gera named is a row currently reading “received in AppFolio at 7:28 p.m. MDT”: that should read “August 29”, with “7:28 p.m. MDT” moved onto the tooltip. | ||||
| Shipped | The renewals detail page doesn’t match the other detail pages and must adopt the standard | Round 5 | 08-29 · renewals | |
New · Renewals detail — one consolidated effort, 2026-08-29 Gera’s example page: /leasing/renewals/lease_87c46bc1-ee97-4138-a837-9fef0373aff4. It is a detail page and should read like the tenant, prospect and work-order ones; today it follows none of them. Consolidated effort: Gera scoped the five renewals rows in this round as one piece of work — “feel free to have a sub-agent tackle that one specifically, like fully” — so they land together, not piecemeal. | ||||
| Shipped | Drop the renewals page’s bespoke progress/status treatment | Round 5 | 08-29 · renewals | |
New · Renewals detail — one consolidated effort, 2026-08-29 The chrome invented for this page goes: “progress stop needs your attention”, “elapsed time 3 minutes”, “handed to you”. None of it exists on the other detail pages and the renewals page should not be the exception. | ||||
| Shipped | Resolution, Policy approval and AppFolio preparation move onto the standard detail-page info cards | Round 5 | 08-29 · renewals | |
New · Renewals detail — one consolidated effort, 2026-08-29 Those three sections currently have their own shapes. They become standard info cards, the same component and layout the tenant, prospect and work-order detail pages use. | ||||
| Shipped | The right-rail “Renewal offer summary” adopts the standard summary-card pattern | Round 5 | 08-29 · renewals | |
New · Renewals detail — one consolidated effort, 2026-08-29 Instead of its own bespoke shape, the right-rail summary uses the standard summary card that tenant, prospect and work-order detail pages already share. | ||||
| Shipped | The renewals conversation becomes a link out, not an inline thread | Round 5 | decide b88064049 | |
New · Renewals detail — one consolidated effort, 2026-08-29 The subject of a renewal is a tenant, so the page links out to that tenant’s unified conversation — shape /tenants/<tenantId>/conversation — ideally landing on the relevant highlighted portion of the thread. Explicitly: do not render the conversation inside this page. Lines up with the In-flight item on entity conversation links opening the person’s whole unified thread, highlighted. | ||||
| Shipped | Zero work orders should collapse, not render empty-state art | Round 5 | #6615 · cd8ac5c4 | |
New · Tenant detail — refinement, 2026-08-29 On tenant detail, when the work-order count is 0, drop the orb/illustration entirely and just show 0 in a collapsed row. 08-29 update: Boundary-definition question RAISED as b88064049 (the one decision deliberately NOT auto-resolved — Gera explicitly reserved it). Recommendation on the card: contiguous span using the #6534 idle-gap segment, one solid band per #6537. | ||||
| Shipped | The tenant unit / past-due block is doing far too much and doesn’t follow the detail style | Round 5 | #6615 · cd8ac5c4 | |
New · Tenant detail — refinement, 2026-08-29 Today that one block crams together: Unit 506 · lease through Jan 31, 2027; a $300 past-due figure in red; “past due 31+ days · oldest in 31–60 days · as of Aug 1, 1:25 AM”; Recorded / No stage set / Observed / Outreach complete / Language / “Language not asked”; the four aging buckets (1–30 $1,000, 31–60 $300, 61–90 —, 91+ —); Account balance $1,300; and a “Build a demand for payment or possession →” call to action.Two things to do. First, standardise it and cut the colour noise — the red $300 and the “Language not asked” styling are the named offenders. Second, fold whatever belongs into the Tenant info table as its own sub-section separated by a horizontal rule, exactly the way Contact is delimited today. Gera: “everything that could be put into the tenant info would be perfect.” | ||||
| Shipped | Collections activity stays a separate section — that one is fine as-is | Round 5 | #6615 · cd8ac5c4 | |
New · Confirmed keep — 2026-08-29 Recorded so nobody folds it into the Tenant info table by mistake while doing the unit/past-due consolidation. Gera reviewed it and it stays its own section, unchanged. | ||||
| Shipped | The “also on file — prospect” cross-link is good and stays | Round 5 | #6615 · cd8ac5c4 | |
New · Confirmed keep — 2026-08-29 Confirmed keep. The cross-link from the tenant to the same person’s prospect record is working as intended — do not remove or restyle it away during the tenant-detail refinements. | ||||
| Shipped | The Back button must behave like a history stack, not a hierarchy | Round 5 | #6618 · 907f1a46 | |
New · Raised live, 2026-08-29 — desktop pass Repro: from the tenant page, click through to the linked prospect, then hit Back — it currently lands on the prospects list. It should return you to the tenant you came from. Back is a history stack, not a walk up a hierarchy. Gera: “the back button shouldn’t be blind.” | ||||
Round 609-01 cleanup pass — chart idiom, reveal speed, the window standard, prospect preferences— | ||||
| Merged | The EXPANDED Leasing pipeline chart still draws the old bar idiom | Round 6 | #6776 | |
Gera · 2026-09-01 cleanup pass His words: “when I click leasing pipeline on the dashboard, it shows like the old version of the bar charts… when we pre-click, that bar chart is cleaner than the one of the expanded version. The expanded version looks kind of old and janky.”He is comparing two real things, and the premise checks out. The COLLAPSED card renders CategoryView — a dot swatch, label and number on the card ground, over a 6px bar. The EXPANDED card renders FunnelView, which had its own idiom: a 36px filled block per stage, an 18%-tint fill with a 2px accent right edge, and the label and number knocked out INSIDE the block. One funnel, two bar languages, and the heavier one is the one clicking promotes you into.BUILT: the stage row now adopts CategoryView’s presentation verbatim — same swatch, type sizes, 6px/3px geometry and hover pop — while keeping everything CategoryView has no notion of: the between-stage conversion rows, the drill-down caret and the inline member accordion. The 8% width floor stays (a slim bar needs it more than a fat one did). Deliberately NOT merged into one component: CategoryView sorts by magnitude and prints a share-of-total percentage, both wrong for a funnel, whose rows are ordered by STAGE and whose ratios are the conversion rows beneath them. MERGED in #6776. Review caught that the first draft answered the wrong question — it defended not folding the two COMPONENTS together when what it had copied was the ROW. There is one shared BarRow now; both views consume it, and only the container concerns stay apart. Rendered before/after. | ||||
| Merged | The Leasing / Renewals tab strip fades in too slowly on expand | Round 6 | #6776 | |
Gera · 2026-09-01 cleanup pass His words: “when I click it, it says like leasing pipeline and renewal pipelines — that slowly fades in. Can we kind of have it show up a little faster?”What the delay is for, and why it cannot just be deleted: the expanded shell flies from the collapsed card’s box, while RowSwitcherTabs is laid out against the FINAL full-width box — so mid-flight the strip paints wherever the origin box is. That is Gera’s own 2026-07-22 bug (tabs sitting far-right before drifting home). The mask is load-bearing.BUILT — bought by SHAPING the ramp, not by trusting a shorter wait. The old schedule waited for the flight to be essentially over: the expand spring (stiffness 300, damping 30 → ζ≈0.87, ωn≈17.3/s) is 2%-settled at 4/(ζωn) ≈ 0.26s, then a LINEAR 0.12s fade ran after it — first pixel 0.26s, full 0.38s. Because the residual offset decays as e−ζωnt, a linear ramp spends half its opacity in the half of the fade where the error is still largest. An ease-in curve inverts that: 0.14s delay, 0.16s fade, [0.4,0,1,1] → visible at 0.14s (46% sooner), fully opaque at 0.30s (21% sooner), and dimmer at every instant than the old linear ramp would have been at the same offset. The 2%-settled bar the old delay was built on is still the bar — it now governs full opacity rather than the first frame. Switches stay appear=’instant’; the no-animation switch rule is untouched.Deeper fix NOT taken, and why: hoisting the strip out of the projected subtree would make the reveal genuinely instant and need no mask at all. It works for the CardExpandGroup host (the transform is on the motion.div itself; its grid cell is stable) but NOT for the dashboard canvas host, where the transform sits on an ancestor overlay. A correct version needs a portal target in the canvas too — real work, larger blast radius than “a little faster” asks for. Logged here rather than done silently.MERGED in #6776. Review also caught two overclaims in the derivation comment — the ease-in is ~33% opaque at the fade midpoint (not ~16%), and “dimmer at every instant than the old ramp” is false for a schedule that starts 46% sooner. The honest claim, now in the comment, is against a LINEAR ramp over the same new window. | ||||
| Merged | The “Specific months…” tab UI is a card inside a card — redesign the whole popover | Round 6 | #6775 · round 4 | |
Gera · 2026-09-01 cleanup pass — open design question His words: “when I click specific months, let’s say on occupancy, it has like select specific months. I feel like the tab UI there is not good because it’s in a card. If it wasn’t in a card then it would be okay, but since it’s in a card we probably don’t want that — it feels like a nested thing… I would actually recommend you put that entire card, not just the show-specific-months, but the entire card, with variations of recommendations. Maybe use Fable 5.1. Because it doesn’t make sense to put it in. And if you just remove it, then we kind of go back to that poor UI. What I’m thinking is, to some degree, if it could expand. I don’t know, I’ll leave it open for you.”What the surface is today: DateRangePopover (month grain) renders quick-range rows, then a “Specific months…” expander, which opens MonthRangeFolder — the C3-2 shape Gera picked in the round-3 exploration: “the fields ARE the folder tabs”, two FolderTab tabs sitting ON the panel. Those folder tabs are the nesting he is now reacting to — the same silhouette RowSwitcherTabs uses to sit on a CARD edge, here sitting on a surface that is already a card.Two prior decisions the redesign must not quietly reverse: (1) round-2, Gera: “people aren’t gonna click months — focus on the quick options, and if they want specific they can click specific”, which is why the expander is a disclosure rather than two always-open value tabs at the top level; (2) 2026-07-15, Gera: “as soon as they choose, it closes. I don’t like that” — every pick is draft-only, Apply is the sole commit path. His “if it could expand” this pass points back toward the disclosure idea, not away from it. SCOPE: the whole card, not the folder. He asked explicitly for the entire popover to be re-explored with variations, and for Fable 5.1 to be used on it. DONE — round 4 is published, and it is waiting on his pick ( b88318608). Fable 5.1 generated five directions against the constraint set; three are built as WORKING mockups off one shared picker implementation, so they differ only in the structure under test rather than in incidental geometry. A — Second page (the disclosure navigates; the picker gets the whole popover; From/To are ordinary menu rows — recommended). B — Flat unfold (same fold, the enclosing card deleted, segmented From/To — the cheapest, and the direction that TESTS whether the objection was surface or structure). D — One endpoint at a time (a captioned two-step picker; the only direction with no tab-shaped structure anywhere). C (two panes, ~600px) is written up but not built — it needs the width constraint reopened, and its own bonus would make one row mean two things depending on state. E (four dropdowns) is written up as the bad idea that re-creates the nesting four times over.ANSWERED — Gera picked A. Built in #6775 (rendered page two): MonthRangeFolder is deleted, From/To are ordinary menu rows, and the picker sits directly on the popover. Review caught that the first pass deleted a 403-line test file with no replacement (only 3 of 29 cases were actually obsolete) and shipped an a11y downgrade justified by citing a file that says the opposite — both fixed. Round 2 then caught the focus guard being bypassed by effect ordering, AND that the test written for it could not fail (the suite stayed green with the fix mutated out); the rewrite is positive-controlled. MERGED. | ||||
| Merged | One window standard: trailing 30 / 60 / 90 days, never “last month” | Round 6 | #6773 · standup 08-31 | |
Founders’ standup · 2026-08-31 — the verdict, verbatim The bug that caused it. Sean: “this one also says 37 inquiries. But we know we’ve had 49 or something… down here it says it’s only counting 37.” Gera: “so this one is last month, and the other one is last 30 days, and not every month is 30 days… we should, like, standardize. When we look back a short amount of time, should we always look back one month, or should we always look back 30 days? Because when we have that inconsistency, those two days could cause a differentiation in numbers.” Sean: “So, basically today, plus 30 days back?” Gera: “31. In this case.”THE VERDICT. Sean: “let’s just apply the same methodology, so, like, let’s just do trailing 30, 60, and 90.” Gera: “Okay.” Sean: “And then we could do, like, an all-time, if you want to see, like, forever.” Scope settled by an independent Fable round (receipt fc3c06359, held under reversal, confidence high/high): only the SHORT rungs change. Retire 1m (a trailing CALENDAR month — 28–31 days depending on when you ask) and last-month from the pickers; introduce trailing 30/60/90 days; keep 6 months, 12 months, Year-to-date and All time for long-horizon trend surfaces. Gera framed the question as “when we look back a SHORT amount of time” and Sean’s all-time was additive (“and then we could do”), not a retirement of the long rungs. Wiping them would also break what the decision serves: monthly-bucketed trend charts (occupancy, NOI, rents) yield 1–3 points on a 90-day span, gutting the 12-month occupancy trend that is Sean’s own documented 2026-04-30 default.The work. PeriodKey in src/lib/data/period-filter.ts is months-only by an earlier Sean decision (2026-07: “remove day over day and week over week from anything, just only use months”) — this supersedes it for the short rungs. Day keys must join PeriodKey and TOKEN_TO_PERIOD_KEY (day tokens map to null today, so a day window cannot be recounted through ?period= at all), a 60d token has to be added to WindowToken, and every picker ladder re-pointed. MERGED in #6773 — 66,550 tests green. Trap Fable flagged: last-month also anchors closed-books financial labelling (closedMonthName / lastClosedMonthKey) — retiring the picker rung must not delete the closed-month accounting rule the financial surfaces depend on. | ||||
| Merged | Prospect info: surface the Preferences field where the card shows dashes | Round 6 | #6774 · decide b88316543 | |
Gera · 2026-09-01 — raised, waiting on his click His words: “in places like prospect info and stuff, where we have a dash instead of data, let’s actually have… I forgot what the field was called, but like more details or something like that. And then we just post them there. I don’t know if they mentioned which one specifically — like interested units and units shown.”The field is preferences, and it already exists. Federico, standup 08-31: “there was a bug where he was like, oh, I want something with natural light. And then Clara escalated that. I was like, no no no, that’s a preference. So we really need a preference field. So that’s what I’m introducing… that’s just something we can put on the guest card.” Verified in origin/main: ProspectInquiry.preferences is merged by merge-prospect-preferences.ts, written by update_prospect, gated per-property by preference-capture-flag.ts (fail-closed, default OFF) — and rendered nowhere on the prospect detail page. That is the whole gap.Why this needed a human. The card’s always-on dashes are Sean’s own 2026-08-30 rule: “in a situation where they don’t give you complete information I still think you should have all the fields… it should have just a dash here. That way you can see what other information needs to be collected.” Removing “Interested unit” and “Units shown” partially reverses it. The 08-31 transcript does NOT revoke it — the fields Sean said to drop there were price range and preferred lease term, which are not rows on this card at all, and he explicitly kept move-in / unit type / pets. Gera hedged on the same point (“I don’t know if they mentioned which one specifically”). An independent Fable round agreed on “keep everything, add Preferences” in both passes but escalated anyway (receipt f1580d033) rather than settle a commitment-to-a-person question. ANSWERED — Gera chose REMOVAL, against the recommendation. MERGED in #6774. One Preferences row now carries the stated wants plus both unit facts, each entry self-labelled so a bare join cannot print west-facing · 204 · 101. The card no longer advertises that nobody captured a unit yet — that cost is real, was argued, and is spent for these two rows only. Review caught an assertion that could never fail and an untested call site; both fixed, and the worst case (~1,300 chars = a 24-line wall, 885px of card) is bounded to 525px by a “+N more” clamp. | ||||
Round 709-02 — picker nits, the signal-rail de-colouring, and the renewals-progress compaction— | ||||
| Merged | Month picker: the whole header goes back, and every open returns to 30/60/90 | Round 7 | #6780 | |
Gera · 2026-09-02 — two nits on what #6775 shipped His words: “the specific month one, when I’m focused on it — I want to be able to click back by clicking the whole title, like the whole header” and “if I click off so it collapses and then I open it up again, the state should go naturally back to the 30 60 90.”BUILT (#6780). The header shipped as a 24px chevron with the title as INERT text beside it, so the obvious target — the words — was dead and ~250px of a row that looks like one control did nothing; it is one full-width button now. And the popover opened on page TWO whenever the committed range was custom, so a single custom Apply meant every later open landed on the picker and the quick ranges became the thing you navigate BACK to. The draft is still seeded from the committed range, so re-editing costs one click; reopening no longer strands the reader on the secondary surface. | ||||
| Merged | Renewals: Approve asks before it sends — the accidental-approve fix | Round 7 | #6784 | |
Gera · 2026-09-02 — the only DEFECT in the renewals batch His words: “if you click approve I feel like there should be a little bit of a ‘are you sure you want to approve this?’ Because I accidentally just approved it, which I guess is fine.”Why this control and no other. Approve is the page’s ONE irreversible outbound act — it starts the renewal workflow, prepares the offer in the PMS and hands it to the outreach ladder, and there is no unapprove anywhere on the page. Every other control there is a retry of something that already failed (Retry preparation, Resend letter), so a misclick costs nothing. It also sits inline under the phase rows, which is how it got hit. Shipped. The arsenal ConfirmDialog, neutral tone — this is a commitment, not a destruction. It names the tenant and the PMS rather than asking a bare “are you sure”, and closes on both outcomes because the failure already renders inline beside the button.One test trap worth recording. The old helper found the button by includes, and the confirm’s “Approve & send” is a PREFIX of the trigger’s “Approve & send the offer” — so either click could silently land on the other. Both are matched exactly now. 37/37 green; the positive control (restoring the one-click wiring) reddens 5. | ||||
| In review | Renewals: Phase 1/2/3 fold into ONE split card, and the chrome comes off | Round 7 | #6785 | |
Gera · 2026-09-02 — four items, one design pass His words: “for the renewals progress can we remove that Powered by Clara tag… for the phase one and phase two I feel like that should have a redesign given what the standard is for the other ones… we can remove that top-right Review and approve… I wonder, can we put the phase one, two, and three into one big card that’s like split? Just like a clean manner.”Five cards down the main column become three. The three phase cards become one “Renewal phases” card of CardSections — the work-order identity card’s idiom, which is the standard he was pointing at: one card, <h3> sections, whitespace instead of dividers, one heading idiom per card.The Clara byline’s SLOT goes too, not just the badge — it was the slot’s only caller on both the rail and its ghost, and a slot kept warm with no caller is how the mark comes back. Clara is untouched where she was doing work: the step labels, the approve dialog. Only ONE header-action variant was removed. Countersign and the two “Needs your review” links survive — they point at something a PM would otherwise hunt for. awaitingPmDecision is the one state where the page is already showing you the button.⚠️ One decision not asked for by name: the “Phase 1..n” eyebrows are gone. Inside a single card the reading order already IS the phase order, and an eyebrow above every <h3> is the second heading hierarchy the standard removes. Trivially reversible if he wants them back.Two guards were too weak and only mutation testing showed it. “One card” passed even when each phase re-acquired its own nested <Card> — twice, under two different readers. It now also asserts zero card frames inside the stack. 326 green across the renewal page, the progress guards and every CardSection consumer. | ||||
| Merged | Tenant signals: the category badges become plain labels | Round 7 | #6782 | |
Gera · 2026-09-02 — “it’s too much color” His words, on the tenant page: “the titles of like delinquency, cost, legal — they have like a badge to them, like a background. I want to just have them regular title color, probably like total rent, security deposit, resident portal, like that one. Just because it’s too much color.”Shipped. Those three he names are Field labels — small, uppercase, tracked, --color-text-label, no ground. The signal category now renders in that idiom, and the CATEGORY_TONES table is DELETED rather than neutralised (a colour table whose entries are all neutral is one edit from being a rainbow again).The red is not lost. The rail’s own rule is “colour says one thing here: urgency, carried by the severity dot”, and a red ground behind the WORD “Delinquency” was a second thing colour was saying. Delinquency arrives critical by construction, so its red dot is still one column left.Three pages, one edit — the card is mounted on tenants, prospects and unit detail. | ||||
| Answered — not a bug | A prospect “skipped tours” — is the funnel wrong? | Round 7 | SadieKate Ball · pers_eff31991 | |
Gera · 2026-09-02 — asked with a prospect link He pasted a prospect and said “it seems like they skipped tours.” They did, and the funnel is right to say so.The evidence. That prospect is stage applied, source pms, entitySource: external, carrying an AppFolio guestCardUuid + rentalApplicationGroupId — and applied eight minutes after the inquiry landed. She arrived from AppFolio as an already-applied guest card and never toured through PropFlow.The funnel refuses to invent the tour, deliberately. reachedFunnelStage’s own docblock: Tours is “deliberately NOT inferred from APPLIED / LEASE_SIGNED — AppFolio’s rental-application and guest-card syncs mint applicants who never toured, and counting them as tours would invent a tour rate out of nothing. The visible consequence is that Applications CAN exceed Tours; that is a real signal about how leads reach us, not a bug in the chart.” No change. | ||||
Round 809-02 — the collections head, plus two labelling nits Gera caught in the same pass— | ||||
| Superseded | Collections: the cohort funnel — falsified, superseded by the row below | Round 8 | #6804 · design | |
Gera · 2026-09-02 — the round-8 headline item His words: “the collections page could use a little bit of work, especially on what is owed and how old it is. It’s a very wordy card, it’s really hard to find what is what, and a lot of the text gets ellipsis because it’s too long. Are we trying to show too much information here? My guess is yes.” And the direction: “I kind of like the leasing prospect page where you have the funnel… I’m wondering if we should do that also for collections… maybe like a funnel chart, but with past due, judgment, applied.”Three defects, and they are separable. (1) The dashboard Collections card reads $66,275 while this page’s headline reads $112,581 — the delinquent worklist against every account balance. Both are right and neither surface says which it is. (2) The four band cards decompose the worklist, not the headline, so they visibly fail to add up to the card beside them; the explanation is in a sentence below the grid. (3) At five-across a card is ~190px, which is why LIFECYCLE_NODE_LABELS_SHORT exists at all — a second label table whose own docblock says it is there to stop “Assistance applied” and “Assistance approved” rendering identically.One finding constrains the answer. Aging cannot be a funnel: the bands are not nested subsets and 91+ is the largest, so an aging funnel draws backwards. The collections lifecycle is a real funnel — everything that reached Judgment also reached Filed, Served and Demand — so it takes the same reached-this-stage-or-beyond semantics reachedFunnelStage already uses on prospects. That is where past due / applied / judgment actually lands.NOT STARTED — five directions are drawn and waiting on a letter (collections-cards-round-8), from a minimum-delta pass through the lifecycle fork, the age×track matrix, a decomposing bar, and dropping the cards entirely. Recommendation on the page is B. PICKED AND BUILT (#6804). Four design rounds landed on a stacked cohort funnel: one month’s past-due money followed across four reads, bars split by the track it was on at that date, and selecting a track RESCALES the plot onto it rather than dimming it against the total. Three invariants carry tests and positive controls — step k reads band k (never balance, which rises as new rent is charged); a missing balance row is UNKNOWN, never zero (a gap read as zero invents a payment, in the flattering direction); and a per-track delta is NET movement, never “paid”. Writing the tests caught two real bugs: date-fns’ local-time month helpers shifted the whole cohort a month, and an invalid stage literal made a mapping test pass on a branch that never fired. ⛔ PARKED — the premise is falsified by real data, and only a prod read caught it. The module claims reading band k at step k follows the same dollars. It does not: the aged bands are a whole-ledger re-aging at every read, not a conveyor, so a tenant’s new monthly rent lands in 1–30 and the series stops describing one cohort. Read-only against prod, one Willows account with zero unknowns — where the model predicts a monotonic fall — reports [750, 1545, 0, 3640] while its actual debt grows 750 → 6,730. Camellia’s $48,348 collapses to $20 by step 1, because “only 1–30 money” selects ordinary in-cycle lateness rather than delinquency. Every unit test passed; each one encoded the assumption under test. Salvageable: the scope chain, the paid/reappeared/net semantics, absence-is-not-zero, the replayed track axis and the whole component. The open question for Gera: re-cut the cohort on ACCOUNTS that crossed the 31+/$50 floor, plotted on their 31+ balance — real and checkable, but their debt can grow, so it stops being a funnel and “the only exit is payment” goes with it. That changes the shape he picked. | ||||
| Merged | Collections: the aging cards go quiet, and the breakdown opens in a panel | Round 8 | #6806 | |
Gera · 2026-09-02 — the first slice, merged on its own His words on the shipped page: “a very wordy card, it’s really hard to find what is what, and a lot of the text gets ellipsis because it’s too long.” The cards went quiet — an eyebrow, a figure, a share bar — and the composition moved to an expanded panel at the section’s full width, which is where the ellipsis fix actually is: at ~190px a status label shared its track with a value and a percentage, so “Assistance applied” and “Assistance approved” truncated to the SAME ellipsis.⚠️ It merged mid-flight, and that is worth recording. Fede squash-merged #6806 at 23:21Z while the funnel work was still being pushed to the same branch. A squash closes the PR and it stops tracking the branch, so six later commits kept landing on a branch nobody was reviewing and no CI was running. They are rebased onto main as #6825. The tell was the PR head not moving across three pushes; it was read as webhook lag for over an hour, because mergeStateStatus reports UNKNOWN on a merged PR rather than saying it merged. | ||||
| In review | Collections: the canonical funnel, extracted — and the strip that hosts it | Round 8 | #6825 | |
Gera · 2026-09-02 — eight rounds on the live page It started as “a very wordy card, it’s really hard to find what is what, and a lot of the text gets ellipsis because it’s too long.” It ended at “I want the exact same experience here… essentially use exactly the funnel chart that we have over there. Make the standard canonical funnel chart and then add a layer on top of it.”So the funnel is one implementation now. FunnelBarChart is /leasing/prospects’ chart with the leasing vocabulary lifted out — extracted, not copied, so the two pages cannot drift into two funnels that look almost the same. Collections is the layer: age bands instead of pipeline stages, dollars instead of counts, and chips that re-value the bars.The resting row is one strip, not five cards — the same MetricStripCell + chrome pair prospects uses, five columns at every width, expanding in place through CardExpandGroup so row-mates become tabs.Three things it refuses to say. The gap pills never say “paid”: these are four age bands from one snapshot, and production settles it — at Willows, one account, zero unknowns, the bands read [750, 1545, 0, 3640] while that account’s debt grew 750 → 6,730. The caption that says so is behind an Info glyph rather than deleted. And the no-branch track is “Nothing yet”, not “Past due”, because past_due is one lifecycle node with its own row in the table directly below the chart.Six defects found on the way, each with how it was caught: a flicker that was a shape change in the animation driver (and a first regression test that passed on the broken code); SlotMachineNumber rendering every digit twice so textContent read “4422” for 42; an import cycle whose only symptom was six test files failing to load; an ACCOUNTS column that shipped as em-dashes with every test green; a brand test measuring tighter than its own instrument; and a disclosure cleared on one of its three writers. | ||||
| Not started | Month picker: the reset to 30/60/90 happens on OPEN, so you watch it flick back | Round 8 | DateRangePopover.tsx | |
Gera · 2026-09-02 — a follow-on to what #6780 shipped, not a regression in it His words: “whenever I click the date range calendar, and then specific months, and then I click out — and then I open up the calendar date range again, it flickers back to the last little page of it. I feel like it should do that when it’s clicked out, so not when I click in.”He is describing the exact mechanism. The popover BODY is conditionally rendered on open, but monthPage lives on the parent, which stays mounted — so it is still 2 while the popover is shut. Reopening paints page two on the first frame; the re-seed effect (useEffect(…, [open])) then sets it to 1 and the second frame replaces it. That is the flick.The fix is to move the reset to the CLOSE edge, so the next open paints page one from its first frame and there is nothing to see. #6780 chose the right behaviour — every open returns to the quick ranges — and put it on the wrong edge. | ||||
| Not started | Leasing pipeline says “30 days” four times over a card that already says it once | Round 8 | LeasingSection.tsx | |
Gera · 2026-09-02 — a labelling nit, caught in the same pass His words: “on the leasing pipeline it says prospect 30 days, tours 30 days, applications 30 days. We don’t need to say that, because on the card it already says 30 days at the bottom.”The four stages labels each append the window suffix ${w} — Prospects 30 days, Tours 30 days, Applications 30 days, Signed 30 days — while the card carries the window once already. The suffix comes off the rows.⚠️ It is not simply deletable, and the comment above it says why: the suffix exists so a closed-month window reads Prospects (June 2026) rather than an undated count. So the window has to keep being NAMED once on the card for every period value, not just for the 30-day default — otherwise this fix trades four redundant labels for one ambiguous card. | ||||
In flightcurrent work— | ||||
| Shipped | Concession “restrictions apply”: should VOICE carry the hedge? | In flight | b87934913 | |
You · Waiting on Gera — everything else is moving Not “never shipped” — it already ships on SMS, email and Telegram. Only voice omits it, deliberately, because voice quotes the special verbatim. So the question is where the rule lives, not whether to build it. RESOLVED 08-29: Gera (b87934913): “I don't wanna add that in the voice, only text.” That is exactly current behavior — the hedge ships on SMS/email/Telegram, voice omits it. No change. | ||||
| Shipped | PMS Inspector: drop its local property dropdown? | In flight | b87934916 | |
You · Waiting on Gera — everything else is moving All three original claims verify. The cost the note omitted: consuming the nav picker fully means opening a dev tool re-scopes the whole workspace and loses the inspector’s sandbox-first default. RESOLVED 08-29: Gera (b87934916): remove the local dropdown, use the global nav picker — and it is BUILT: PmsInspectorClient consumes the TopBar picker, the local select and its race-guard module are gone (PmsInspectorClient.nav-scope.test.tsx cites this decision verbatim). | ||||
| Shipped | A reported number was wrong | In flight | Gera’s countersign ruling found this | |
#6476 · Merged — eleven items agent-metrics counted any renewal at status === 'renewed' as signed — no signature gate at all — and published it as renewalOffersSigned. Fixed by deleting the fourth independent implementation, not adding a fourth gate. | ||||
| Shipped | The page-settings slot, across all six policy pages | In flight | his decision | |
#6476 · Merged — eleven items Includes moving the maintenance control off the nav hub onto the work-order list. The date window deliberately stays in the toolbar, per Gera’s own 08-27 call. | ||||
| Shipped | Side nav gets a real phone tier | In flight | found in this pass | |
#6476 · Merged — eleven items Every workspace page was paying 56px of a 390px screen. The rail only ever collapsed, never hid. | ||||
| Shipped | The conversation Details panel names the person | In flight | 08-28 | |
#6476 · Merged — eleven items His premise checked out and was worse: below 768px the name appeared nowhere — breadcrumb hidden md:flex, <h1> sr-only at every width. A prospect is never labelled a tenant. | ||||
| Shipped | Last 60 days is back as an option | In flight | 08-28 | |
#6476 · Merged — eleven items Dropped as a default on 08-26 for matching no quick range; adding it as a row removes that objection. Day family, not trailing-2-months — two calendar months is 59 days in Jan+Feb and 62 in Jul+Aug. | ||||
| Shipped | Six more | In flight | #6476 | |
#6476 · Merged — eleven items PMS Inspector’s permanently-false “Last scraped”; the vendor hint no longer hover-only; collections “Last paid”; the daily work-order job’s no-op guard; the shared chart restyle; the quiet-inset panels made one idiom. | ||||
| Withdrawn | “Make all of them lighter” — not executable in dark mode | In flight | Gera: “go find out” | |
#6476 · Merged — eleven items Muted text on the dark quiet ground is 4.5016:1 against a 4.5 floor. One hex step lighter is 4.4408 and fails AA — on a dark ground, lighter moves the surface toward the ink. Pinned by a guard whose mutation control makes exactly that edit and watches it redden, so a future “lighten per Gera” cannot ship it silently. | ||||
| Shipped | Dashboard “This week” restyle | In flight | #6536 | |
Next · Answered, queued to build Keep two cards; give the weekly counters the owner email’s vocabulary. SHIPPED in #6536. | ||||
| Shipped | Collections: drop certified_funds_only everywhere | In flight | #6476 / #6536 | |
| Shipped | Follow-ups fold into the vertical stepper | In flight | his own brief, verbatim | |
Next · Answered, queued to build SHIPPED. The fold landed in #6476; the dismissed cycle then got its own nested dot chain, a trailing caret and a fixed label column in #6520 (design of record: artifact). Pending touches greyed below the line like “Tour requested” already is; a superseded funnel collapses with “dismissed due to activity”; always show the next funnel; a terminal “dismissed — no follow-up criteria met” when there is none. The stepper needs collapsible groups, default collapsed. | ||||
| Shipped | The button-vs-text token split | In flight | #6536 | |
Next · Answered, queued to build Eleven files including the audio player. Doing four of them would put two greys side by side on the same page — the exact defect he complained about — so it needs one atomic change. SHIPPED in #6536, as one commit: --color-surface-soft (text/inset grounds) lightened #F2F2F7 → #F5F5F9; --color-surface-control (button grounds — the audio player among them) held at the original value. Contrast recomputed and checked in both themes, not assumed. | ||||
| Shipped | Work-order (and any entity) conversation links should open the PERSON’S whole unified thread, highlighted — not a one-off scoped view | In flight | #6632 · #6665 · decide b88097742 | |
New · Raised live, 2026-08-29 — not yet scoped Today, a work order’s “conversation” link (e.g. /maintenance/work-orders/CAM-2479/conversation?with=tenant) opens a view scoped to just that one work order’s thread. Gera wants that changed so the link instead takes you to that person’s own unified conversation page — e.g. /tenants/<id>/conversation — showing their entire history (every call, text and email, across every work order or topic), with the specific exchange that pertains to the work order in question visually highlighted and scrolled into view, rather than being the only thing shown. He wants the same hierarchy to apply from any entity — tenant, prospect, vendor, handyman — not just work orders.Open design question, his own, unresolved: what exactly counts as the “start and end” of the sub-conversation to highlight for a given work order — the boundary of “the exchange pertaining to this work order” needs a definition before it can be encoded (e.g. as a highlight-range query param) and passed through the URL to drive the highlighting. Separate naming problem he flagged: the /conversation URL segment and page title imply “a conversation” — one bounded exchange — when the actual object is a unified thread made of many conversations (calls, texts, emails, potentially spanning topics and work orders over time). He thinks that naming has been quietly misleading people, himself included. He wants the URL/title reconsidered to say “one unified thread across everything” — but was explicit he has no good name yet (“I’m just gonna make up names, but don’t use these because these are bad — unification conversations or tenants conversations”), so the rename itself is an open question, not a decision — do not build toward either of those two names.Why full-thread-plus-highlight, not a hard filter: if the app’s conversation chunking/classification logic ever misclassifies a message, that message could be silently dropped from a scoped/chunked view. Showing the full unified thread with the relevant part just highlighted avoids losing anything to a bad chunk boundary. Gera called this “one of the harder items” — multiple plausible implementation pathways, not an obvious single right architecture — and said if Fable 5 is available as a model choice, whoever picks this up should consider using it to explore the space rather than building the first idea. He also said he’s happy to answer clarifying/decision questions himself once someone starts scoping this — loop him in on the boundary-definition question above rather than guessing. 08-29 status: mechanics scoped (idle-gap segmenter #6534 + coalesceHighlightRuns #6537 + Message.sourceConversationId cover everything but the boundary rule); the boundary-definition question Gera asked to answer is drafted and QUEUED behind the session's 3-open-block cap — it raises the moment one of b88062292/b88062434/b88062780 resolves. CLOSED 08-30 (verified in origin/main, not assumed): Still with Gera as b88064049 (the boundary definition he explicitly reserved — NOT auto-resolved despite the pre-auth relay; the canonical answer store shows it open). Everything else on this page is terminal. ANSWERED 08-30 (b88064049, by Gera): contiguous span — first to last WO-pertaining message, extended to the enclosing 18-hour idle-gap segment (#6534), one solid band. Build underway on branch gera/wo-links-unified-thread: WO links re-point to the person's entity conversation route with a highlight param; old scoped URLs keep resolving. SHIPPED 08-31 (#6665 merged 01:08Z): b88064049 executed — the regarding lens bands one contiguous span (first→last pertaining message bridged unconditionally; extension bounded by the segmenter's own cards, all four boundary rungs by construction) and the legend counts the painted band, not the tagged set. Two-round bot review 🟢; non-gating advisories dispositioned on the PR. | ||||
Slack backlogrecovered asks— | ||||
| Shipped | Kill the big banner | Slack backlog | RenewalDetailClient .statusheader.test.tsx | |
Renewals · The renewal detail page — five asks, re-derived against main Eats too much space; keep the tenant name and unit number up top. — Fede / Sean. Done 2026-08-25 in the detail-page standardization: the gradient hero (radial-gradient card, ping-pulsing pill, AuroraText headline) came out on Gera’s own call and the page moved onto DetailPage’s v2 structured header. | ||||
| Shipped | Cut the AppFolio-prep clutter | Slack backlog | shipped in code | |
Renewals · The renewal detail page — five asks, re-derived against main “You don’t need to know Clara opened AppFolio” — just that it got sent. — Fede. Confirmed still live: Phase 2 emits three rows per run — “Clara opened AppFolio to prepare the renewal”, “Clara finished preparing…” and “Waiting for AppFolio to confirm the offer” — for the one fact Fede wanted. RESOLVED 08-29: Built: the phase section now emits exactly one honest fact (af-sent / af-prepared / waiting), with Fede's quote in the source (RenewalDetailClient.tsx:594) and tests pinning it (renewal-detail-timeline.test.ts). | ||||
| Shipped | Latest outcome / resolution at the TOP | Slack backlog | #6463 · Round 5 | |
Renewals · The renewal detail page — five asks, re-derived against main You should not scroll to the bottom to find out what happened. — Fede + Gera. Half done: the status word now rides the v2 header as a static badge, and an action link jumps to #phase-resolution. But Resolution is still Phase 4 of 4, so the actual “what happened” is at the bottom of a four-phase rail. The header ask was met; the scrolling ask was not. CLOSED 08-30 (verified in origin/main, not assumed): Resolution hoists as “Latest” and now LEADS the page (Latest, then Phase 1..n — RenewalDetailClient:968), which retires the scrolling half; the header badge shipped earlier. Round 5's renewals-standardization wave carried the rest. | ||||
| Shipped | Rename the “countersign” status | Slack backlog | #6463 | |
Renewals · The renewal detail page — five asks, re-derived against main It does not say who the holdup is. Wanted: “Tenant signed ✅” plus a step reading “Pending PM signature/review”. — Sean + Gera. Half done: the label is “Ready to countersign”, which implies the PM is the holdup but never says the tenant signed. Proposed: Tenant signed — pending your countersignature, changed in the shared derivation so the list pill moves with it. CLOSED 08-30 (verified in origin/main, not assumed): The label now reads “Tenant signed — pending your countersignature” (renewal-status-display.ts, reworded 08-19 and again 08-28) — exactly the who-is-the-holdup phrasing Sean and Gera asked for. | ||||
| Withdrawn | That pending state should be YELLOW, not purple | Slack backlog | renewal-status- display.ts L157 | |
Renewals · The renewal detail page — five asks, re-derived against main Draw attention to the blocker. — Sean. Someone considered this and overruled it, with the reasoning committed beside the token: “Purple, not warning — this row needs the PM to act (countersign), but it’s a positive action-ready state, not a problem; yellow made it blend in with Flagged/Escalated/Missing email.” Those three states are all already warning, so Sean’s fix makes the blocker louder at the cost of making it indistinguishable. Not flipping this silently — it is on the Decisions tab. | ||||
| Shipped | Open question nobody answered | Slack backlog | b87932311 · fixed 08-28 | |
Renewals · The renewal detail page — five asks, re-derived against main If a tenant signed but the PM has not countersigned, does it count as a renewal in the stats? AppFolio’s API cannot tell — it reads “not fully executed.” RESOLVED 08-29: Gera (b87932311): do not count until the PM countersigns. The code agrees: signedRenewed excludes awaiting_countersign (signedThisCycle: false), and publishing the raw RENEWED bucket is documented as the exact 08-28-fixed defect (renewal-view.ts). | ||||
| Shipped | “AppFolio has all 12 months data, we need to go all the way back” | Slack backlog | #5997/#6036/#6042 | |
Dashboard · Six asks, two shipped Took five rounds of pushback against a “we can’t” answer — “I don’t think we should have a time to live” — and landed. Gera confirmed: “this actually looks exactly how I wanted it to.” Left open in that thread: Yale’s stored daily NOI is byte-for-byte identical to its rent — an income number wearing an NOI label. | ||||
| Shipped | “Let’s remove the Portfolio Metrics card” | Slack backlog | #6067 | |
Dashboard · Six asks, two shipped Removed from the dashboard, component kept — his explicit instruction. | ||||
| Shipped | “The bar needs work too, it feels like slop” | Slack backlog | #6476 | |
Dashboard · Six asks, two shipped Already diagnosed and never acted on: thin low-contrast styling, a preserveAspectRatio="none" stretch that distorts the curve, fragile HTML-overlay axis labels, and two clashing palettes inside one shared chart engine. One restyle lifts every chart in the app. RESOLVED 08-29: The shared chart restyle shipped in #6476 (its own commit message names it; the tracker's #6476 row listed it all along — this row was the stale duplicate). | ||||
| Shipped | The “This week / 0 Move-ins” tile should have the email’s feel | Slack backlog | #6536 | |
Dashboard · Six asks, two shipped Modelled on the owner email’s “Occupancy · as of Jul 20 … Move-ins this week 0 … Trending occupancy”. A design question was asked back — fold it into OccupancyCard, or make it a “what changed this week” pill panel? — and never answered. RESOLVED 08-29: Shipped in #6536 — the weekly counters carry the owner email's vocabulary (WeeklySnapshotCard: “Move-ins this week / Move-outs this week / Applications”, Trending occupancy). | ||||
| Shipped | “The Maintenance is very poor too” | Slack backlog | catalog.ts | |
Dashboard · Six asks, two shipped Reality check from the same meeting: Camellia has almost no middle-to-end maintenance data, so the metrics should stay basic — open vs completed over a period — rather than aspirational. RESOLVED 08-29: The dashboard maintenance card is now exactly the basic shape this ask settled on: an ops ACTIVITY card — created / resolved / self-serve over a 30d window — in the rebuilt card catalog. | ||||
| Shipped | “Collections needs a section” | Slack backlog | decide b88062292 | |
Dashboard · Six asks, two shipped “I’ll share more context on this later” — never did. RESOLVED 08-29: Raised as b88062292: ship a basic collections card (past-due / in outreach / paid-after-outreach) now, or wait for the context Gera said he'd share. 08-29 update: BUILD STARTED per the b88062292 resolution (auto-resolved on Gera's relayed pre-authorization): a basic Collections card — total past due + tenants past due from the live dashboard-stats metrics — is being built into the card catalog on branch gera/dashboard-collections-card. | ||||
| Shipped | “Rent Income” should say what it represents | Slack backlog | #5473 | |
Dashboard · Six asks, two shipped “Why do they differ?” — $130,497 on the dashboard vs $123,383 on the property page. SHIPPED — verified against a prior claim of "no evidence of shipping": #5473 (2026-08-06) both reconciled the two figures onto one producer and shipped the label change. Older than this tracker's own claim that it hadn't. | ||||
| Shipped | The collections Stage tooltip SHOUTS | Slack backlog | 55a85d861cd | |
Small · Cheap, agreed, still open text-transform: uppercase on the whole “derived from the ledger…” explainer. Fede raised it, it was filed, and the filing said in as many words “didn’t assign it to anyone.” SHIPPED — commit 55a85d861cd (2026-08-24, "The Stage panel's explainers stop shouting") fixed it before this tracker was even written. | ||||
| Shipped | “Last scraped” on the PMS Inspector reads “Never”, for every property, forever | Slack backlog | #6476 | |
Small · Cheap, agreed, still open OnsitePricingConfig.lastScrapedAt was declared and never written by anything. SHIPPED in #6476 — the dead field was deleted outright rather than wired up, and the inspector re-pointed at the field the scrape path actually writes. | ||||
| Shipped | The PMS Inspector’s second property dropdown | Slack backlog | b87934916 | |
Small · Cheap, agreed, still open It competes with the nav bar’s. Removing it also deletes a whole module that exists only to guard the race the local dropdown creates. RESOLVED 08-29: Same resolution as the In-flight row above: Gera chose the nav picker, the local dropdown and its race-guard module were deleted, pinned by PmsInspectorClient.nav-scope.test.tsx. | ||||
| Shipped | Two vendor-page follow-ups | Slack backlog | #6320 / #6535 | |
Small · Cheap, agreed, still open The “match them in AppFolio first” hint is hover-only — invisible on a phone and to a screen reader. And a vendor that is matched but never synced shows no timestamp at all, where the resident page says “Not synced yet.” BOTH SHIPPED. The timestamp half landed 2026-08-26 in #6320. The touch-reachability half — closed by teaching Tooltip tap-to-toggle, which the component had not yet adopted when this was filed — shipped in #6535. | ||||
| Shipped | Three collections copy changes | Slack backlog | #6476 | |
Small · Cheap, agreed, still open “paid” → “last paid”; kill “certified funds only” as a status; the confusing date field. ALL THREE SHIPPED — the first two in #6476; the third ("the confusing date field") was the "Outreach ended" column, removed in 6d663477ae. | ||||
| Shipped | “Restrictions apply” on the concession | Slack backlog | b87934913 | |
Small · Cheap, agreed, still open Sean asked 08-05; Gera re-proposed it 08-20 after reverting an over-verbose version — “it should be a prompt update, not a product change.” Neither shipped. RESOLVED 08-29: Same resolution as the In-flight row above: text carries the hedge (it already ships on SMS/email/Telegram), voice deliberately does not — Gera: “only text.” | ||||
| Shipped | The audio player misalignment | Slack backlog | db06b6a6f4 | |
Small · Cheap, agreed, still open Fede: “was the audio player recently changed? It looks mis aligned.” CONFIRMED fixed, not absorbed — verified against the current component: the diagnosed defect was the pre-db06b6a6f4 shape and does not reproduce at HEAD. | ||||
| Shipped | The nightly refresh fakes activity | Slack backlog | #6476 | |
Small · Cheap, agreed, still open It nudges the Updated time on every work order it touches, so an automatic sync looks like real activity on the maintenance list. SHIPPED in #6476 — the daily wo_enrichment sweep stopped nudging updatedAt on semantic no-ops. | ||||
| Shipped | dateWindow=14 vs the 60-day window | Slack backlog | b88046872answered → Round 5 | |
Small · Cheap, agreed, still open The entire funnel discussion was reasoned on 60 days; the link shared was 14. Asked directly whether 14 was deliberate — never answered. The default may still not match what the team reasoned about. RESOLVED 08-29 (b88046872): Gera ruled keep 180 — his 08-28 "last six months" default stands and 60 days stays a one-click dropdown option. The 14-day premise of the original 08-19 ask was stale; current code already matches, so nothing shipped. ANSWERED / SUPERSEDED 08-29 (Round 5): Gera then dictated the default directly — the pipeline opens on 60 days on login. That overrides the “keep 180” ruling above. This row is closed; the live row is “The prospect pipeline’s default date window becomes 60 days” in Round 5. Do not work this row. | ||||
| Shipped | Sean’s vendor data model | Slack backlog | ADR-0033 · b88062434 | |
Small · Cheap, agreed, still open Company → nested employee contacts → one flagged primary dispatch contact. Fede: “love that.” That is the last anyone spoke of it. RESOLVED 08-29: Raised as b88062434: ADR-0033 already shipped company → contacts → dispatcher role; the open delta is only whether a single primary-dispatch FLAG is wanted. 08-29 update: CLOSED per the b88062434 resolution (auto-resolved on Gera's relayed pre-authorization): the model shipped structurally with ADR-0033 (VendorCompany → VendorMembership with dispatcher role, used by dispatch intake); a primary-dispatch flag waits until a company actually has several dispatchers. | ||||
| No items match your filters | ||||
| Status | PR | Title | When |
|---|---|---|---|
| Merged | #6537 | Round 4: the conversation reader — a linked name, one band, a forward card, and a way ou… | Aug 29 |
| Merged | #6534 | feat(conversations): a conversation is a time frame, not a calendar day | Aug 29 |
| Merged | #6536 | Split the grey by ROLE (lighter text ground, media buttons kept), reverse the info-card… | Aug 29 |
| Merged | #6535 | The vendor "not linked" hint reaches a phone | Aug 29 |
| Merged | #6533 | A cancelled tour gets its own rail step, and every page setting gets its loading ghost | Aug 29 |
| Merged | #6532 | A skipped touch says "Unsent text", not "Text not sent" | Aug 29 |
| Merged | #6530 | Follow-up rail: the label says which send it is, and the nested chain connects | Aug 29 |
| Merged | #6520 | The dismissed follow-up cycle opens into a chain, and the row says it opens | Aug 29 |
| Merged | #6476 | Backlog cleanup: eight verified-open items, one PR | Aug 28 |
| Merged | #6463 | Round 3 UI: stranded-tour join, prospects toolbar, renewal rail, narrow viewports | Aug 28 |
| Merged | #6462 | Tenant conversations: band the thread you clicked inside the merged read | Aug 28 |
| Merged | #6450 | fix(ci): drop stale link-color allowlist entry for WorkOrderDetailClient (unbreak main) | Aug 28 |
| Merged | #6448 | Prospects list, tenant/WO cards, conversations and the collapse standard | Aug 28 |
| Merged | #6421 | Prospect review round 2: stepper polish, card consolidation, app-wide time standard | Aug 28 |
| Merged | #5473 | Rent Income: one producer + labeled basis on dashboard and property page | Aug 6 |
#alerts.ui-evidence --base-url <preview> unblocks the visual-diff gate.