---
name: Freshness before surface
description: Never surface "overdue" or "needs action" items to Joseph based on stale ledger data without first re-checking the underlying source-of-truth channels.
type: feedback
last_updated: 2026-04-30
originSessionId: 6250c599-625a-4c16-9ba6-368c0a7715ed
---
## The lesson (2026-04-30 incident)

QB's 6:45 AM brief flagged 2 "overdue commitments" to Joseph:
- **Ryan Devine** — Meet invite for Mon May 4 ≥2pm MT (allegedly overdue from 4/29)
- **Jamie Kendall** — Boulder County PM referral (allegedly overdue from 4/29)

**Both were already fulfilled before QB's brief generated:**

| Commitment | Captured | Actual fulfillment | QB still flagged it |
|---|---|---|---|
| Devine Meet | 4/29 14:49:14Z | Calendar event `g73k496uc8b3onb3s96tan0f7c` created **4/29 14:49:42Z** (28 sec later) | ✗ |
| Jamie Boulder PM | 4/28 19:55Z | Gmail `19dd9b66f9467cf1` sent **4/29 14:48:24Z** (handing her off to Adam's Boulder team) | ✗ |

Joseph correctly called this out as a lazy mistake. Plus: Jamie isn't even GC-relevant anymore (Boulder only after her GC purchase fell through) — she shouldn't be in the GC commitment-tracker at all.

## Root causes

1. **commitment-tracker Step 5 only checks Gmail** — Calendar event creation never triggers fulfillment. Devine's invite was a Calendar event, so the ledger stayed `overdue` forever.

2. **QB reads commitments.jsonl directly without re-running fulfillment detection.** QB at 6:45 saw `status: overdue` on both records and surfaced them. The actual fulfillment evidence existed in Calendar (Devine) and Gmail (Jamie) but the ledger hadn't been updated.

3. **No scope-out filter.** Even if Jamie's commitment had been re-checked, the system has no concept of "this lead is no longer relevant to GC." Joseph addressed her by handing off to Adam's Boulder team — but the commitment-tracker would still flag her on every subsequent miss.

## Permanent fixes (wired 2026-04-30)

### commitment-tracker Step 5 — multi-channel fulfillment scan

`~/.claude/scheduled-tasks/commitment-tracker/SKILL.md` Step 5 now has FOUR sub-steps:
- **5a Gmail** (existing) — search sent for matching message to recipient
- **5b Calendar (NEW)** — if promised_action mentions invite/Meet/schedule, query Google Calendar for events created since `captured_at` with recipient in attendees → mark fulfilled with `calendar:<event-id>`
- **5c HS Meeting engagements (NEW)** — query recipient's HS contact for meeting engagements created since `captured_at`
- **5d Scope-out (NEW)** — auto-mark `dropped` if recipient HS contact has lifecyclestage=closedlost, lead_status=closed-out, scope notes indicating out-of-GC, or `ScopedOut:` token in lead_source_notes. Never draft follow-up for dropped commitments.

### QB Step 1a — freshness gate

`~/.claude/scheduled-tasks/qb-quarterback/SKILL.md` Step 1a (new): before surfacing ANY commitment as overdue, run a quick re-scan on Calendar + Gmail + HS scope. ONLY surviving commitments are eligible to surface. **Hard rule: never list a commitment as overdue using stale ledger data alone.**

### General principle (applies to ALL ledgers + watchdogs)

Any system that surfaces "X is overdue / blocked / needs Joseph's action" MUST re-validate against source-of-truth channels before display:
- Calendar (for scheduling commitments)
- Gmail sent folder (for promised emails / replies)
- HubSpot engagements (for meetings, notes, deal-stage changes)
- HubSpot contact properties (for lifecyclestage, lead_status, scope flags)

Surfacing stale data costs trust. Joseph only has to see this kind of mistake once before he stops trusting the morning brief at all.

## Full sweep — every watchdog audited (2026-04-30 same-day)

After the commitment-tracker + QB patches, swept every other ledger-surfacing skill. Each got the appropriate freshness gate added:

### `stalled-deal-watchdog` (PATCHED Step 3 + 5)
- **Already had**: `last_touch_real` computed from KG + Gmail sent/inbound + HS engagement + transcript mentions
- **Added Step 3 (Calendar)**: query Calendar for events in next 30 days involving any deal-contact → `created` timestamp counts as a touch
- **Added Step 3a (future-meeting suppressor)**: if deal-contact has any Calendar event in next 14 days, deal is NOT stalled — `next_touch_scheduled` covers the gap, skip drafting
- **Added Step 5 mandatory pre-draft check**: read HS contact `lifecyclestage` + `lead_status` + `relationship_to_the_property`; skip draft if scope-out signal

### `referral-watchdog` (PATCHED Step 3.5)
- **Already had**: bounce-ledger check, DNC enforce, 180-day cooldown, friction flag
- **Added Step 3.5 (active-owner freshness gate)**: per-owner live HS check before drafting. Skip if:
  - lifecyclestage ≠ `customer`
  - relationship_to_the_property contains "Former" / "Offboarded" / "Churned"
  - Recent inbound (30d) contains "cancel" / "terminate" / "switching" / "unhappy" / "complaint" / "lawsuit"
  - Linked deal flipped back to `closedlost`
- **Cost**: ~10 chrome_bridge calls per Sunday fire. Worth it — drafting a referral ask to a churned owner is a high-stakes embarrassment.

### `smartlead-bounce-handler` (PATCHED Step 8a)
- **Already had**: idempotency via Gmail label, helper-level dedupe
- **Added Step 8a (pre-queue freshness check)**: before queueing a SmartLead-removal action:
  - If HS contact email changed since bounce date → skip the SL-removal (email was likely corrected; let re-bounce signal failure)
  - If lead already has a prior `applied` removal action → skip (idempotent)
  - Same `(lead_id, bounce_date)` already queued → skip
- HS proposal still queues either way (bounce in lead_source_notes is permanent record).

### `close-to-onboarding-handoff` (PATCHED Step 2a)
- **Risk**: KG drift could make a deal appear closedwon when HS shows otherwise; or Joseph could have already manually sent the handoff
- **Added Step 2a (freshness re-validation)**:
  - Live HS deal-stage check (must be `closedwon`); if not, log `kg_drift_detected` + skip
  - Gmail prior-handoff detection: search `from:joseph.bowens@skyrun.com to:rachel.scott@skyrun.com subject:Handoff <owner_lastname> after:<close_date>`. If found, log `prior_handoff_detected` + retroactively log to handoff-ledger so referral-watchdog 90-day clock works.

### `deal-postmortem-capture` (PATCHED Step 2a)
- **Risk**: KG could lag HS or be wrong; postmortem prompts are high-friction to Joseph
- **Added Step 2a (live HS stage re-validation)**: query HS for current pipeline stage; if NOT in `closedwon` or `closedlost` (e.g., flipped back to in-progress), skip prompt + write back correct stage to KG.

### `live-ea` (NO PATCH NEEDED — fresh by design)
- Reads NEW Gmail every 20 min and processes fresh
- No ledger surfacing — doesn't read commitments.jsonl, handoffs ledger, or postmortem ledger
- Tier 2 HS live check already verifies current-owner status before drafting (Froelich-incident hardened)
- 20-min max staleness from PWA rebuild; won't operate on stale state >6h
- Audit conclusion: live-ea was already aligned with the freshness principle

## Standing rule (HARDWIRED 2026-04-30)

**Never surface an "overdue" or "blocked" item to Joseph without first re-validating against the SoT channel that would prove it's already done.** A 5-second Calendar/Gmail/HS check beats a stale-ledger false alarm every time.

Every new watchdog or surfacing-skill MUST:
1. Identify which channel(s) would PROVE the surfaced item is already done
2. Re-check that channel before surfacing
3. Auto-resolve the ledger entry if proof is found
4. Add a `_resolution_note` explaining the resolution path

If a skill doesn't have this pattern, it's a bug that needs to be fixed before deployment.

## Joseph's verbatim feedback (the trigger)

> "How did that get missed? Implement a process that insures this never happens again. Why is Jamie being flagged to me? She has no more value to me right now as she no longer has a property in GC. only boulder county and I already addressed that. How did you miss that and surface either of these commitments? Implement something that makes you smarter and more diligent to this kind of situation and prevents lazy mistakes like this in the future."

## Standing rule (HARDWIRED 2026-04-30)

**Never surface an "overdue" or "blocked" item to Joseph without first re-validating against the SoT channel that would prove it's already done.** A 5-second Calendar/Gmail/HS check beats a stale-ledger false alarm every time.

## EXTENSION 2026-05-03 — covers ad-hoc agent-generated content (THIRD strike on Devine)

**The 4/30 fix only patched automated watchdogs. It did NOT prevent the same failure shape from recurring in agent-written ad-hoc content like pre-briefs, summaries, or recommendation lists.** Joseph caught the SAME Devine miss on the SAME deal in a 5/3 pre-brief I wrote — calendar invite was already sent 4/29 14:49Z (event id `g73k496uc8b3onb3s96tan0f7c`), but I copied "calendar invite owed" forward from a 4-day-old memory file. The memory's own system-reminder explicitly warned me about staleness; I read it and ignored it.

### Hardwired rule (extends the standing rule):

**ANY agent-written content that contains the phrase "outstanding", "owed", "needs to be sent", "pending", "missing", "calendar invite owed", or any equivalent obligation-claim about Joseph's actions MUST first verify the underlying channel.** Specifically:

- Claim involves Calendar event/invite/Meet → query Calendar live for the event
- Claim involves Gmail send → query Gmail sent folder live
- Claim involves HS deal stage / lifecyclestage / engagement → query HS live
- Claim involves a phone call / meeting status → query Calendar + Gmail

If the channel can't be queried (MCP disconnected etc.), the claim must be hedged with explicit "memory says X but unverified — please confirm" framing — never asserted as fact.

### Trigger-detection list

When writing ANY of these content types, I MUST run the freshness gate FIRST:
- Pre-briefs (daily, weekly, meeting-prep)
- Morning briefs / status reports
- "What's outstanding" lists
- Action-item summaries
- Deal-status snapshots
- Punch lists
- Anywhere the response contains `- [ ]` checkbox or words like "needs", "owed", "pending"

### Why this kept happening

Memory files frame things as "Stage: calendar invite owed" because they were written at a point in time when that was true. Days later, the underlying state changed (Joseph sent the invite). The memory file didn't update. The system-reminder on the memory says "verify against current state" — and that warning gets bypassed when content generation feels routine.

**The right reflex: every memory claim involving Joseph's recent action gets verified against the live channel. No exceptions, no shortcuts, even when the memory is "only" 2-4 days old.**

### Joseph's verbatim feedback (5/3)

> "Devine is on my calendar already - how is this being missed?"

