Skip to content

Commit 0038c6f

Browse files
committed
feat(cmo): enforce no-emoji/no-exclamation/no-em-dash voice style
1 parent 8775506 commit 0038c6f

2 files changed

Lines changed: 58 additions & 17 deletions

File tree

ops/cmo-automation/scripts/hydrate_approved_queue.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import re
66
import subprocess
7+
import time
78
from datetime import datetime, timezone
89
from pathlib import Path
910
from typing import Callable
@@ -16,6 +17,22 @@
1617

1718
Resolver = Callable[[str | None, str], dict | None]
1819

20+
EMOJI_RE = re.compile(
21+
"["
22+
"\U0001F300-\U0001F5FF"
23+
"\U0001F600-\U0001F64F"
24+
"\U0001F680-\U0001F6FF"
25+
"\U0001F700-\U0001F77F"
26+
"\U0001F780-\U0001F7FF"
27+
"\U0001F800-\U0001F8FF"
28+
"\U0001F900-\U0001F9FF"
29+
"\U0001FA00-\U0001FAFF"
30+
"\U00002700-\U000027BF"
31+
"\U00002600-\U000026FF"
32+
"]+",
33+
flags=re.UNICODE,
34+
)
35+
1936

2037
def load_json(path: Path):
2138
if not path.exists():
@@ -37,11 +54,20 @@ def load_credential_mapping() -> None:
3754
os.environ.update(mapping)
3855

3956

40-
def run_json(cmd: list[str]):
41-
p = subprocess.run(cmd, capture_output=True, text=True)
42-
if p.returncode != 0:
57+
def run_json(cmd: list[str], retries: int = 2):
58+
for attempt in range(retries + 1):
59+
p = subprocess.run(cmd, capture_output=True, text=True)
60+
if p.returncode == 0:
61+
return json.loads(p.stdout)
62+
63+
reset_match = re.search(r"Resets at (\d+)", p.stderr)
64+
if reset_match and attempt < retries:
65+
reset_ts = int(reset_match.group(1))
66+
sleep_for = max(1, reset_ts - int(time.time()) + 1)
67+
time.sleep(min(sleep_for, 30))
68+
continue
69+
4370
raise RuntimeError(f"Command failed: {' '.join(cmd)}\nSTDERR:\n{p.stderr}")
44-
return json.loads(p.stdout)
4571

4672

4773
def normalize_text(text: str, limit: int = 100) -> str:
@@ -51,36 +77,44 @@ def normalize_text(text: str, limit: int = 100) -> str:
5177
return clean[: limit - 1].rstrip() + "…"
5278

5379

80+
def enforce_style(text: str) -> str:
81+
s = text or ""
82+
s = s.replace("!", "")
83+
s = s.replace("—", " ").replace("–", " ")
84+
s = EMOJI_RE.sub("", s)
85+
s = re.sub(r"\s+", " ", s).strip()
86+
return s[:278]
87+
88+
5489
def build_root_text(account: str, role: str) -> str:
5590
ideas = {
56-
"founder": "Building AI products is mostly distribution math + feedback loops. Own both, ship faster.",
91+
"founder": "Building AI products is mostly distribution math plus feedback loops. Own both and ship faster.",
5792
"brand": "Good AI products win when onboarding is instant, outcomes are measurable, and support feels human.",
5893
"product-agent": "Agent workflows improve when memory, orchestration, and evals are designed as one system.",
5994
}
6095
base = ideas.get(role, ideas["brand"])
61-
return f"{base} #{account}"[:278]
96+
return enforce_style(f"{base} #{account}")
6297

6398

6499
def safe_founder_reply(target_user: str, excerpt: str) -> str:
65-
return (
66-
f"@{target_user} Good signal here. I care less about hype and more about repeatable distribution + retention. "
100+
raw = (
101+
f"@{target_user} Good signal. I care less about hype and more about repeatable distribution plus retention. "
67102
f"{excerpt}"
68-
)[:278]
103+
)
104+
return enforce_style(raw)
69105

70106

71107
def build_reply_text(account: str, role: str, target_user: str, source_text: str) -> str:
72108
excerpt = normalize_text(source_text, limit=80)
73109
if role == "founder":
74110
return safe_founder_reply(target_user, excerpt)
75111
if role == "product-agent":
76-
return (
77-
f"@{target_user} Nice thread. Curious what your eval loop looks like once this is in production. "
78-
f"{excerpt}"
79-
)[:278]
80-
return (
81-
f"@{target_user} Solid point. We see the same pattern in shipping: clear UX + measurable outcomes compound. "
82-
f"{excerpt}"
83-
)[:278]
112+
return enforce_style(
113+
f"@{target_user} Useful thread. Curious what your eval loop looks like once this is in production. {excerpt}"
114+
)
115+
return enforce_style(
116+
f"@{target_user} Solid point. We see the same pattern in shipping: clear UX plus measurable outcomes compound. {excerpt}"
117+
)
84118

85119

86120
def contains_denylist(text: str, keywords: list[str]) -> bool:

ops/cmo-automation/tests/test_hydrate_approved_queue.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99

1010

1111
class HydrateApprovedQueueTests(unittest.TestCase):
12+
def test_enforce_style_removes_exclamation_emoji_and_em_dash(self):
13+
raw = "Useful launch 🚀 with fast rollout — now live!"
14+
cleaned = hydrate_approved_queue.enforce_style(raw)
15+
self.assertNotIn("!", cleaned)
16+
self.assertNotIn("🚀", cleaned)
17+
self.assertNotIn("—", cleaned)
18+
1219
def test_hydrate_root_post_adds_text_and_command(self):
1320
action = {"account": "sovren_software", "action": "root_post", "priority": "high"}
1421
policy = {

0 commit comments

Comments
 (0)