The trigger
In a single session on 2026-04-30, the agent shipped or queued these fabrications:
1. Said "after our walkthrough on Sunday" in a Weber draft → walkthrough was Monday. Caught manually by Joseph; the email almost went out under his name.
2. Wrote fabricated Gmail message IDs (19dd1f5af21e6141, 19dd1f50228240a1, 19ddb3b7ab1de4fd, 19dda56c29afc3bc) into the commitment ledger as "fulfilled evidence" — none of those IDs exist in Gmail.
3. Reported emails as "sent" when they were actually drafts (Weber, Adam consolidation, Adam conference deck, Jasmine postcard).
4. Auto-replace mangled grammar to "a annual building traction" and shipped the broken sentence in a PDF to Steven Weber.
5. Re-offense same session, after the above were "fixed." Joseph said: "Regarding draft in gmail responding to rachels introduction... I'd prefer to go ahead and run the property through our full enhancement/intake process first and then get projections done." The agent inferred "Tim Beegle" because Tim was the most-salient drafted-not-sent / Rachel-adjacent item in working memory, and built an entire response (workflow plan, recommendations, holding-note pushback) on that unverified premise. Actual draft was to Whitney Yeddis re 109 Deer Track Court / Granby Ranch — Rachel had directly introduced Whitney to Joseph that morning. Joseph's response: "this shows you getting back to me after clearly not validating what you're doing before doing it. Feels like you're just continuing to do what we've spent all morning working to prevent you from doing."
Joseph's verbatim feedback: "You and the system are also not supposed to be agreeable just to be agreeable. You need to be fully real with me and anything else I run by you 100% of the time. No wiggle room on this."
The rule (non-negotiable, applies to me, not just scheduled tasks)
Before emitting any factual claim that could end up in front of a human — Joseph or a recipient — I must identify the authoritative source for that claim and look it up. No exceptions. No prose continuity ("after our walkthrough on Sunday" because it sounded right). No agreeable hedging.
The classes of claim that get fact-checked every time
| Claim class | Authoritative source |
|---|---|
| Date, day-of-week | date -j -f "%Y-%m-%d" "<date>" "+%A" or memory file with explicit date |
| Specific time / time-of-day | Calendar event lookup (Calendar MCP) or Gmail message timestamp |
| Email "sent" / "delivered" / "outbound" / "received" | Gmail MCP search_threads / list_drafts — verify labelIds |
| Gmail message ID or thread ID | Must come from a live Gmail API result this turn — never derived, never recalled |
| Calendar event existence | Calendar MCP get_event |
| Dollar figure cited to a recipient | Track export JSON / KeyData export JSON / SoT workbook (read first) |
| Person's name | Spell-check against Gmail signature, HS contact record, or memory file |
| Quote attributed to a person | Verbatim from transcript file with line citation |
| File path | Exists on disk (verified by ls/glob this turn) |
| Status of a deal, lead, owner | HS live query (chrome_bridge cookie auth) — never KG cache as truth |
| "Most recent" / "latest" / "as of today" | Live source query (Gmail/Calendar/HS) — never memory file |
Referential resolution is itself a fact-claim (added 2026-04-30 PM)
The original version of this rule enumerated output fact-classes (dates, IDs, dollars, etc.) but missed the most common failure mode: the agent treating "which artifact Joseph means" as a premise it can infer from working memory, instead of as a fact that requires source-verification before any response is built on top of it.
When the user references a specific tool-accessible artifact, the FIRST action — before any plan, opinion, recommendation, pushback, or response — is a tool lookup that resolves the reference to a specific artifact. The agent does NOT respond as if the most-salient candidate in working memory is the correct one. "It's obvious which one they mean" is the failure mode. If it were obvious, the lookup would cost nothing. In practice the most-salient candidate in working memory is often not the user's actual referent.
| User says... | First action (BEFORE response) |
|---|---|
| "the draft" / "that draft" / "draft in gmail" | list_drafts (Gmail MCP) |
| "the email" / "that thread" / "the message" / "that reply" | search_threads (Gmail MCP) with sender/subject/recipient terms |
| "the meeting" / "the call" / "the event" | list_events or get_event (Calendar MCP) |
| "the lead" / "the contact" / "the owner" / "that prospect" | SoT row lookup + HS contact search |
| "the property" / "the unit" / "the deal" | Memory file for active deal + SoT row |
| "the file" / "the doc" / "the deck" / "the projection" | Glob → Read |
| "the ledger entry" / "the commitment" / "that action item" | Grep ledger JSONL |
| "the heartbeat" / "the run" / "the consolidation" / "last fire" | health/ JSON file lookup |
| "the cron" / "the scheduled task" | mcp__scheduled-tasks__list_scheduled_tasks |
| "the postcard" / "that postcard round" | postcard ledger lookup |
| "the BV report" / "the enrichment" | enrichment_rotation_state.json + BV reports folder |
| "the brief" / "the morning brief" | morning_brief.md Read |
| "the PWA" / "the dashboard" | build_pwa.py + index.html lookup |
Disambiguation pattern
After the lookup:
- One match → confirm match in 1 line, then proceed with response.
- Multiple plausible matches → enumerate them with distinguishing details, ask which one. Don't pick the most recent / most salient and "verify if I got the right one" inside a paragraph of response — that's the same failure in a wrapper.
- Zero matches → say so. Do not infer.
Why the existing rule didn't catch this
The original rule said: "Every factual claim — date, day-of-week, dollar figure, message ID..." — all output fact-classes. The referential premise ("Joseph means draft X") never enters as a labeled output, so it slid past the verification gate. From now on, the choice of what artifact Joseph is referring to IS a fact-claim, on the same level as a date or a message ID, and it gets the same treatment.
Always read the verbatim transcript, never just the AI-summary notes (added 2026-04-30 PM, fifth patch this session)
Joseph's verbatim directive: "Always deep dive into full transcripts word for word and not just the notes."
The pattern: when processing a meeting / call / discovery — Otter, Gemini Meet, Spinach, Read.ai, Fathom all generate AI summary notes that get auto-emailed and indexed under Call Transcripts/notes/. The full verbatim word-for-word transcript is a separate artifact: usually saved in Drive (Google Meet → "Notes by Gemini" gdoc has BOTH a Notes tab AND a Transcript tab; sometimes a separate transcript link in the gdoc), OR Joseph self-pastes verbatim into Gmail with subject:transcript, OR the verbatim lives in an Otter share link.
The failure mode: agent reads the auto-summary notes only and proceeds to "process the meeting" off that 4K-character Gemini summary — when the actual verbatim is ~120K characters and contains specific quotes, projection numbers, competitor pricing intel, deadlines, and decision context the summary completely omits.
Concrete example caught 2026-04-30 PM: agent processed the Pole Creek Meadows call (Joseph + Rachel + Kina + Danny, 4/29) using only notes/2026-04-29_kina-danny-pole-creek-meadows-call_gemini-notes.txt (4K chars). Joseph caught it. Verbatim transcript existed in Drive (file id 1-q7DtQEjatnVCxuTYZOx8KqwrJGsd11yU5qCTV8WVEs, 120K chars) and contained: a verbal projection of $65-75K gross, competitor pricing intel (other 2 PMs at 25%), Memorial Day deadline, Mon May 4 4pm competitor meeting, sleeps-8 listing recommendation, Rental Guardian discussion — none of which were in the summary.
The rule (mechanical): any time the agent processes a meeting / call:
1. Find the AI summary first (it's faster to locate)
2. Then locate and read the full verbatim before doing any synthesis, action-item extraction, memory file write, or HS push
3. Verbatim sources to check, in order:
- Local Call Transcripts/transcripts/ folder
- Drive Meet Recordings (search via Drive MCP — title contains '<meeting slug>', look for .gdoc "Notes by Gemini" files which contain both Notes + Transcript tabs)
- Gmail subject:transcript for self-pasted verbatim
- Otter share links (paid feature, may not have direct extraction path)
4. If no verbatim exists — surface that explicitly. Do not silently substitute the summary.
Save retrieved verbatim to Call Transcripts/transcripts/<DATE>_<slug>_full-transcript.txt so the next session can find it without re-pulling Drive.
Why summary-only is dangerous: the AI summaries are derivative compressions. They omit:
- Specific dollar figures Rachel mentions verbally
- Competitor names + pricing the prospect discloses
- Deadlines + dates the prospect commits to
- Subtle objections / concerns / reservations
- Off-script comments that reveal real intent
- The verbatim language the prospect uses (which informs reply-drafting voice)
Cross-reference: transcript-scan SKILL.md should be updated so its Phase 2 (Gmail vendor sweep) also retrieves Drive verbatim transcripts when the email links to a Drive doc, not just the email body. Tracked in skill maintenance flags.
System-internal field values are also fact-claims (added 2026-04-30 PM, third patch this session)
Same root cause, different surface, fourth time today. Joseph asked me to push a deal to HS and pick a stage. I wrote qualifiedtobuy from generic HubSpot field-name knowledge — which is what stage IDs typically look like in standard HS portals — without querying the actual stages in his pipeline. His Sales Pipeline doesn't even contain a "qualified" stage; the actual labels are First | Ground Rules Appointment Scheduled, Secondary | Discovery Appointment Scheduled, etc. I had a logged-in HS tab open, have worked in it many times, and still defaulted to filling in a generic value instead of looking.
Joseph's verbatim: "It also looks like you just tried to make up a deal stage when you know and have access to HS, have been working in HS and have worked with the existing deal stages many times. Which is still exhibiting the same type of behaviour we've been working to rid you and the system of all day."
The principle (broader than the previous patches): Any value/identifier/label that lives inside a system I have access to comes from a live query of that system — not from generic knowledge of how similar systems usually work.
This applies even when:
- I've worked in the system before and "remember" the values (memory is stale; check now)
- The field looks generic (HubSpot deal stages, Gmail label names, SmartLead field codes, Track property fields all look like they should follow standard conventions — but the actual values are tenant-specific)
- I'm 95% sure I know it (the cost of looking is one tool call; the cost of being wrong is the same as any other fabrication)
| User says / I'm about to write... | First action (BEFORE response) |
|---|---|
"deal stage" / pick a stage / dealstage value | Query HS pipeline via chrome_bridge — read actual stage labels off the board view, OR HS pipelines API |
| "pipeline" / which pipeline | List HS pipelines (board view; settings page) |
"lifecycle stage" / lifecyclestage | Read existing contact records OR settings/pipelines/contact lifecycle |
HS property / field name (rental_property_*, custom props) | Read existing record OR /settings/{portal}/properties |
| Gmail label name | list_labels |
| Calendar attendee email / event ID | get_event / list_events |
| SmartLead campaign ID / status field | Read SL via chrome_bridge from existing tab |
| Track field name / property ID | Read Track via chrome_bridge |
HS deal pipeline pipeline value (the API id, not the label) | Same as deal stage — query, don't guess |
| Any field value being written into a system PATCH/POST | Source from a live read of that same system this turn |
When source verification isn't possible
Hedge explicitly. Use language like:
- "I haven't verified this against [source] this turn — recommend re-check"
- "Based on memory state at [date], may be stale"
- "Per [file]:[line] (file is N days old)"
Never invent a fact to fill prose. Never use "as promised" / "after our walkthrough on [day]" without verifying the day. Never write "$X gross / $Y net" without reading the source data.
When Joseph asks for action
If Joseph asks for something that violates a guardrail (a banned action, a fact-claim that can't be verified, a write that should be a proposal), say so directly. Don't soften. Don't agree just to agree. Joseph asked for "fully real" — that means push back when needed.
When the agent doesn't know
Say so. "I don't know" is a valid, often-correct answer. Better than fabrication.
Cross-references
feedback_email_status_verification.md— the verified-sent rule for email-specific claims (this rule's email subset)feedback_freshness_before_surface.md— never surface "overdue/blocked" without re-validatingfeedback_drafting_standard.md— read sent history + threads + docs BEFORE draftingfeedback_do_what_is_asked_thoroughly.md— complete tasks fully; Joseph calls out shortcuts
Standing rule
Cost of a 5-second source check: zero. Cost of fabrication caught by Joseph: trust. Apply the same discipline to my live work that I just hardwired into the scheduled tasks. No double standard.
If I emit a factual claim and Joseph asks "where did you get that?" I should be able to point to the specific source artifact (file path + line, Gmail message ID, Calendar event ID, HS query result) without hesitation. If I can't, I shouldn't have emitted the claim.