@vruum/skills 0.6.29 → 0.6.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vruum",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
4
4
|
"description": "Vruum AI skills + remote MCP server for B2B GTM teams. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vruum AI",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vruum",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
4
4
|
"description": "Vruum AI skills + remote MCP server for B2B GTM teams. Skills for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vruum AI",
|
|
@@ -100,9 +100,9 @@ Use these tools when:
|
|
|
100
100
|
- You're rewriting a message and need a real, specific hook
|
|
101
101
|
- The prospect's LinkedIn posts field is null and you want to find recent activity
|
|
102
102
|
|
|
103
|
-
### MANDATORY web search for follow-ups
|
|
103
|
+
### MANDATORY web search for follow-ups and fallback openers
|
|
104
104
|
|
|
105
|
-
For ANY follow-up
|
|
105
|
+
For ANY message that is a real follow-up (conversation_state.first_content_touch=false; when the block is absent, sequence_number >= 2) OR a first content touch riding a higher T-number (first_content_touch=true with sequence_number >= 2 — e.g. an email that fell back from a never-accepted LinkedIn connect; these need full T1-style opener research, not dedup review), web search is REQUIRED before making a decision. Cached person/company research payloads (`fetch` type=person_research / type=company_research) are often weeks or months old and miss recent signals (acquisitions, role changes, new posts, new reqs, funding, layoffs). You MUST run at least one WebSearch query on the prospect + company before approving or editing a T2+ message.
|
|
106
106
|
|
|
107
107
|
What to search for:
|
|
108
108
|
- "{Person Name} {Company}" — surfaces recent LinkedIn posts, interviews, podcast appearances
|
|
@@ -111,15 +111,16 @@ What to search for:
|
|
|
111
111
|
|
|
112
112
|
If web search surfaces nothing useful, note that in RESEARCH_DONE ("web search: no material new signal") so the operator knows you checked. Never skip the search and claim cached context was sufficient.
|
|
113
113
|
|
|
114
|
-
Exception: T1 initials (blank connection requests or first-touch sends) don't require web search. Fit-rejection calls (where the prospect obviously doesn't match ICP from cached data) don't require web search — but state that explicitly in REASONING.
|
|
114
|
+
Exception: T1 initials (blank connection requests or first-touch sends at sequence_number = 1) don't require web search. Fit-rejection calls (where the prospect obviously doesn't match ICP from cached data) don't require web search — but state that explicitly in REASONING.
|
|
115
115
|
|
|
116
116
|
For T1 structural reviews and other cases, the existing "use when needed" rule applies.
|
|
117
117
|
|
|
118
118
|
## Step 4: Edit if needed
|
|
119
119
|
|
|
120
|
-
If the message needs changes, rewrite it, check the rewrite with `check_prose` (`{item_id: <message id>, item_type: "message", content: <rewrite>}` — the `failures[]` are an advisory checklist to consider, not a pass/fail loop: fix what you agree with;
|
|
120
|
+
If the message needs changes, rewrite it, check the rewrite with `check_prose` (`{item_id: <message id>, item_type: "message", content: <rewrite>}` — the `failures[]` are an advisory checklist to consider, not a pass/fail loop: fix what you agree with; severity `block` failures are hard stops — channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener; the failure `span` names the offending text)), then apply the edit using `manage_messages` with action=edit, the message id, and payload={subject?, content, client_rules_version: <rules_version from check_prose>}. The edit re-runs the same lint server-side; annotations are recorded to the label corpus, never rejected — a `block`-severity draft bounces (`prose_gate_blocked` with `failures[].fix`).
|
|
121
121
|
|
|
122
122
|
When rewriting:
|
|
123
|
+
- Frame by conversation state: first_content_touch=true → OPENER (never reference prior outreach); prior_context='note_only' → don't repeat the connection_note_text; prior_context='inbound_only' → continue THEIR conversation; channel_fallback=true → the copy must stand alone on the item's actual channel. Block absent → T-number heuristic. If prior contact happened outside Vruum, flag for the operator instead of resubmitting.
|
|
123
124
|
- Keep the same strategic intent (don't change a T2 into a T4)
|
|
124
125
|
- Follow the segment tone instructions exactly
|
|
125
126
|
- Stay within word/character limits
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vruum/skills",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
4
4
|
"description": "Vruum AI skills for Claude Code, Claude Desktop, Codex CLI, and any AI assistant with a skill directory. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis. Pairs with the Vruum MCP server at https://api.vruum.ai/mcp.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"outreach",
|
|
43
43
|
"gtm"
|
|
44
44
|
],
|
|
45
|
-
"contentHash": "
|
|
45
|
+
"contentHash": "edb317856c2825036b1d3d8017b14b908386a977b3d0f05efc61e0db0cee7762"
|
|
46
46
|
}
|
|
@@ -30,21 +30,41 @@ For small queues (5 or fewer) or when subagents can't access MCP, review directl
|
|
|
30
30
|
|
|
31
31
|
Call `fetch` with type=stats and subtype=outreach to see the pending queue shape. The response carries `needs_draft_count` (unauthored touches awaiting authoring) alongside `draft_count` (authored, awaiting approval) — surface both so the authoring backlog is visible up front. Present a quick summary:
|
|
32
32
|
|
|
33
|
-
"You have N to author (needs_draft), X reply responses, Y pending T1s, Z T2+ follow-ups. [Any critical alerts.] Want me to run full triage or focus on a specific category?"
|
|
33
|
+
"You have N to author (needs_draft, W of them WARM accepted-connection follow-ups), X reply responses, Y pending T1s, Z T2+ follow-ups. [Any critical alerts.] Want me to run full triage or focus on a specific category?"
|
|
34
|
+
|
|
35
|
+
**Warm follow-ups outrank everything except replies.** The stats payload carries `needs_draft_warm_count`: unauthored `linkedin_message` touches to people who ACCEPTED the connection request. These are the highest-EV rows in the queue — a person who said yes to the invite is waiting on a first real message. When non-zero, lead the summary with it and default the triage order to: inbound replies → warm follow-ups → everything else.
|
|
34
36
|
|
|
35
37
|
Keep it short. The user knows their queue — they just need the numbers to decide what to prioritize.
|
|
36
38
|
|
|
39
|
+
**needs_draft items EXPIRE.** A nightly backend sweep (03:20 UTC) rejects any `needs_draft` row older than 14 days from creation — intended garbage collection, not an operator action. An expired touch is not lost forever (the plan reschedules and mints a fresh row about a week later), but the authoring work is deferred a cycle and the queue silently shrinks. The tools tell you: the stats payload carries `needs_draft_expiring_soon_count` (rows within 3 days of the sweep — surface it in the summary when non-zero), and each needs_draft item carries `expires_at` (its sweep deadline). Consequence for triage: author oldest-first, and if the queue is too big to clear in one session, clear the items closest to `expires_at` rather than sampling the freshest.
|
|
40
|
+
|
|
37
41
|
### Step 2: Build the dispatch list and categorize
|
|
38
42
|
|
|
39
|
-
Once the user says go (or picks a focus area), pull the lightweight message queue via `search` with type=messages, `fields=compact` and limit=100 — make
|
|
43
|
+
Once the user says go (or picks a focus area), pull the lightweight message queue via `search` with type=messages, `fields=compact` and limit=100 — make THREE cheap calls: `warm_only=true, status=needs_draft, sort_by=expiring` (the WARM authoring lane — LinkedIn follow-ups to accepted connections, nearest deadline first), `status=needs_draft` (the full authoring lane; warm rows appear here too — dedupe by message_id, warm lane wins), and `status=draft` (the review lane). `fields=compact` returns message_id, person_name, category, sequence_number, channel, status, match_score, touches_completed, campaign_id, first_content_touch, and channel_rewrite_reason WITHOUT message content — very cheap on tokens. Tag each item with its status so dispatch routes it to the right mode: `needs_draft` → authoring, `draft` → review. (Omitting the status filter returns the default actionable set — needs_draft + draft + approved — but pull the two lanes explicitly so already-approved messages awaiting send don't enter triage.)
|
|
44
|
+
|
|
45
|
+
**Frame by conversation state, not sequence number.** `first_content_touch=true` means this person has NEVER received a content-bearing message — their history is only connection requests (blank or noted). That item is an OPENER no matter its T-number (a T3 email that fell back from a never-accepted LinkedIn connect is still the first thing this person reads from the sender). `first_content_touch=false` means real prior contact exists — follow-up framing is legitimate. If the field is null/absent (rows drafted before this signal shipped, or an older backend), fall back to the sequence_number heuristic: seq 1 = opener, seq ≥2 = follow-up. The full block lives on the `get_outreach_review` item as `conversation_state` (`{v, first_content_touch, prior_context: none|note_only|inbound_only|content, connection_note_text, last_delivered_channel, designed_channel, channel_rewrite_reason, channel_fallback}`) — same absent-block fallback applies when `v` is unrecognized.
|
|
46
|
+
|
|
47
|
+
**Authoring mode (needs_draft items).** The backend no longer writes outreach prose — touches arrive as `needs_draft` items carrying the decision context (channel, touch number, signals) and no content. These are not rewrites; they are blank pages. For each needs_draft item the subagent AUTHORS the message: check the person's research freshness from the review item itself — `person_researched_at` / `company_researched_at` / `research_status` are on the payload, no extra fetch needed (older than ~14 days or missing → research first with WebSearch + the research reads, and persist what you learn via `research` action=save_person, plus action=save_company when you learned something about the company, so it compounds), then write the touch from scratch in the seller's voice against the same quality standards as any review, then check it — call `check_prose` with `{item_id: <message id>, item_type: "message", content: <draft>}` and treat the `failures[]` as an advisory checklist: fix what you agree with; severity `block` failures are hard stops that bounce at post time — today: channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener) — then submit it via `manage_messages` action=edit with the content and the returned `rules_version` as `client_rules_version` — that transitions the item to a normal draft — and approve only what the user's standing instructions allow. (The edit re-runs the same lint server-side; annotations are recorded to the label corpus, never rejected — a `block`-severity draft bounces with `prose_gate_blocked` + `failures[].fix` and a `span` naming the exact offending text.) Inbound replies also arrive as needs_draft (category inbound_reply, with the conversation attached): author the reply with full thread context. If a prospect turns out to be a bad fit at authoring time, skip the item and say why — authoring is the second qualification gate, not an obligation to write.
|
|
48
|
+
|
|
49
|
+
**Dispatch order within the authoring lane.** Pull the WARM lane directly — `search` type=messages with `warm_only=true, status=needs_draft, sort_by=expiring, fields=compact` returns exactly the LinkedIn follow-ups to accepted connections, nearest sweep deadline first, immune to page-1 truncation (warm rows are old by construction, so the default newest-first sort buries them under the cold backlog). Dispatch that lane first — they accepted the invite and the follow-up is the first real message they read. For the remaining lanes, compact rows carry `connection_status` if you need to spot stragglers, and `sort_by=expiring` works on any needs_draft pull.
|
|
50
|
+
|
|
51
|
+
**Expiry and recovery.** The 14-day sweep (see Step 1) means two things at authoring time:
|
|
52
|
+
- Order the authoring lane by `expires_at` (each needs_draft item carries it) — nearest deadline first, warm bucket before cold.
|
|
53
|
+
- An expired touch shows as `rejected` with BLANK content and `ai_decision_context.expired_signal` (reason `needs_draft_ttl_expired`) — GC, not a human rejection; do not read it as an operator verdict when diagnosing a campaign. (Rows expired before the marker shipped lack it — the tell there is blank content + `updated_at` near 03:20 UTC.) To revive one, call `manage_messages` action=`regenerate` (returns it to `needs_draft`), then author and `edit` as normal. Do NOT use `draft_followup` to recover an expired touch — it creates a NEW touch row instead of reviving the existing one.
|
|
40
54
|
|
|
41
|
-
**
|
|
55
|
+
**Conversation-state authoring rules** (from the item's `conversation_state`; skip when the block is absent):
|
|
56
|
+
- `first_content_touch=true` → write an OPENER: never reference prior outreach ("my last note", "since I haven't heard back", "bumping this" all bounce at the gate). Referencing THEIR world — posts, news, role — is what openers are made of.
|
|
57
|
+
- `channel_fallback=true` → the step was designed for `designed_channel` (usually a LinkedIn DM that never got the connection accept); the campaign's per-step instructions may describe the wrong channel. Always author for the item's actual `channel`, and make the copy stand alone there.
|
|
58
|
+
- `prior_context='note_only'` → the person got a connection request WITH a note (`connection_note_text` has the text — they may have read it). Don't parrot or duplicate it; referencing the connection event itself ("thanks for connecting") is fine once connected.
|
|
59
|
+
- `prior_context='inbound_only'` → the person has messaged the sender but never received content from them. Continue THEIR conversation — referencing their message is right; referencing "my previous outreach" is not.
|
|
60
|
+
- The block is frozen at draft time: if the item's `conversation_thread` shows an inbound message or delivered content newer than the draft, trust the thread over the block.
|
|
61
|
+
- If you believe prior contact happened outside Vruum (a call, a meeting, another mailbox), don't fight the gate — flag it to the user; an operator override is required.
|
|
42
62
|
|
|
43
63
|
Categorize into three processing groups:
|
|
44
64
|
|
|
45
|
-
1. **Reply responses** (category=reply_response) — someone replied, always P1, always human review
|
|
46
|
-
2. **Follow-ups** (sequence_number >= 2) — need research and quality check
|
|
47
|
-
3. **
|
|
65
|
+
1. **Reply responses** (category=reply_response or inbound_reply) — someone replied, always P1, always human review
|
|
66
|
+
2. **Follow-ups** (first_content_touch=false; fallback when null: sequence_number >= 2) — need research and quality check
|
|
67
|
+
3. **Openers** (first_content_touch=true; fallback when null: sequence_number = 1) — first real message regardless of T-number; full opener research standards, never "follow-up" framing
|
|
48
68
|
|
|
49
69
|
Present the queue composition before dispatching. For the counts in one call, use `search` with type=messages and `view=breakdown` — it returns grouped counts (by status, category, sequence/touch number, channel, and campaign) over the whole queue plus a compact items page, so you don't have to tally the pages yourself:
|
|
50
70
|
|
|
@@ -98,10 +118,10 @@ For each message:
|
|
|
98
118
|
4. Rate personalization depth (surface/basic/deep)
|
|
99
119
|
5. Check strategic fit (CTA matches stage, moves conversation forward)
|
|
100
120
|
|
|
101
|
-
If a message needs fixes, run the rewrite through check_prose ({item_id: <message id>, item_type: "message", content: <rewrite>}) and weigh the failures[] as an advisory checklist — fix what you agree with;
|
|
121
|
+
If a message needs fixes, run the rewrite through check_prose ({item_id: <message id>, item_type: "message", content: <rewrite>}) and weigh the failures[] as an advisory checklist — fix what you agree with; severity "block" failures are hard stops — today: channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener; the failure span names the offending text) — then edit it via manage_messages with action=edit, passing the returned rules_version as client_rules_version. If personalization is weak, use search with type=kb to find better hooks.
|
|
102
122
|
|
|
103
123
|
Return a structured summary per message:
|
|
104
|
-
MESSAGE: {id} | PERSON: {name} | MATCH_SCORE: {n} | CATEGORY: T{n} | RECOMMENDATION: {approve|edited|flag|reject} | CONFIDENCE: {high|medium|low} | REASONING: {1-2 sentences} | EDITED: {yes/no} | ISSUES_FOUND: {list or "none"}
|
|
124
|
+
MESSAGE: {id} | PERSON: {name} | MATCH_SCORE: {n} | CATEGORY: T{n} ({opener|followup} per first_content_touch, fallback seq#) | RECOMMENDATION: {approve|edited|flag|reject} | CONFIDENCE: {high|medium|low} | REASONING: {1-2 sentences} | EDITED: {yes/no} | ISSUES_FOUND: {list or "none"}
|
|
105
125
|
|
|
106
126
|
{user_notes}
|
|
107
127
|
```
|
|
@@ -113,7 +133,7 @@ You are a prospect research and outreach review agent.
|
|
|
113
133
|
|
|
114
134
|
Message ID: {message_id}
|
|
115
135
|
Prospect: {person_name}, {title} at {company}
|
|
116
|
-
Message type: T{sequence_number} follow-up
|
|
136
|
+
Message type: T{sequence_number} — frame by the item's conversation_state, NOT the T-number: first_content_touch=true means this is an OPENER (their history is only connection requests; never reference prior outreach); false means a real follow-up; block absent → treat T{sequence_number}>=2 as follow-up
|
|
117
137
|
|
|
118
138
|
Steps:
|
|
119
139
|
1. Call get_outreach_review with message_ids="{message_id}" and content_length="full" to get the current message, thread context, campaign instructions, and match analysis.
|
|
@@ -128,10 +148,10 @@ Review the message against what you learned:
|
|
|
128
148
|
- Is the personalization based on real, verified information?
|
|
129
149
|
- Are there AI tells, cross-touch duplication, or structural issues?
|
|
130
150
|
|
|
131
|
-
If the message is good as-is, approve it. If there is clear opportunity to improve (weak personalization when rich signals exist, fabricated references, wrong framing), run the rewrite through check_prose ({item_id: <message id>, item_type: "message", content: <rewrite>}) and weigh the failures[] as an advisory checklist — fix what you agree with;
|
|
151
|
+
If the message is good as-is, approve it. If there is clear opportunity to improve (weak personalization when rich signals exist, fabricated references, wrong framing), run the rewrite through check_prose ({item_id: <message id>, item_type: "message", content: <rewrite>}) and weigh the failures[] as an advisory checklist — fix what you agree with; severity "block" failures are hard stops — today: channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener; the failure span names the offending text) — then edit it via manage_messages with action=edit, passing the returned rules_version as client_rules_version. Do NOT rewrite messages that are already solid just because you can.
|
|
132
152
|
|
|
133
153
|
Return a structured summary:
|
|
134
|
-
MESSAGE: {id} | PERSON: {name} | MATCH_SCORE: {n} | CATEGORY: T{n} | RECOMMENDATION: {approve|edited|flag|reject} | CONFIDENCE: {high|medium|low} | REASONING: {1-2 sentences} | EDITED: {yes/no} | ISSUES_FOUND: {list or "none"} | RESEARCH_SUMMARY: {2-3 sentences on what you found} | PROBLEM_IDENTIFIED: {yes/no/speculative} | REWRITE_REASON: {why you edited, or "n/a"}
|
|
154
|
+
MESSAGE: {id} | PERSON: {name} | MATCH_SCORE: {n} | CATEGORY: T{n} ({opener|followup} per first_content_touch, fallback seq#) | RECOMMENDATION: {approve|edited|flag|reject} | CONFIDENCE: {high|medium|low} | REASONING: {1-2 sentences} | EDITED: {yes/no} | ISSUES_FOUND: {list or "none"} | RESEARCH_SUMMARY: {2-3 sentences on what you found} | PROBLEM_IDENTIFIED: {yes/no/speculative} | REWRITE_REASON: {why you edited, or "n/a"}
|
|
135
155
|
|
|
136
156
|
{user_notes}
|
|
137
157
|
```
|
|
@@ -4,7 +4,9 @@ You are reviewing and improving outreach messages before they go to a human oper
|
|
|
4
4
|
|
|
5
5
|
You do NOT approve or send messages. You review, edit if needed, and return a structured summary.
|
|
6
6
|
|
|
7
|
-
**needs_draft items are authoring jobs, not reviews.** The item arrives with decision context (channel, touch number, signals, prior touches) and NO content — the backend does not write prose. Author the message from scratch: verify research freshness first — the review item carries `person_researched_at`, `company_researched_at`, and `research_status`, so no extra fetch is needed (missing or >14 days stale → research with WebSearch and the research reads, persist findings via `research` action=save_person, and action=save_company for company-level findings), then write the touch in the seller's voice to the same standards below, check it with `check_prose` (`{item_id: <message id>, item_type: "message", content: <draft>}` — the `failures[]` are an advisory checklist: fix what you agree with;
|
|
7
|
+
**needs_draft items are authoring jobs, not reviews.** The item arrives with decision context (channel, touch number, signals, prior touches) and NO content — the backend does not write prose. Author the message from scratch: verify research freshness first — the review item carries `person_researched_at`, `company_researched_at`, and `research_status`, so no extra fetch is needed (missing or >14 days stale → research with WebSearch and the research reads, persist findings via `research` action=save_person, and action=save_company for company-level findings), then write the touch in the seller's voice to the same standards below, check it with `check_prose` (`{item_id: <message id>, item_type: "message", content: <draft>}` — the `failures[]` are an advisory checklist: fix what you agree with; severity `block` failures are hard stops — channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener)), and submit it with `manage_messages` action=edit (content + the returned `rules_version` as `client_rules_version`) — that turns it into a normal draft for the orchestrator's approval flow. If the prospect is a bad fit on fresh evidence, recommend skip with a one-line `reason` instead of writing (reasons feed the prose_labels corpus).
|
|
8
|
+
|
|
9
|
+
**Frame by conversation state, not touch number.** The review item's `conversation_state` block (when present) is the truth about what this person has actually received: `first_content_touch=true` means their entire history is connection requests — the message is an OPENER whatever its T-number; never reference prior outreach ("my last note", "since I haven't heard back", "bumping this" bounce at the gate). `channel_fallback=true` means the step was designed for `designed_channel` — author for the item's actual `channel` and make the copy stand alone. `prior_context='note_only'` → don't repeat the `connection_note_text`; `prior_context='inbound_only'` → continue THEIR conversation (their message is referenceable; "my previous outreach" is not). The block is frozen at draft time — if the conversation_thread shows newer inbound or delivered content, trust the thread. Block absent or `v` unrecognized → fall back to the T-number heuristic. If prior contact happened outside Vruum, flag it to the user instead of resubmitting — the gate override is restricted and your resubmission will bounce again.
|
|
8
10
|
|
|
9
11
|
## Step 1: Load your messages
|
|
10
12
|
|
|
@@ -93,7 +95,7 @@ Do NOT use these tools for every message. Only when the draft needs improvement
|
|
|
93
95
|
|
|
94
96
|
## Step 4: Edit if needed
|
|
95
97
|
|
|
96
|
-
If the message needs changes, rewrite it, check the rewrite with `check_prose` (`{item_id: <message id>, item_type: "message", content: <rewrite>}` — the server loads the item's real context for exact parity with the submission gate; the `failures[]` are an advisory checklist to consider, not a pass/fail loop — fix what you agree with
|
|
98
|
+
If the message needs changes, rewrite it, check the rewrite with `check_prose` (`{item_id: <message id>, item_type: "message", content: <rewrite>}` — the server loads the item's real context for exact parity with the submission gate; the `failures[]` are an advisory checklist to consider, not a pass/fail loop — fix what you agree with; severity `block` failures are hard stops: cut to fit on a channel character cap, and rewrite as an opener if a prior-outreach reference fires on a first content touch — the failure `span` names the offending text), then apply the edit using `manage_messages` with action=edit, the message id, the new content, and the returned `rules_version` as `client_rules_version` in the payload. The edit re-runs the same lint server-side; annotations are recorded to the label corpus, never rejected — a `block`-severity draft bounces (`prose_gate_blocked` with `failures[].fix` and `span`).
|
|
97
99
|
|
|
98
100
|
When rewriting:
|
|
99
101
|
- Keep the same strategic intent (don't change a T2 into a T4)
|
|
@@ -114,7 +116,7 @@ After reviewing all messages in your batch, return a summary in this exact forma
|
|
|
114
116
|
MESSAGE: {message_id}
|
|
115
117
|
PERSON: {person_name} ({person_title} at {person_company})
|
|
116
118
|
MATCH_SCORE: {number}
|
|
117
|
-
CATEGORY: {
|
|
119
|
+
CATEGORY: {opener/followup/reply_response} T{touch_number} — opener vs followup comes from the item's conversation_state.first_content_touch (true = opener regardless of T-number; block absent = fall back to T1=opener, T2+=followup)
|
|
118
120
|
RECOMMENDATION: {approve | edited | flag | reject}
|
|
119
121
|
CONFIDENCE: {high | medium | low}
|
|
120
122
|
REASONING: {1-2 sentences explaining your decision}
|