From 2e0b28f2a2b0d5f3b158b95fcc954af3dde1754d Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Tue, 27 Oct 2026 19:49:57 -0400 Subject: [PATCH] Competitor scan always re-runs; report numbers refresh when the report is rewritten - Skill: a "Find competitors" click always does the full scan and rewrites this month's report (the run had skipped because one already existed) - Frozen .data.json is reused only while the .md is older, so a new run's report gets fresh numbers without deleting files by hand Co-Authored-By: Claude Opus 5.5 --- .claude/skills/competitor-scan/SKILL.md | 2 ++ tools/report-pdf/blocks.mjs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.claude/skills/competitor-scan/SKILL.md b/.claude/skills/competitor-scan/SKILL.md index d434ec0..aa1a32e 100644 --- a/.claude/skills/competitor-scan/SKILL.md +++ b/.claude/skills/competitor-scan/SKILL.md @@ -5,6 +5,8 @@ description: Find a client's real competitors (local rivals and niche benchmarks Goal: tell the client who they're actually up against, who's winning and *why*, and the 3–5 openings nobody in their market is using. Numbers come from Instagram via the tools, never from memory or guesses. +**Always run the full scan, even if this month's report already exists.** Someone started this run on purpose (new data sources, tracked accounts changed, or the last report was wrong). Use the old report only as a starting point, then rewrite `competitors-YYYY-MM.md` in place and re-export the PDF. Re-suggesting an account doesn't duplicate it: `competitor_suggest` replaces the existing suggestion and skips tracked or dismissed accounts. + 1. **Know the client.** Read `clients//client.json`, `brand.md`, `goals.md`, `constraints.md` and any `audit-*` file. Note: niche, exact offer, city/area (and radius that matters: a clinic competes within ~10 km, an online brand nationally), price level, ideal customer. Call `competitors` to see who's already tracked, so you don't suggest them again. 2. **Search for candidates** (8–15 searches in total, no more): diff --git a/tools/report-pdf/blocks.mjs b/tools/report-pdf/blocks.mjs index fb1639f..ccfeb23 100644 --- a/tools/report-pdf/blocks.mjs +++ b/tools/report-pdf/blocks.mjs @@ -1,6 +1,7 @@ // Visual report blocks: the AI writes small fenced markers, this file draws them as inline SVG/HTML. // Charts are drawn from the stored account numbers (never from figures typed by the AI) and frozen -// next to the report as .data.json the first time it renders, so a re-export shows the same numbers. +// next to the report as .data.json the first time it renders, so a re-export shows the same numbers +// (until the .md is rewritten, which takes a fresh copy). // // Data blocks: ```mc:kpis ```mc:reach ```mc:funnel ```mc:followers ```mc:formats ```mc:rhythm ```mc:top-posts // Written blocks: ```mc:score (Area | 6/10 | why) ```mc:actions (Action | Owner | When) @@ -90,7 +91,8 @@ function competitionData(slug, snap) { export function reportData(mdPath) { const frozen = resolve(mdPath).replace(/\.md$/i, ".data.json"); - if (existsSync(frozen)) return readJson(frozen); + // Frozen until the report itself is rewritten (a new run or an edit): then its text and numbers move together. + if (existsSync(frozen) && statSync(frozen).mtimeMs >= statSync(mdPath).mtimeMs) return readJson(frozen); const slug = slugFor(mdPath); const data = slug && freshData(slug); if (data) try { writeFileSync(frozen, JSON.stringify(data)); } catch { /* read-only folder: render live */ }