← back to Rentv Sheet Enrich
Cody FIX-FIRST: enforce slug-source gate in write_by_contact.py (auto-reject inferred/fabricated slugs, report rejected_inferred count); require slug_source in worker prompts + name the Cody gate as mandatory in WARM-RESUME
8a0aa5406d16abc9191347d902be7405964c0c01 · 2026-08-22 11:33:35 -0700 · Steve Abrams
Files touched
M WARM-RESUME.mdM write_by_contact.py
Diff
commit 8a0aa5406d16abc9191347d902be7405964c0c01
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Aug 22 11:33:35 2026 -0700
Cody FIX-FIRST: enforce slug-source gate in write_by_contact.py (auto-reject inferred/fabricated slugs, report rejected_inferred count); require slug_source in worker prompts + name the Cody gate as mandatory in WARM-RESUME
---
WARM-RESUME.md | 18 ++++++++++++++++++
write_by_contact.py | 21 +++++++++++++++++++--
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/WARM-RESUME.md b/WARM-RESUME.md
index 5fd5ad0..e09670b 100644
--- a/WARM-RESUME.md
+++ b/WARM-RESUME.md
@@ -68,3 +68,21 @@ The server tightens through a session: 10-burst → 3-burst → even 2-burst eve
Fallback is **1 agent at a time**, and include in the prompt: "if a WebSearch returns a
rate-limit error, wait briefly and retry before giving up; WRITE the output file even if some
searches failed." That recovered wb_010 after it died mid-run.
+
+## slug-source gate (Cody FIX-FIRST, 2026-08-22) — MANDATORY
+The fabrication firewall is now enforced in CODE, not just the prompt. Every worker result
+entry MUST carry `slug_source`: one of `"live_title"` (the /in/ slug appeared verbatim in a
+result title naming the person), `"live_snippet"` (appeared in body text), or `"inferred"`
+(model derived it from metadata / a company-slug pattern — NOT read in a result).
+`write_by_contact.py` now REJECTS any contact_li whose `slug_source` is `"inferred"` and
+reports the count as `rejected_inferred` — the exact class the manual Cody gate used to catch
+by hand (e.g. Bryan Shaffer/GSP `/in/ireto`). Add this line to every worker prompt:
+> For each match also set `slug_source` to "live_title"/"live_snippet"/"inferred". If the slug did
+> not literally appear in a WebSearch result, mark it "inferred" (it will be auto-rejected) — never
+> pass an inferred slug as if confirmed.
+Legacy batches without the field still write (backward-compatible), but all NEW runs tag it.
+
+## Cody gate = a NAMED step, not a convention (Cody FIX-FIRST, 2026-08-22)
+Each yoloforever cycle MUST end its enrichment pass with a contrarian (Cody) gate over the
+cycle result + an empirical read-back (coverage delta on the live sheet), before logging +
+rescheduling. Do not skip it because "the writer looks fine" — the gate is the process backstop.
diff --git a/write_by_contact.py b/write_by_contact.py
index 0b6e9a6..5267564 100644
--- a/write_by_contact.py
+++ b/write_by_contact.py
@@ -39,7 +39,20 @@ for r in range(hr + 1, len(rows)):
if k[0] or k[1]:
idx.setdefault(k, []).append(r)
-cells = []; matched = 0; unmatched = []
+# slug-source gate (Cody FIX-FIRST, 2026-08-22): the "only slugs that literally appear in
+# WebSearch results" rule used to live ONLY in the prompt (model judgment). Now enforced in
+# CODE at write time: any contact_li whose slug_source is "inferred" (model derived it from
+# metadata / a company-slug pattern rather than reading it in a result) is REJECTED here —
+# the exact class the manual Cody gate had to catch by hand (e.g. Bryan Shaffer/GSP /in/ireto).
+# Backward-compatible: entries WITHOUT a slug_source field still write (legacy batches), but
+# the going-forward worker prompt always tags it, so inferred slugs auto-drop with zero human.
+def _li_ok(it):
+ src = (it.get("slug_source") or "").strip().lower()
+ if src == "inferred":
+ return False
+ return True
+
+cells = []; matched = 0; unmatched = []; rejected_inferred = []
for it in results:
key = (norm(it.get("company", "")), norm(it.get("name", "")))
targets = idx.get(key)
@@ -49,8 +62,11 @@ for it in results:
if not targets:
unmatched.append(it.get("name", "") + " @ " + it.get("company", "")); continue
matched += 1
+ li_allowed = _li_ok(it)
+ if not li_allowed and (it.get("contact_li") or "").strip():
+ rejected_inferred.append(it.get("name", "") + " @ " + it.get("company", ""))
for r0 in targets:
- li = (it.get("contact_li") or "").strip()
+ li = (it.get("contact_li") or "").strip() if li_allowed else ""
em = (it.get("email") or "").strip()
if li and not cell(r0, LIC):
cells.append({"row0": r0, "col0": LIC, "value": '=HYPERLINK("%s")' % li.replace('"', ""), "formula": True})
@@ -58,4 +74,5 @@ for it in results:
cells.append({"row0": r0, "col0": EM, "value": em})
res = lib.batch_fill(tok, GID, cells)
print(json.dumps({"gid": GID, "matched": matched, "unmatched": len(unmatched),
+ "rejected_inferred": len(rejected_inferred),
"cells_written": res.get("totalUpdatedCells", 0)}))
← b381287 TK-10579 LinkedIn cycle9: 8 searched, 4 Cody-KEEP staged (Su
·
back to Rentv Sheet Enrich
·
yoloforever cycle 1 ledger: +24 LI (coverage 1016/23%); Cody bad2918 →