mirror of
https://github.com/alirezarezvani/claude-skills.git
synced 2026-08-28 04:24:58 +00:00
fix(linkedin): stop a loose ask match from suppressing pitch-without-ask
Sixth review round. One finding, reproduced first, plus a help-text nit.
The connection branch was fixed earlier this PR to stop scanning a whole note with
the loose ASK_RE, which raised a false premature-ask on "your post on on-call
rotations". The else branch (dm / inmail / followup) had the same defect running
the other way: there a loose match SUPPRESSES a real finding.
pitch language, no ask, neutral text -> warning fires
pitch language, no ask, "on-call rotations" -> warning silently lost
pitch language, no ask, "chat feature" -> warning silently lost
pitch language, no ask, "demo video" -> warning silently lost
Any note containing one of ASK_RE's words anywhere lost its pitch-without-ask
warning even with no ask in it. An ask is now the --ask field or meeting-request
framing in the note - the same test the connection branch uses.
That left ASK_RE referenced nowhere, so it is removed rather than kept as a dead
pattern; its rationale moves into the comment on MEETING_ASK_RE, which now records
both directions the defect ran in.
Verified: the three suppressed cases now fire, and three controls stay quiet - a
real ask in --ask, a meeting-shaped ask in the note, and a note with no pitch at
all.
Also: cadence_planner's argparse description did not mention NO_POSTS_AFFORDABLE,
which shares exit 3 with over-budget. Help text only, no behaviour change.
Not changed, deliberately: _read_input's duplication across 11 scripts (the
self-contained-package convention's accepted cost), and the two different
shapes of bare-list validation between pattern_miner and post_performance_analyzer
- both are correct, and churning one to match the other adds diff without
changing behaviour.
Blocking gates green - compileall, check_paths, check_frontmatter,
check_dual_publish, check_model_freshness, check_skill_names, check_plugin_json
--all, derive_counters --check, smoke_scripts 696/696. Adversarial battery 18/18.
Pinned samples unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BswsZp5zrJWFAGU6KWNA1s
This commit is contained in:
parent
8a16de83c1
commit
81176406e3
2 changed files with 14 additions and 10 deletions
|
|
@ -58,13 +58,13 @@ PITCH_RE = re.compile(
|
|||
r"schedule a (call|demo)|are you the right person|decision[- ]maker|"
|
||||
r"i'?d like to show you|free trial|pricing|proposal)\b", re.I)
|
||||
|
||||
ASK_RE = re.compile(r"\b(call|chat|meeting|demo|coffee|zoom|15 min|30 min|hop on|jump on)\b", re.I)
|
||||
|
||||
# ASK_RE is deliberately loose and is only safe against the --ask field, where every
|
||||
# word is already an ask. Scanning a whole connection note with it would flag "your
|
||||
# post on on-call rotations". This one requires meeting-request framing, so the
|
||||
# premature-ask rule can read the entire note instead of only the --ask field —
|
||||
# without which the rule was bypassable by putting the ask in --reason.
|
||||
# There used to be a loose ASK_RE here (call|chat|meeting|demo|coffee|zoom|...). It
|
||||
# was only ever safe against the --ask field, where every word is already an ask;
|
||||
# against a whole note it matched "your post on on-call rotations". Both branches
|
||||
# below scanned the whole note with it, in opposite directions: the connection branch
|
||||
# raised a false premature-ask, and the else branch SUPPRESSED a real
|
||||
# pitch-without-ask. This pattern requires meeting-request framing, so both can read
|
||||
# the entire note - without which premature-ask was bypassable via --reason.
|
||||
MEETING_ASK_RE = re.compile(
|
||||
r"\b(hop|jump|get)\s+on\s+(a|an|the)?\s*(quick\s+)?(call|chat|zoom|meeting)\b"
|
||||
r"|\b(grab|get)\s+(a\s+)?coffee\b"
|
||||
|
|
@ -142,8 +142,8 @@ def validate(text: str, parts: dict, mtype: str, premium: bool) -> list:
|
|||
"rate drops.",
|
||||
"One specific line, one reason, one bounded ask. Everything else is for the reply.")
|
||||
|
||||
ask = (parts.get("ask") or "").strip()
|
||||
if mtype == "connection":
|
||||
ask = (parts.get("ask") or "").strip()
|
||||
# Read the assembled note, not just the --ask field: the same ask moved into
|
||||
# --reason or --specific-line used to pass clean, which made the documented
|
||||
# "refuses an ask in a first-touch note" guarantee bypassable.
|
||||
|
|
@ -160,7 +160,10 @@ def validate(text: str, parts: dict, mtype: str, premium: bool) -> list:
|
|||
"Delete it. Nobody has ever bought from a connection request, and the request "
|
||||
"is the only impression you get.")
|
||||
else:
|
||||
if PITCH_RE.search(low) and not ASK_RE.search(low):
|
||||
# An ask is the --ask field, or meeting-request framing in the note. Matching
|
||||
# loosely here silently lost the warning on any note that happened to contain
|
||||
# "on-call", "chat feature" or "demo video". See MEETING_ASK_RE above.
|
||||
if PITCH_RE.search(low) and not (ask or MEETING_ASK_RE.search(low)):
|
||||
add("warning", "pitch-without-ask",
|
||||
"Product language with no clear, bounded ask.",
|
||||
"Either make the ask explicit and small, or cut the product language.")
|
||||
|
|
|
|||
|
|
@ -225,7 +225,8 @@ def render_human(r: dict) -> str:
|
|||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Size a sustainable LinkedIn week (fits=0 / below-floor=2 / over-budget=3).")
|
||||
description="Size a sustainable LinkedIn week "
|
||||
"(fits=0 / below-floor=2 / over-budget or no-posts-affordable=3).")
|
||||
ap.add_argument("--minutes", type=int, help="Minutes per week you will actually protect.")
|
||||
ap.add_argument("--stage", choices=sorted(STAGES), default="starting")
|
||||
ap.add_argument("--target-posts", type=int, default=0,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue