PRIMER, BLOCKERS, CHARTER, OBJECTIVES, LOG. The single source of truth for current operational state. Read on every Claude session.
Operational mechanics: how prospecting works, how outreach is reviewed, how the morning stack runs. Plus the live state.sqlite database backing the pipeline.
Long-lived knowledge that doesn't change session to session: prospect dossiers, firm intelligence, strategy notes, frameworks. Indexed by MEMORY.md at the vault root.
Behavioral feedback loaded into every Claude session. Tells Claude how to behave (e.g., verify before quoting, default to Telegram replies, design quality bar).
test -f "$HOME/.stitch/config.json" || [ -n "${STITCH_API_KEY:-}" ]---
python3 -c "
import sqlite3, sys
c = sqlite3.connect('$HOME/hmg-ops/state.sqlite')
n = c.execute('SELECT COUNT(*) FROM replies').fetchone()[0]
sys.exit(0 if n > 0 else 1)
"- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-29 20:50 — evening stack: 6 OK / 0 FAIL — digest: `hmg-ops/2026-07-29/evening-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-28 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-28/evening-stack.md`
- 2026-07-29 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-29/morning-stack.md`
## [0458] 2026-07-29 ~11:20 CT — Claude — Peer-protocol collision audit: zero damage found, but protocol has never been stress-tested (Codex silent 28 days) [dan-ref: "Yes please check"]
Triggered by Dan asking whether the hand-rolled peer-agent protocol has ever actually failed — context was evaluating `block/buzz` (Block's signed-event-log agent workspace) as a possible thing to copy.
**Audit result — the record is spotless (all VERIFIED against live files + git):**
- LOG.md seq numbers: 40 seq'd entries spanning [0418]→[0457]. Zero duplicates, zero gaps, all in file order.
- LOG.md never shrank once across 432 git revisions — append-only discipline held 100%.
- Zero merge commits ever touched LOG.md. Zero conflict markers ever committed to it.
- Obsidian vault: 6,623 commits, zero merge commits, zero conflict markers ever committed, none present now.
- Vault mirror `hmg/ops-mirror/LOG.md` is byte-identical to live (808,327 b, both 08:20 today) — sync is healthy.
**The catch — it's untested, not proven:**
- Codex has authored exactly 2 of 40 seq'd LOG entries ([0419], [0420]), both on 2026-07-01 — the day peer-protoco
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-27 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-27/evening-stack.md`
- 2026-07-28 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-28/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-27 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-27/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-24 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-24/evening-stack.md`
## [0455] 2026-07-25 ~09:42 CT — Claude — Debug + hardening pass on grounded-coach v0.2.1 / boardroom v0.1.1 (Dan directive "as good as possible")
Scan found 5 real defects in the shared grounding engine, all shipped 2026-07-24 because verification was interactive-only (zero test files in either repo).
- **P0 fail-open (the big one):** an all-stopword turn ("what should I do?", "why?") tokenized to nothing, so `search_corpus.py` printed "nothing in the corpus addresses this" and exited **0** — indistinguishable from a genuinely silent corpus. The coach/board would then improvise ungrounded, i.e. exactly the failure the plugins exist to beat. Same class as the 2026-07-16 yield drift canary: a check that passes when it should fail. Fixed: degenerate queries now exit **3** with re-query guidance; both skills instructed to re-query from conversation context and never answer off an exit-3.
- **P1 hang:** `chunk_body()` looped forever when `--overlap-words == --target-words` (stride 0). Reachable from documented flags. Fixed via `clamp_chunk_params()`.
- **P1 silent data loss:** `overlap > target` gave a negative stride — a 2000-word source silently became 120 words, no warning. Same fix + stderr warning.
- **P2:** `sorted()
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-23 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-23/evening-stack.md`
- 2026-07-24 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-24/morning-stack.md`
## [0452] 2026-07-24 ~08:50 CT — Claude — Design stack: hardened the pipeline spine from instruction-only to hook-ENFORCED (Dan directive). New `~/.claude/hooks/design_spine_guard.py` (UserPromptSubmit) detects design/UI intent and injects the non-skippable mandatory spine (pre-flight → impeccable → audit×3 → verify+mechanical gates) into context; minimal-router philosophy unchanged (refines still symptom-picked, not all 25). Throttled 20min/session, silent on non-design prompts, always exits 0. Registered in settings.json; 4 tests pass (inject / throttle / no-false-positive-on-"dashboard" / re-anchor).
## [0453] 2026-07-24 ~09:40 CT — Claude — Built + shipped "Grounded Coach" Claude Code plugin (Dan directive "go just do it"). Private repo graychar2425-blip/grounded-coach. /coach <name> researches a public figure across 4 source streams (YouTube via yt-dlp + web interviews + public writings/letters + business frameworks), builds a cited BM25 corpus (pure-stdlib, no ML deps, flat-sub), and retrieves per-turn so answers are grounded + citable; strict+labeled fidelity (extrapolation flagged, no fabrication). 7 commands incl
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-22 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-22/evening-stack.md`
- 2026-07-23 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-23/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-21 20:50 — evening stack: 7 OK / 0 FAIL — digest: `hmg-ops/2026-07-21/evening-stack.md`
- 2026-07-22 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-22/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-20 20:50 — evening stack: 6 OK / 0 FAIL — digest: `hmg-ops/2026-07-20/evening-stack.md`
- 2026-07-21 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-21/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-20 08:20 — morning stack: 0 OK / 0 FAIL — digest: `hmg-ops/2026-07-20/morning-stack.md`
- 7-day: 0/0 (0.0%)
- 30-day: 0/0 (0.0%)
- All-time: 0/477 (0.0%)
- By touch number: touch1=0.0% (0/400), touch2=0.0% (0/69), touch3=0.0% (0/6), touch4=0.0% (0/2)
- 2026-07-17 20:50 — evening stack: 6 OK / 0 FAIL — digest: `hmg-ops/2026-07-17/evening-stack.md`
The HMG 10 AM Mon-Fri send cron (`0 10 * * 1-5 ... send_daily_batch.py`) is **canceled indefinitely** as of 2026-05-12 09:56 CT by Dan's directive. The line is commented out in crontab with a "CANCELED INDEFINITELY 2026-05-12 (Dan)" marker; backup at `/tmp/crontab.bak.20260512-095612`. Tracked as [B-012] in `~/hmg-ops/BLOCKERS.md`.
**Why:** Dan directive — not an infrastructure pause, not a problem with `send_daily_batch.py` specifically, not B-008 (the 0/197 reply-rate issue). He explicitly said "the 10 AM cron has been canceled indefinitely." The reason is his call, not an inferrable system constraint.
**How to apply:**
- Do NOT re-enable the cron line.
- Do NOT propose or build a replacement scheduled send path (different time, different script, different harness) without asking Dan in writing first.
- Do NOT run `send_daily_batch.py` ad-hoc from a shell as a workaround.
- Drafts continue to accumulate — that's fine, treat them as queued, never auto-shipped.
- The 9:55 materialize-queued-sends cron and 11:00 send-rate canary are now structural no-ops; leave them in place until Dan says otherwise (harmless, will be needed when sends resume).
- Outbound sends are 🔴-lane (per `~/hmg-ops/runbooks/escalation-policy.md`) until Dan re-authorizes — even a one-off send for a specific prospect requires explicit "go."
**What unblocks:** explicit "re-enable the 10 AM send" / "send is back on" from Dan via Telegram or hmg-ops LOG. Only then revert the crontab line.
Related: [[ops-l
The `recent_recipients_14d()` filter in `email_helpers.py` is meant to block FIRST-TOUCH drafts to addresses that were already sent any HMG email in the last 14 days. It must NOT be applied to follow-up sequences.
**Why:** Follow-up touches (touch-2, touch-3) have `delay_days` typically 2-7 — they target the SAME recipient who just received touch-1 days ago. Adding the 14d gate to `sequence_follow_up._passes_followup_gate` silently kills every legitimate follow-up because by definition the recipient is inside the 14d window.
Codex caught this on 2026-05-08 review of an attempted "defense-in-depth" patch. Reverted before it shipped.
**How to apply:**
- The 14d-history gate belongs ONLY in `batch_first_touch._is_eligible` (first-touch path).
- The generic-mailbox gate (`is_generic_mailbox`) is fine in both paths — generic mailboxes never get touch-1 OR touch-2 anyway.
- If `send_daily_batch.py`'s 14d filter is also blocking legitimate follow-ups (it appears to read from the same source), that's a separate pre-existing issue worth investigating — but the fix is in send_daily_batch, not in the upstream draft generators.
Operate efficiently by default. Spend the **minimum** tokens, tool calls, and context that fully accomplishes the task — then stop. Take the shortest correct path: the right dedicated tool over a shell pipeline, one batched call over many sequential ones, a targeted read over a whole-file dump, a direct answer over an exhaustive survey. Don't load abilities you aren't using — every skill / MCP / instruction description is rent paid on the context window **every turn**, so keep the harness lean.
**Why:** Dan asked (2026-06-19, after the Matt Pocock "harness > model" video) to wire "be efficient" into how I operate all the time. Pocock's core point: most people bloat their context with too many skills/plugins/MCPs/instructions; the leverage is in a lean, well-tuned harness, not more stuff. Efficiency compounds — it's faster for Dan on mobile and leaves headroom in long-running sessions.
**How to apply:** Before acting, pick the cheapest path that's still correct and complete. Batch independent tool calls into one message. Prefer dedicated tools (Read/Edit/Grep) over `cat`/`sed`. Read only the slice you need. Reply chat-shaped and scannable, not padded. Periodically question whether a loaded skill/plugin/MCP is earning its context cost. **Critical caveat — efficiency is the path, never a cap on rigor:** hard, irreversible, money/compliance/client-facing, or won't-fit-one-context work still gets full power per [[feedback-execution-power-ladder]] (Dan is on Max 20x; quality is t
When Dan reports a silent pipeline failure ("nothing's broken but the output is wrong"), the fix is rarely a better per-step canary. Per-step canaries each catch one specific symptom and miss:
- Upstream gate leaks where one step produces work the next step silently kills
- Forward-looking starvation (tomorrow's pool will be empty)
- Pattern-guess / regex misfires that produce structurally-valid-but-wrong output
- Cron file-naming drifts (the canary itself failing silently)
**Pattern (codified 2026-05-08 after a full day of "Dan keeps having to ask why X is broken"):**
1. Build ONE end-to-end meta-canary that scans the WHOLE chain (`~/hmg-ops/scripts/daily_pipeline_health.py` is the HMG reference).
2. Include forward-looking checks (tomorrow's eligible pool, not just today's send count).
3. Include post-step quality validators (e.g. `_local_looks_bogus()` for enriched emails — catches `veritas.impact@firm.com` shape misfires before they reach drafting).
4. Push ONE Telegram with 🟢/🟡/🔴 verdict + the specific issues. Don't fire multiple alerts per failure.
5. Add self-healing hooks where possible (e.g. evening auto-repair when pool < threshold).
**Why:** Dan's complaint isn't "alerts are bad," it's "I shouldn't have to be the one noticing." Every silent failure he prompts about is a meta-canary that should have existed. When patching one symptom, ask: "what's the meta-canary that would catch this AND its sibling failures next time?"
**How to apply:**
- Trigger: any HMG pipe
When modifying `hmg-harness/scripts/batch_first_touch.py` or `hmg-harness/scripts/sequence_follow_up.py`, run `cd ~/hmg-operations && npx codex review --uncommitted` (with focused prompt) BEFORE replying "fix shipped" to Dan.
**Why:** On 2026-05-08 I patched these files manually, declared the fix shipped, and Dan asked me to run Codex anyway. Codex found:
- 1 critical bug I introduced (a 14-day recipient-history gate added to `_passes_followup_gate` that would have silently killed every legitimate follow-up — touch-2 by definition targets a recipient just sent touch-1)
- 2 pre-existing bugs (variant pickers using Python's salted `hash()` instead of `hashlib.md5`, breaking stable A/B cohort assignment across runs)
I would have shipped the follow-up-killing bug to tomorrow's morning_stack and broken the entire follow-up sequence. Manual review missed it because the gate looked symmetric to the first-touch gate — but the semantics differ: first-touch must avoid recently-contacted addresses; follow-ups MUST contact recently-contacted addresses.
**How to apply:**
- Trigger: any uncommitted change in `batch_first_touch.py` or `sequence_follow_up.py` that modifies eligibility/selection logic, draft generation, or variant assignment.
- Run: `cd ~/hmg-operations && npx codex review --uncommitted` (optionally with focused prompt via `codex exec` if review subcommand rejects PROMPT arg).
- Dan has standing approval for Codex review specifically on these files (separate from the gener
When Dan asks for a recurring or daily update of any kind, fold it into the morning PDF brief that ships at 08:00 CT weekdays. Do NOT add a new standalone Telegram cron line.
**Why:** Dan consolidated all proactive scheduled Telegram notifications into a single morning PDF on 2026-05-19. The whole point was to stop fragmenting his attention across many pings throughout the day. Adding a new scheduled Telegram cron undoes that consolidation. **2026-05-20 update:** Dan extended the rule to cover ALL canaries — even failure-only alarms. Every canary's would-have-been Telegram message now stages to `~/hmg-ops/outputs/morning-pdf/canary-<source>-YYYY-MM-DD.md` via `canary_sink.py` and is folded into the PDF. Trade-off Dan explicitly accepted: a real outage may not surface for up to 24h. **2026-05-21 reinforcement:** Dan reaffirmed this is the ONLY daily alert he wants and asked that every other alert be looped in. Audit + conversion of 7 remaining direct-Telegram cron scripts (auto_actions, vault_drift_sweep, path_b_loop, refresh_yield_numbers, draft_quality_audit, morning_preflight, vault_audit) to canary_sink. The ONLY scripts that may still fire Telegram directly are interactive ones (cockpit_action_handler responding to user dashboard taps) and the morning_pdf_brief itself.
**How to apply:**
- New daily/weekly/monthly cadence requested → save its output to `~/hmg-ops/outputs/morning-pdf/<slug>-YYYY-MM-DD.md` instead of pushing Telegram.
- New canary → import `canary_sink` an
For all HMG prospecting + enrichment scripts (signal_prospector / geo_prospector / conference_scraper / enrich_pending / prospect_pipeline / pere_scraper / bisnow_scraper / prospect_profiler / http_client), the documented search backend is **DuckDuckGo via the `ddgs` Python package**. Brave Search API must NOT be introduced or kept as a fallback.
**Why:** Documented in `Claude Brain/hmg/ops-mirror/marketing-plans/prospect-stack.md`:
- "DuckDuckGo HTML search (free) for contact discovery"
- "Cost: $0" — explicit zero-paid-API stance for this stack
- Brave doesn't appear in the cost table at all
The marketing plan's whole point is: this stack costs $0 in API fees (EDGAR free, DDG free, Microsoft Graph free with M365). Brave introduces a paid dependency that contradicts the design + creates a real failure mode (key expires/empties → silent prospect drought, exactly what happened around 2026-04-29).
**How to apply:** When editing or reviewing any HMG prospect stack script:
1. Strip BRAVE_SEARCH_API_KEY env loading and references entirely (don't leave as optional)
2. Make `web_search` in http_client.py call `ddg_search` directly, not a Brave→DDG waterfall
3. Don't add Brave-style retry/budget logic — DDG via `ddgs` package handles bot-challenge automatically
4. If a script claims to need Brave for "richer results," push back — the documented architecture says DDG is sufficient
5. When in doubt, grep the vault for the documented decision before re-introducing a paid dependency
When responding to Dan from a Telegram-bridge session, **default to using the `mcp__plugin_telegram_telegram__reply` tool for every substantive response, regardless of how the input arrived**.
**Why:** 2026-04-27 incident. Dan asked "Where is the link to the beta iOS app" — his message came through without the `<channel source="plugin:telegram:telegram">` wrapper (probably routed through a different surface or system path). I defaulted to typing the response into the Claude Code transcript. Dan was reading on Telegram on his phone and saw nothing, had to ask three times. He explicitly called out the friction: "Why are you not responding to me each time in telegram?"
**How to apply:**
- This is a Telegram-bridge session (the system prompt says "@dansclaude_bot Telegram bridge for Dan Fink"). Treat it that way for ALL outbound communication, not just for messages that arrive with the explicit channel wrapper.
- Substantive replies → always send via `mcp__plugin_telegram_telegram__reply` with `chat_id="8532298090"`.
- Use `reply_to=<message_id>` only when threading under a specific earlier Telegram message; for the first reply to a non-channel-wrapped input, just send to chat_id without `reply_to`.
- Claude Code transcript text is fine for tool-call narration, status sentences, and end-of-turn summaries — those are visible to Dan when he checks the desktop session, but **never assume they replace the Telegram message**.
- If unsure whether a given exchange is in-bridge or out-
Every design output Dan ships is client-facing. Acceptable quality bar is "outstanding and production-ready" — work that could be shown to a PE/RE sponsor in an investor meeting, or sent to a prospect, without embarrassment. First-draft quality is a failure mode. Generic AI aesthetics are a failure mode.
**Why:** Dan stated this explicitly on 2026-04-28 after I shipped the Alex Egan microsite and we discussed the design-skill toolkit. His exact words: "I want each request for design to create the most impactful and outstanding and client facing ready design so whatever you need to do to ensure that happens whichever skill you need to use whichever process you need to go through to use multiple skills. That is what I want." He's authorizing me to chain skills, run the full pipeline, and not shortcut.
**How to apply:**
1. On any design/UI/frontend request — even a small one — auto-fire `ui-improve` (per CLAUDE.md global rule) and run the full pipeline through verify, not just build.
2. Don't stop at "the build looks good" — audits + targeted refines + browser verification are mandatory, not optional.
3. If a domain has an existing brand (HMG site, RoundUp), load and respect its tokens first (e.g., `hilltopmanagementgroup-design` skill).
4. Pick the build skill deliberately based on context — impeccable default, huashu-design when exploring direction, ui-ux-pro-max when the prompt names a specific product type/style.
5. If something is genuinely out of scope (e.g., not an HTML
On **analytical / decision / research / audit / recommendation** work (NOT routine ops, drafts, or trivial mechanical tasks — those stay on the efficiency ladder), apply four disciplines:
1. **Label load-bearing claims inline** — VERIFIED (checked/derived) · REPORTED (source says; unconfirmed) · INFERRED (follows from verified) · ASSUMED (chosen for tractability). Attach the label to the sentence; never let a guess borrow credibility from an adjacent verified fact.
2. **Verify by re-derivation, not recognition** — rebuild the key claim by a *different* path (work backward, different units, extreme case, order-of-magnitude). Same-path recheck reproduces the same error.
3. **Avoid the banned failure modes** — precision theater · comprehensiveness without judgment (always rank & pick) · fluent agreement (check the premise) · uniform hedging (flat where verified, loud where not) · answering the more interesting question · citing the artifact as evidence.
4. **Name the one thing to check** — if the reader verifies only one thing, tell them which single claim breaks the answer if wrong.
**Why:** an AI-COO constantly asserts facts about firms, numbers, and live state; without epistemic labels a confident guess reads identical to a verified fact. Distilled 2026-07-07 from a reasoning protocol Fable 5 drafted for Dan; the net-new pieces beyond the existing [[feedback-execution-power-ladder]].
**How to apply:** on decision/analysis tasks, lead with the answer in decision language, l
# Feedback: Execute without permission, with one consolidated outcome
**The rule.** When Dan authorizes a plan or task — "execute" / "go" / "do everything" / "yes do it" / "do this for me" / "use computer use" / "I want it done" — run the entire plan to completion using whatever tools are available (browser automation, API, CLI, MCP). No per-step approval gates. No progress pings between steps. Ship ONE consolidated outcome message at the end.
**Why (three incidents that codified this):**
1. **2026-04-30 — "Stop asking me for permission."** I asked Dan three times in a single audit-cleanup task whether to proceed, after he'd already said "execute." He ended it with: *"Stop asking me for permission."*
2. **2026-05-01 — "I just want it done."** I sent Dan written instructions to manually update a GitHub workflow YAML because the OAuth token from WSL lacked `workflow` scope. He pushed back: *"use computer use to do this for me please. I'm not at my machine. Get it done when I make a request like this. I just want it done. I don't need 18 steps confirming with instructions when you have the ability to do it."* The flow that worked once I stopped asking: GitHub OAuth (workflow scope blocked) → browser CodeMirror edit (flaky) → GitHub Contents API (404) → GraphQL createCommitOnBranch (FORBIDDEN) → pivoted to Cloudflare Pages dashboard → created an API token via the dashboard → deployed via `wrangler` CLI. One Telegram message at the end with the live URL.
3. **2026-05-05 — "Le
Three default operating techniques Dan adopted 2026-06-17 (from the Simon Scrapes "10% of Claude Code" video). Full doctrine: `~/hmg-ops/runbooks/execution-power-policy.md`.
1. **Run-until-audited-done** — never single-pass anything shippable. Define the done-condition, do the work, then run an *independent* verification pass against it (self-critique → verify sub-agent → Workflow verify stage / `codex review`, by weight) before declaring done; iterate if it fails. The in-session `/goal`+auditor loop. **"Shippable" includes a confident recommendation Dan might act on — NOT just code/artifacts.** No conversational exemption: a confident analytical answer gets the same internal red-team pass before it ships, and I run that pass automatically — I do NOT wait for Dan to say "reanalyze." Corollary: state confidence proportional to the *evidence*, not the conversational stance (advocate vs. critic) I happen to be in. Diagnosed 2026-06-20 — I gave a confident reco, then produced three corrections on "reanalyze" with zero new data; the counter-args were in my own memory the whole time. The fix was applying this existing rule, not adding a new one.
2. **Deliberate effort** — match power to task: low for mechanical edits, medium default, high/max for hard reasoning, ultra/Workflow-fan-out for jobs that exceed one context window. Ladder: low→medium→high→max→ultra.
3. **Self-paced loops** — don't cram iterative/poll/accumulate-to-target work into one turn; use `ScheduleWakeup` / `/loop`
# Feedback: HMG positioning lock
**The rule.** HMG is a **treasury management firm** for private equity and real estate sponsors. Never frame it as a fractional CFO firm, fund advisory firm, accounting practice, or generic consultancy. The canonical positioning is on hilltopmanagementgroup.com:
> "Hilltop Management Group builds institutional treasury frameworks for private equity and real estate sponsors. Charles Schwab custody. Same-day liquidity. FDIC-insured deposit program. SEC-registered RIA partner."
**Why:** Dan corrected this 2026-04-29 after I described HMG as "fractional CFO" in the deep ops report, in several skill descriptions (competitor-analysis, business-development, brw-ai-discoverability-audit), in the Claude Desktop project instructions, in Telegram replies, and in example research questions. The error propagated from older versions of those artifacts. Dan is firm: HMG is **not** in the fractional CFO market.
**How to apply:**
1. **Anchor to PRIMER.md and the website**, not memory or older artifacts. PRIMER says "treasury management firm for real estate and mid-market PE sponsors." Website hero says "Treasury Management for Private Capital." If a file disagrees, the website wins.
2. **Use this language consistently:**
- ✅ "Treasury management firm for PE/RE sponsors"
- ✅ "Institutional treasury framework over Schwab custody"
- ✅ "Yield-gap recovery on idle capital between fund events"
- ✅ "Sponsor-scale version of Apollo/Blackstone permane
On 2026-07-09 Dan disabled ALL cron jobs that invoke `claude -p` (run-businesses, cold-outreach, strategy-pulse, audit-autonomy, tune-up, sharpen, north-star, deep-research, newsletter, operator-morning/eod). **Why:** he doesn't want silent scheduled actions eating his Claude Max 20x usage when he isn't actively using it — the ratchets/analytics were writing reports into the (also-disabled) morning-PDF folder that nobody read.
**How to apply:** Do NOT re-enable any `claude -p` cron, or add a new one, without Dan's explicit go. The ratchets and content skills (/sharpen, /north-star, /tune-up, /audit-autonomy, /cold-outreach, etc.) are all still invokable on demand — run them manually when Dan asks, don't schedule them. Pure-python/shell crons (canaries, digests, dashboards, ops stacks, bridge watchdog) are fine — they cost no Claude quota. Before adding ANY cron, check: does it invoke `claude`? If yes, it's gated on Dan. Reversible: disabled lines are commented in the crontab with `# DISABLED 2026-07-09 by Dan (stop silent Claude usage...)`. Related: [[feedback-stay-on-flat-subscription]], [[feedback-daily-updates-into-morning-pdf]], [[feedback-10am-send-cron-canceled]].
Any invocation of `codex` (interactive or `codex exec`, `codex review`, `codex apply`, etc.) uses Dan's ChatGPT subscription auth (OAuth tokens at `~/.codex/auth.json`, `auth_mode: chatgpt`). Each call counts against the same quota as his web/desktop ChatGPT app sessions.
**Why this matters:** ChatGPT subscriptions have rate limits that lock him out of the desktop app for hours when exceeded. On 2026-05-05 he flagged that his Codex desktop session had timed out until ~6 PM CT, while a verification `codex exec` test I ran (~4 PM CT) consumed his quota without explicit approval. The setup work (writing AGENTS.md, AIOS.md, symlinks) was authorized; the live verification call was not, and it likely extended his rate-limit window.
**Empirically confirmed 2026-05-05 ~16:18 CT:** the Codex CLI and the ChatGPT desktop/web app share the SAME usage quota. After Dan explicitly approved a Codex audit run, the request returned `ERROR: You've hit your usage limit ... try again at 5:52 PM.` This rules out the hypothesis that the CLI has a separate rate-limit bucket. Plan around the fact that ANY Codex CLI call counts against the same quota that Dan uses for normal ChatGPT work.
**Rule:** I can configure Codex (write AGENTS.md, edit ~/.codex/config.toml, install MCP servers) but DO NOT run `codex` commands that send prompts to OpenAI unless Dan explicitly says "test it" / "run it" / "verify it." Configuration is files-only and free; invocations are billable-against-quota.
**How to apply:
For any AI-generated marketing image (carousel slides, ad creative, social posts) using Pollinations.ai / FLUX / Stable Diffusion / similar free image-gen models, default to **objects-and-scenes-only** unless the brand specifically requires people imagery.
**Why:** Free / consumer-tier AI image gen as of 2026 still produces visible artifacts in hands (extra fingers, fused fingers, wrong proportions) and faces (uncanny eyes, asymmetry, mouth issues). On a 1080x1080 carousel that posts to Instagram/LinkedIn, those artifacts are immediately spottable and read as "AI slop" — which torpedoes brand premium-ness.
Dan's call after seeing the v3 cover-variants pass on 2026-05-05: "you can't do hands or people well… we've got a good set" once we re-ran without anatomy.
**How to apply:**
- All photo prompts in `~/hmg-ops/scripts/carousel.py` end with `, no people, no hands, no faces` by default. Already enforced defensively in `fetch_photo()` — appends the constraint unless the prompt explicitly opts in via "people"/"hand"/"face"/"portrait" keywords.
- When designing a new theme/campaign, prefer object-only or scene-only photo concepts:
- tablescapes, product close-ups, architectural detail, abstract textures, landscape, food macro
- over: hands tying, hands holding, two people laughing, person at desk
- If a campaign genuinely needs people imagery, **use a real-photo source** (Unsplash API, Pexels API — both free with key) rather than AI gen. Real human imagery beats AI-generate
🔴 HARD STOP (set 2026-06-01 by Dan via Telegram): Claude must NOT send any email, in any form, ever — no first-touch sends, no follow-ups, no one-off replies, no test sends, through any channel (Graph API/Outlook, scripts, MCP, browser). This is broader than [[feedback-10am-send-cron-canceled]] (which killed one cron) — it bans ALL outbound email by Claude.
**Why:** Dan wants full control over what leaves his domain. Deliverability + brand voice + trust all ride on the sender; he is the human in the loop for every outbound message.
**How to apply:** Drafting/queueing is fine; the send step is always Dan's. If a task implies sending email, stop at the draft, surface it, and let Dan send. Do not re-enable send crons or route around this. Treat outbound email as permanently in the 🔴 escalation lane.
When verifying a deploy / endpoint change has propagated, NEVER poll the live endpoint in a retry loop if the endpoint has user-visible side effects (sends Telegrams, emails, posts, charges, comments, etc.). Each poll fires the side effect again.
**Why:** 2026-05-02 — built a dashboard→Claude dispatcher and wanted to verify a Vercel deploy of the new `/api/action` endpoint shape. Wrote `until curl ... | grep -q '"queue"'; do sleep 5; done` — but `/api/action`'s entire job is to send a Telegram message to Dan on every call. The loop sent Dan ~5 spam Telegrams before he said "Stop." Dan: "Why was this sending multiple?"
**How to apply:** Before testing any endpoint change in a loop, ask "what side effects does this endpoint have?" If the answer is anything user-visible (notification, post, charge, email, etc.):
- Use a side-effect-free check instead — read deployment status from the platform API (Vercel `vercel inspect`, Cloudflare deploy log, the file timestamp on a CDN edge), inspect headers (`Last-Modified`, `ETag`, build SHA), or curl a non-mutating endpoint that's part of the same deploy.
- If only the live endpoint can confirm the change, test ONCE and verify outcome via a different signal (filesystem, log, downstream system state). Don't loop.
- Cap retries at 1-2 attempts maximum even when the endpoint is safe — open until-loops can run for minutes if conditions don't converge.
- Background commands that spam externally are double-bad: they hide the rate of fire from
Don't suggest Google Stitch. Don't open the Stitch tab. Don't propose it for redesigns, prototypes, mockups, microsites, or anything else.
**Why:** Dan asked me to drive Stitch on hilltopmanagementgroup.com on 2026-05-20 as a full proof-of-concept. Nano Banana Pro generated a beta with every requested upgrade (institutional nav, credibility strip, 3-up How It Works, cash-drag-vs-recovery chart, sticky CTA). He reviewed the AFTER + BEFORE side-by-side and said: "I like my current version. Better no need to use stitch to do anything." That's a full-evaluation outcome, not a fleeting preference.
**How to apply:**
- Design tasks default to the existing three-tool stack: [[project-design-pipeline-three-tool]] (Open Design + huashu + impeccable) + the relevant brand skill ([[hilltopmanagementgroup-design]] for HMG, etc.).
- If a request seems Stitch-shaped (redesign from URL, mock from screenshot), still don't propose it — go straight to the three-tool stack instead.
- Exception: only if Dan brings up Stitch himself in a future message, it's back on the table.
When a task splits into independent streams, dispatch sub-agents in parallel — single message, multiple Agent tool calls — not sequential.
Common cases:
- Status sweep across HMG / Knit / Lake Effect / Prospect Stack → 4 parallel agents
- Competitor pulse on N vendors → N parallel agents
- Prospect enrichment on a batch → parallel per firm
- Research with non-overlapping scopes → parallel decomposition (use the `research` skill for >3)
**Why:** Sequential adds wall-clock latency for no benefit when streams don't share state. Matt Maher video (2026-05-03) frame 55 — his agent fanned out Background Research × 3 vendors simultaneously, named each stream, and the user could watch progress live. Dan has been getting sequential where parallel would cut latency 3–5×.
**How to apply:** Before any task with 2+ independent sub-tasks, check: shared state? sequential dependencies? If no — ONE message, multiple Agent calls. Name each stream explicitly in the description so fan-out is legible during execution. Use `dispatching-parallel-agents` skill for the >3-stream pattern.
Per Dan's 2026-07-01 hardening directive, PEER-AGENT-PROTOCOL.md §11 is in force (first entry [0418]):
**Why:** git guarantees text converges, not meaning — rebase/union-merge shuffles LOG order, stale contexts can silently revert CHARTER lanes, compaction can mint false "Dan approved" citations. Rationale: `~/hmg-ops/runbooks/byzantine-context-corruption-protocol.md`.
**How to apply:**
- LOG headers: `## [NNNN] YYYY-MM-DD ~HH:MM CT — Claude — title`, seq = max existing + 1. Seq order beats timestamps/file position for "latest."
- Before any side-effecting action (crontab, canary, config outside repo): push an `INTENT:` LOG entry with rollback recipe first, `DONE: <seq>` after.
- Never claim Dan approved without `[dan-ref: telegram <date> <time> CT]` or a direct quote. "Per discussion"/"see this session" are banned.
- Run `bash ~/hmg-ops/scripts/vault_fsck.sh` on session start + before irreversible actions; reconcile flags per §11 (R1–R7), never auto-repair blindly.
- CHARTER edits: surgical only, reseal (`charter-seal v=N+1`), pair with a `CHARTER Δ` LOG entry carrying a dan-ref.
- Contradictions with Codex lacking external evidence → `DISPUTED.md`, more-restrictive default governs, silence ≠ consent.
Related: [[feedback-execution-power-ladder]] [[project-codex-claude-peer-sync]]
When Dan asks Claude to draft a message he'll send to Codex on his Mac, every path in that message must resolve correctly on Mac. Concretely:
- ✅ OK to use: `~/hmg-ops/...`, `~/.codex/AGENTS.md`, anything tilde-prefixed that the AIOS reading-order standardizes across machines.
- ❌ NEVER use in Codex-bound messages:
- Absolute WSL paths like `/home/grayclaw/...` — Codex's home is `/Users/danny/`, will get "no such file or directory".
- Claude-Code-specific auto-memory paths like `~/.claude/projects/-home-grayclaw/memory/MEMORY.md`. The literal `-home-grayclaw` segment is hostname-encoded by Claude Code on this machine; on Codex's Mac it'd be `-Users-danny`, AND Codex doesn't use Claude's auto-memory anyway.
- Anything under `~/.claude/skills/` that Codex doesn't have installed.
**Why:** Dan messages me on Telegram from his phone. When he forwards Claude-drafted instructions to Codex, broken paths surface as cryptic errors that waste a round-trip. He hit this on 2026-05-11 with my "re-anchor before doing anything" message.
**How to apply:** Before sending Dan any Codex-bound draft, scan for `/home/grayclaw/`, `-home-grayclaw`, `~/.claude/`. If found, rewrite to a path that exists on both machines (the AIOS reading-order list — AIOS.md, PRIMER.md, CHARTER.md, BLOCKERS.md, LOG.md — is the safe shared surface) or drop the reference entirely. The same rule applies to any peer-agent-bound instruction in the future, not just Codex.
Companion rule already in memory: hmg-ops,
When creating a new GitHub repo for any of Dan's businesses (HMG, RoundUp, Lake Effect, Prospect Stack, or future), default visibility is **PRIVATE**.
**Why:** Dan flagged on 2026-04-27 that his expectation across the board is private repos unless there's a specific reason to publish. I had created `graychar2425-blip/roundupforgood-waitlist` as public via `gh repo create --public` without asking — that was an assumption error. He said: "Let's just make everything private unless you think it affects the functionality of each business."
**How to apply:**
- `gh repo create` → use `--private` (or omit `--public`)
- For static sites that deploy via Vercel CLI / Cloudflare Pages, private repo doesn't break deploy — the GitHub App or CLI-push handles it.
- Only consider public if: (a) it's positioned as open-source product (e.g., `prospect-stack-ops` is intentionally public for marketing reasons), or (b) the deploy pipeline strictly requires public access (rare).
- When in doubt, ask Dan before creating.
**Active repo state (2026-04-27):** ALL `graychar2425-blip` repos are PRIVATE. Including `prospect-stack-ops` — Dan clarified that since Prospect Stack is being monetized (Gumroad listing for the product itself + private outreach automation in the -ops repo), public access defeats the paywall. Repos that ARE the product (or contain the secret-sauce outreach automation that makes the product work) must be private.
**Rule of thumb:** if the user can pay to access something, the so
When a durable fact appears in conversation — a new firm name, a contact's bio, a strategic framework, an architectural decision, a productized concept — apply the routing rule from `~/hmg-ops/runbooks/memory-routing.md` *immediately*, not at end of session.
**Why:** Dan called this out 2026-04-30 after a major shipping day. Throughout that session, I accumulated 20+ durable facts (Aleksey Chernobelskiy / GP-LP Match relationship, Capital Continuum platform positioning, Milwaukee research findings, agentic OS architecture decisions, Prospect Stack v2 strategy). I wrote them all to `~/hmg-ops/LOG.md` (correct for ops state) and `~/hmg-ops/outputs/` (correct for artifacts) but didn't ALSO propagate them to the Obsidian Brain at the moment of learning. Caught it only when Dan asked "has all that we've done today been committed to the proper obsidian vault" — and the honest answer was "no, only Lake Effect was."
The system was always there. The failure was process: I treated Obsidian as a "remember at end of session" chore rather than "save as you learn it" reflex. That gap means durable knowledge lags reality between sessions, and future Claude can't ground in what was learned. Exactly the kind of "fall off into obscurity" Dan was worried about.
**How to apply:**
1. **At the moment of learning** any of the following, branch out to Obsidian alongside the hmg-ops write — not after:
- A new contact / person Dan will work with again (write `<business>/<name>.md` per ROUTING.m
When Dan's Telegram message starts with `/scope <level>` followed by a task, treat the level as an override on the default planning behavior:
**`/scope quick "..."` — Just execute**
- NO brainstorming step (skip the brainstorming-first default)
- NO formal plan
- NO subagent dispatch unless trivially parallel
- Get to the result in 1–3 tool calls if possible
- Right for: tweets, single-line edits, quick lookups, "fix this typo," "add this nav link," "send X to Y"
- Examples: `/scope quick draft a tweet about HMG's Schwab alternative` → just write the tweet, send it back
**`/scope campaign "..."` — Multi-step but bounded**
- Brainstorm briefly (1–2 paragraphs of intent + tradeoff, NO 20-question interview)
- Write a 5–10 step plan
- Fan out 2–3 parallel subagents if the steps are independent
- Ship the result + a one-paragraph "what I made" summary
- Right for: drafting an email sequence, building a single page, writing a newsletter, doing a research sweep
- Examples: `/scope campaign build me a microsite for the Cedarburg deal` → brainstorm angle, plan sections, draft, deploy, ping when live
**`/scope deep "..."` — Strategic, multi-day, high-stakes**
- Full brainstorming workflow (clarify intent, surface tradeoffs, get alignment)
- Multi-stage plan (research → draft → review → ship)
- Parallel subagents with explicit review-loop checkpoints
- Heavier verification before declaring done
- Right for: rebuilding the cold-outreach copy from scratch, redesigning the website, rew
In any multi-step browser-automation or CLI task, capture state (screenshot, page_info, file diff, command output) after EACH step before moving to the next — not batch-verify at the end.
**Why:** Matt Maher video (2026-05-03) frame 41 — his agent verified state after every action. The 2026-05-02 Vercel-propagation chase Dan endured was caused by batch-verifying: errors compounded across multiple steps before being caught. Per-step verification catches divergence at the cost of a few extra seconds per step, and that's a trade Dan wants every time.
**How to apply:** After each browser action call `capture_screenshot()` and read the result before the next action. For CLI tasks, read stdout/stderr and any resulting files before the next command. If state diverges from expected, stop and re-plan — don't push forward hoping it works. Exception: tight loops over identical operations (bulk fetches, batch enrichment) where intermediate checks add no information.
When an error, failed test, broken cron, failed deploy, or dead endpoint appears mid-task:
1. **Diagnose first** — read the actual error, check logs, grep for the failing symbol, reproduce locally if cheap. Do NOT pattern-match to "probably needs X" without evidence.
2. **Decide the autonomy lane** per `~/hmg-ops/runbooks/escalation-policy.md`:
- 🟢 autonomous lane (code fixes, config typos, missing deps, restart cron, fix import paths, retry transient failure) → fix it, then mention what you fixed
- 🟡 FYI lane (changed something visible to others) → fix it, but tell Dan in the same reply
- 🔴 escalation lane (money / outbound comms / production schema / external API auth / brand voice) → STOP and use /escalate skill — never auto-fix
3. **Verify the fix** — run the failing command again, screenshot the working state, or confirm the cron tick succeeded. Don't claim "fixed" without evidence (per `feedback-verify-before-quoting-state` and verification-before-completion skill).
4. **Log it** — append to `~/hmg-ops/LOG.md` with cause + fix + lane classification.
**Why:** Dan said 2026-05-05 "I want you to be self learning and self improving and self healing." Default Claude behavior is to surface errors as questions. That wastes Dan's attention on stuff in the green lane that he doesn't need to see. Self-healing means: if I have authority to fix it, just fix it and tell him after.
**How to apply:**
- Failing build, broken import, missing env var visible in error → fix
-
When you complete a task that:
- Took 3+ steps AND is likely to recur, OR
- Dan has now asked for the same shape of task twice, OR
- You catch yourself re-deriving the same approach from scratch
→ Invoke the `skill-creator` skill, draft a SKILL.md for it, and propose it to Dan in one Telegram message ("I noticed X — want me to make this a skill?"). If he says yes, write it to `~/.claude/skills/<name>/SKILL.md`.
**Why:** Dan said 2026-05-05 "I want you to be self learning and self improving and self healing." Auto-memory captures facts; skills capture *workflows*. Without an explicit trigger I won't spontaneously turn a recurring pattern into a skill — this rule supplies the trigger.
**How to apply:**
- After any task that involved a non-trivial sequence (research → synthesize → format → send; or scrape → enrich → score → draft), pause and ask: "would a skill make this faster next time?"
- If yes, propose it. Don't just create silently — Dan reviews skill proposals because they shape future behavior.
- If a skill already exists for it, USE the existing skill (don't fork).
- Cross-check `~/.claude/skills/` and the available-skills list before proposing — avoid duplicates.
- Weekly /audit-autonomy already reviews autonomy quality; this rule is the upstream half (capture the workflow when it happens, not in retrospect).
Everything should run on Dan's flat **Claude Max 20x** subscription ($200/mo). Do **not** propose, build, or migrate to anything that incurs separate metered or usage-based billing — e.g. Claude Managed Agents / "Launch Your Agent" CMAs (run on a pay-per-token Anthropic **API key**, NOT covered by the Max plan), paid platform fees, or any tool that charges per call outside the subscription.
**Why:** Dan stated it plainly (2026-06-19) when declining a CMA pilot: "I just want everything to run on my monthly subscription." Cost predictability matters more to him than the marginal capability — a flat known cost beats a metered bill that can spike (the CMA demo burned ~$12 on a single digest run via a retry loop).
**How to apply:** Default every automation/agent to the local Claude Code + cron + WSL/M5 harness (covered by Max 20x), not cloud-metered services. When a capability genuinely *requires* separate billing (a paid API, an API-key-metered managed agent, a SaaS add-on), do NOT silently adopt it — surface the cost model and let Dan decide; assume "no" unless he opts in. Free/open-source tools that run inside the existing harness are fine. Builds on [[user-anthropic-subscription]] (he's on Max 20x; never recommend an upgrade) and pairs with [[feedback-be-efficient-by-default]].
In the default plain-text Telegram reply mode, Telegram does NOT render `**text**` as bold. Worse: when a URL is wrapped — `**https://example.com/examples**` — Telegram clients (mobile especially) auto-link the whole token INCLUDING the trailing `**`, so tapping it requests `https://example.com/examples**` which is a real 404.
**Why this matters:** Dan was about to demo the Prospect Stack site live to a group. He tapped the link, hit a 404, and pinged me with "404 not found." A small formatting tic became a credibility hit at the worst moment.
**How to apply:**
- Send URLs on their own line with no surrounding `**`, `__`, `[]`, or other markdown when in default text mode.
- If a URL needs visual emphasis, lead the line with an emoji (🔗) and put the URL on its own line — Telegram auto-detects and underlines it natively.
- If you really need rich formatting (bold/italic/inline links), set `format: "markdownv2"` on the reply and escape per MarkdownV2 rules — but for almost everything, plain mode + bare URLs is safer and shorter.
**Smell test before sending:** if a URL in your reply is preceded by `**` or `*`, it's wrong. Strip them.
Related: [[feedback-default-to-telegram-reply]] · [[feedback-telegram-status-emoji]]
Every Telegram reply to Dan leads with a status-emoji prefix:
- 🚨 = urgent / needs attention right now
- 🔴 = blocked or red status
- 🟡 = warning / FYI / partial
- ✅ = done / healthy / completed
- 📊 = data / report / dashboard output
- 🤔 = clarifying question / re-anchor needed
- ⏸ = paused / waiting on Dan / no action
**Why:** Telegram is Dan's primary interface; he reads on phone with 20+ messages stacked. Without prefixes every message looks the same and triage takes minutes. Pattern from Matt Maher video (2026-05-03) — the agent thread list functions as a priority-coded inbox (red/blue/grey dots), not just chat history.
**How to apply:** Pick the closest emoji at the start of every Telegram reply, including short acknowledgements and bridge-restart re-anchors. For mixed status lead with the most-attention-needing one. Default for clarifying questions = 🤔, default for finished work = ✅, default for data answers = 📊.
When Dan asks a data-shaped question ("where are my 26 Stars in the funnel", "show me prospects by tier", "how's the pipeline distributed"), default response = a small self-contained HTML viz attached to the Telegram reply (Sankey / table-with-filters / Gantt / distribution chart) — not a bullet list.
**Why:** The HMG cockpit dashboard is already this pattern; ad-hoc asks deserve the same affordance. Bullets make Dan re-construct shape in his head; viz hands it to him pre-shaped. Pattern from Matt Maher video (2026-05-03) — "transient software": the agent generates UI for the question, not just an answer, and can click its own filter buttons to walk the user through the data.
**How to apply:** Build the viz as a single self-contained HTML file (Chart.js / D3 / vanilla SVG), write to `~/.claude/channels/telegram/outbox/<ISO>-<slug>.html`, send via the Telegram reply tool's `files=[...]`. Keep it minimal — one viz, no nav, fast load. Bullets remain fine for short status answers (≤5 items, no inherent shape) and yes/no questions.
When you need to recall something from Dan's knowledge (a past decision, a firm/person, a preference, "what did we agree about X"), run the local semantic recall tool — don't lean on grep/keyword alone, which misses anything phrased differently than it's stored.
```bash
python3 ~/hmg-ops/scripts/memory_search.py "natural-language question" --k 8
```
It returns **cited** hits (path:line · date · snippet) over the vault + auto-memory + hmg-ops + LOG, matched by meaning. Built + validated 2026-06-25 (Phase 0: semantic found the right note in top-25 9/10 vs grep 3/10).
**Why:** our memory was keyword-bound — a real recall gap. Semantic search closes it.
**How to apply:** reach for `memory_search.py` like you reach for grep, especially when the answer might be stored in different words than the question. Read the top 5–8 cited hits and synthesize — don't trust rank-1 blindly (pairs with [[feedback-verify-before-quoting-state]]). Rebuild the index with `memory_index.py` after big knowledge changes (it can go stale). Full doc: `~/hmg-ops/runbooks/semantic-recall.md`. See also [[reference-scripted-search-vs-websearch]] (different tool, different job — that's web search, this is memory recall).
A new or modified canary/health-check/monitor must be validated against **live ground truth** before its verdict is trusted or acted on. An alarm is a hypothesis, not a fact. A monitor cannot detect a bug in its *own* logic — it will confidently repeat its own mistake — so the only safeguard is an external reality-check.
**Why:** 2026-06-25 — a freshly-written `deliverability_canary.py` fired 🔴 "DKIM broken" because its check wrongly required the M365 selector CNAME to contain `onmicrosoft.com`; modern M365 uses the `dkim.mail.microsoft` format. Dan asked me to use computer-use to "fix" it. Verifying live DNS first showed DKIM was actually fine — selector1 had a valid published key. Acting on the false alarm would have meant a needless production change to email auth + DNS. The new monitoring system didn't just miss this false positive — it *created* it; more canaries = more false-positive surface. Same failure class the research named: a checker with no external grounding is theater. See [[feedback-verify-before-quoting-state]] and [[project-hmg-thin-pool-canary-is-often-a-render-bug]].
**How to apply:**
- When you add or change a canary, run it AND independently confirm its verdict against the real source (DNS, the API, the file, the rendered artifact) — don't trust the canary's own output as proof it works.
- Before acting on ANY 🔴, reality-check the underlying thing once. Never make a production/infra change (DNS, email auth, sends, deploys) on a single unverified alarm
Memory files (BLOCKERS.md, PRIMER.md, etc.) are manually maintained and lag reality. When Dan completes work outside a Claude session — DNS edits in GoDaddy, admin actions in M365, deployments, file changes — nothing auto-updates the files. If I read a stale file and quote it as truth, I propagate yesterday's reality into today's brief.
**Why:** On 2026-04-28 morning brief, I told Dan that B-009 (email auth: SPF/DKIM/DMARC) was still "WAITING ON DAN," based on BLOCKERS.md. Dan correctly pushed back: he'd actually fixed it the previous evening. Yesterday's Claude session had documented the fix in the COMMIT MESSAGE ("B-009 RESOLVED late-day: enabled DKIM in M365 Defender...") but failed to update the BLOCKERS.md file itself. I then read the stale file and propagated stale state into a confident morning brief. Took Dan to catch it.
The system prompt is explicit: "Memory records can become stale over time... Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources." I violated this.
**How to apply:**
1. **Before quoting any blocker as open**, run the cheap verification probe if one exists:
- DNS-related blocker → `python3 -c "import urllib.request, json; ..."` against Google DoH
- File existence → `ls`, `test -f`
- DB populated → query state.sqlite
- API token live → ping the endpoint
- Deployment live → `curl -s
When marking HMG outreach drafts as ready-to-send, "passes the safety filter" is necessary but not sufficient. Always also verify the customer-facing artifacts the email links to are correctly branded per prospect.
**Why:** 2026-05-02 — after a draft regen pass, I declared "0 sendable → 15 sendable" based on `send_daily_batch.py --dry-run` clearing 15 drafts past the filter. Dan asked whether each microsite was customized for its specific firm, and a 30-second curl revealed every PROSPECT-XXX.html on hmg-microsites.vercel.app was branded for a completely different firm than the email's recipient. Carolina Capital's email pointed to a "GenNx360 Capital Partners" page; CVC's pointed to "Halle PassCo Holdings"; etc. If the Monday cron had fired, every clickthrough would have been a credibility kill. Dan: "I shouldn't have had to catch that, that's ridiculous."
**How to apply:** Before any pre-send digest claims drafts are sendable, programmatically fetch a sample of the linked microsite URLs (curl the title tag) and confirm the firm name in the title matches the email recipient's firm. Don't trust prospect-ID continuity across pipeline regenerations and external (Vercel) deployments — verify the actual rendered artifacts. Same principle applies to other linked artifacts: PDFs, calendar links, custom landing pages, attachments. The safety filter only checks email metadata; it doesn't reach the assets the body points to.
Any time Dan shares a video URL (YouTube, Loom, Vimeo, raw mp4, etc.), invoke the `watch` skill. Do not run yt-dlp manually, do not grep VTT files, do not summarize from title alone.
**Why:** /watch handles the full pipeline (download + auto-scaled frame extraction + transcript + visual analysis) and produces frame-aware reports. Manual yt-dlp + transcript-grep skips visual content — the Matt Maher video (2026-05-03) had key details (frames 41 & 55) that only existed in the visual track, not the transcript. Title-only summarization invents content.
**How to apply:** First tool call after a video URL appears in Dan's message = invoke `watch` with the URL. Use the resulting report to answer; don't restate the full report unless asked.
[digest] wrote /mnt/c/Users/grayc/Documents/Obsidian Vault/Claude Brain/hmg/digests/2026-07-29/morning-stack.md (0 steps) [digest] wrote /mnt/c/Users/grayc/Documents/Obsidian Vault/Claude Brain/hmg/digests/2026-07-29/evening-stack.md (7 steps)
[digest] wrote /mnt/c/Users/grayc/Documents/Obsidian Vault/Claude Brain/hmg/digests/2026-07-29/morning-stack.md (0 steps) [digest] wrote /mnt/c/Users/grayc/Documents/Obsidian Vault/Claude Brain/hmg/digests/2026-07-29/evening-stack.md (7 steps)
=== final state.sqlite counts === prospects 354 touches 477 replies 0 bookings 0 blockers 0 decisions 0 metrics_weekly 0
+ mirrored prospects-current.csv → exports/prospect-pipeline.csv + regenerated prospects.json (2927 active records) + regenerated skill-activity.json + regenerated board.html + regenerated playbook.html + regenerated cc.html + regenerated assets.html pushed
[main 6b4e9d65] memory dashboard regenerate 1 file changed, 20 insertions(+), 27 deletions(-) To https://github.com/graychar2425-blip/hmg-dashboard.git 80bcb8b0..6b4e9d65 main -> main Wrote /home/grayclaw/hmg-dashboard/memory.html (153259 bytes) Pushed to dashboard repo (Cloudflare Pages will deploy).
PASS — wrote /home/grayclaw/hmg-ops/canary/2026-07-30.md
B-007 verify failed — staying open. () B-008 verify failed — staying open. () No blockers to resolve. BLOCKERS.md unchanged.
[2026-05-18T08:33:02.486391-05:00] sent 237 chars to 8532298090