@sellable/mcp 0.1.295 → 0.1.297

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.
@@ -97,7 +97,25 @@ export async function getTableRowsMinimal(tableId, options) {
97
97
  return api.get(path);
98
98
  }
99
99
  export function normalizeRowIds(rowIds) {
100
- const rawIds = Array.isArray(rowIds) ? rowIds : rowIds.split(",");
100
+ if (Array.isArray(rowIds)) {
101
+ return rowIds.map((rowId) => rowId.trim()).filter(Boolean);
102
+ }
103
+ const trimmed = rowIds.trim();
104
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
105
+ try {
106
+ const parsed = JSON.parse(trimmed);
107
+ if (Array.isArray(parsed)) {
108
+ return parsed
109
+ .filter((rowId) => typeof rowId === "string")
110
+ .map((rowId) => rowId.trim())
111
+ .filter(Boolean);
112
+ }
113
+ }
114
+ catch {
115
+ // Fall through to comma-splitting for malformed JSON-like input.
116
+ }
117
+ }
118
+ const rawIds = rowIds.split(",");
101
119
  return rawIds.map((rowId) => rowId.trim()).filter(Boolean);
102
120
  }
103
121
  export async function getRows(tableId, rowIds) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.295",
3
+ "version": "0.1.297",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -87,6 +87,9 @@ Required:
87
87
  the brief justifies absence with reference to a specific archived winner.
88
88
  If a candidate uses `two options:`, option `a)` and option `b)` must be
89
89
  separated by a blank line so the CTA scans cleanly on mobile.
90
+ - **Template spacing enforcement.** The Selected Winner and Approved Message
91
+ Template must use real blank lines between beats/sentences. Reject any body
92
+ with 3+ consecutive non-empty prose lines separated only by single newlines.
90
93
  - **All Sellable cleanup rules, including all 8 substance filters from
91
94
  `sellable-cleanup-rules.md`, MUST PASS per candidate.** A candidate that fails
92
95
  any cleanup rule or substance filter is BLOCKED. The Finalizer Pass CANNOT
@@ -237,7 +237,7 @@ headlineICPCriteria })` ->
237
237
  re-run a narrow campaign-scoped `search_signals` call to recover current
238
238
  post rows, or ask the user to promote the desired posts in the UI and then
239
239
  retry `import_leads`.
240
- Start Import approval is the explicit confirmation for this selected-post
240
+ Start Import approval is the explicit confirmation for this selected-post
241
241
  scrape; do not ask for a second yes/no gate between
242
242
  `select_promising_posts` and `import_leads`.
243
243
  - Sales Nav: `get_provider_prompt({ provider: "sales-nav", campaignOfferId,
@@ -387,6 +387,26 @@ templateRevision: "current" })` until the first current-revision generated
387
387
  6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user to
388
388
  approve the generated message before continuing to Settings. Only that
389
389
  approval may move the campaign to Settings / `awaiting-user-greenlight`.
390
+ 7. When approval is given — or auto-approved in YOLO when the Recommendation is
391
+ `approve-message` — you MUST actually mark the reviewed passing rows approved
392
+ by calling `update_cell({ cellId: <approveCellId>, value: true })` for at
393
+ least one generated-message row, preferably two when available (read each
394
+ `approveCellId` from `get_rows`). The campaign UI gates "Next: Settings" on
395
+ at least one row whose `Approved` cell is true:
396
+ moving `currentStep` to `settings`/`awaiting-user-greenlight` with zero
397
+ approved rows desyncs the watch UI, which stays stuck on Generate Messages
398
+ showing "Approve at least one draft to continue." Setting the `Approved`
399
+ flag via `update_cell({ cellId: approveCellId, value: true })` is explicitly
400
+ allowed; the "do not use `update_cell`" rule below applies ONLY to message
401
+ bodies, never to the `Approved` flag. Advance `currentStep` only after the
402
+ approve writes succeed.
403
+ If any Settings/sequence/send/start step call returns
404
+ `error: "approved_message_required"` with
405
+ `requiredAction: "approve_generated_message"`, recover by reading rows with
406
+ `get_rows`, writing `update_cell({ cellId: approveCellId, value: true })`
407
+ for a completed generated-message row, confirming the approved count is at
408
+ least 1, then retrying the blocked action. Do not ignore or route around
409
+ that error with a different currentStep write.
390
410
 
391
411
  **Do NOT hand-write message bodies via `update_cell`.** `update_cell`
392
412
  is reachable for legitimate operator overrides AFTER the tail hands
@@ -435,9 +455,15 @@ strings, which is why Step 16 requires Step 15 to be complete.
435
455
  `maxRevisionRounds` cap.
436
456
  7. On success, keep `currentStep: "auto-execute-messaging"`, show that the
437
457
  first passing generated message is ready in Messages, and ask for approval
438
- before Settings. Only after the user approves the generated message should
439
- `update_campaign({ campaignId, currentStep: "awaiting-user-greenlight" })`
440
- run.
458
+ before Settings. When the user approves (or YOLO auto-approves on an
459
+ `approve-message` recommendation), first write the approval to the rows with
460
+ `update_cell({ cellId: <approveCellId>, value: true })` for at least one
461
+ reviewed passing row, preferably two when available, confirm the table now
462
+ reports `approved >= 1`, and only then run
463
+ `update_campaign({ campaignId, currentStep: "awaiting-user-greenlight" })`.
464
+ Never advance to Settings while the table still reports `approved: 0` — the
465
+ builder UI will refuse to leave Generate Messages and the watch step will
466
+ desync from the headless step.
441
467
 
442
468
  Critique failure modes NEVER escalate. A critic timeout, a total
443
469
  timeout, a budget trip, a fake-proof rejection, or an unsupported-
@@ -470,7 +496,7 @@ Shape:
470
496
  sender to attach. Attach only after the user chooses. In explicit UAT safe
471
497
  mode, use only the safe mock sender; never pick a real sender.
472
498
  7. Call `update_campaign({ campaignId, senderIds: [selectedSenderId],
473
- currentStep: "sequence" })` to attach the sender through the v3 senders
499
+ currentStep: "sequence" })` to attach the sender through the v3 senders
474
500
  route and describe the Sequence view.
475
501
  8. Call `attach_recommended_sequence({ campaignId, currentStep: "send" })`
476
502
  (bind the tier-recommended sequence to the campaign). Explain the sequence
@@ -482,12 +508,12 @@ currentStep: "sequence" })` to attach the sender through the v3 senders
482
508
  `update_campaign({ campaignId, currentStep: "send" })`.
483
509
  9. Surface the `handoff.orientation` string from `auto-execute.yaml` without
484
510
  repeating the watch URL.
485
- 11. Ask the final launch greenlight with the structured question function:
511
+ 10. Ask the final launch greenlight with the structured question function:
486
512
  Start campaign, Review campaign first, or Pause here.
487
- 12. STOP unless the user explicitly chooses Start campaign. Do NOT call
513
+ 11. STOP unless the user explicitly chooses Start campaign. Do NOT call
488
514
  `start_campaign` from Step 16 itself; that belongs only to the Claude
489
515
  greenlight path. The autonomous tail ends here.
490
- 13. Make the credit boundary explicit in the handoff: only the first review
516
+ 12. Make the credit boundary explicit in the handoff: only the first review
491
517
  batch has been enriched/messaged; expanding to more leads requires a
492
518
  separate user instruction.
493
519
 
@@ -558,7 +584,7 @@ runs.
558
584
  | `references/final-handoff-contract.md` | Step 16, and every Claude greenlight turn |
559
585
  | `references/parallel-critique-protocol.md` | Step 15 when `messaging.critique.enabled` is true |
560
586
  | `references/thomas-variant-selection.md` | Step 15 when `messaging.critique.opus.enabled` is true |
561
- | `references/sellable-cleanup-rules.md` | Any critique rewrite, before persisting |
587
+ | `references/sellable-cleanup-rules.md` | Any critique rewrite, before persisting |
562
588
  | `core/auto-execute.yaml` | Start of Step 13, load once; all tail steps read parsed values |
563
589
  | `core/auto-execute.README.md` | When tuning `auto-execute.yaml` knobs or reviewing threshold-trip logs |
564
590
 
@@ -252,6 +252,11 @@ Orchestration requirements:
252
252
  product/problem -> next step. If two adjacent lines could be swapped, deleted,
253
253
  or connected with `anyway` without changing the meaning, the transition is
254
254
  fake and the draft must be revised.
255
+ - pass the template-spacing gate before writing findings. The Selected Winner
256
+ and any Approved Message Template must use actual blank lines between message
257
+ beats/sentences. If there are 3+ consecutive non-empty prose lines separated
258
+ only by single newlines, the draft is blocked until rewritten with double
259
+ newlines.
255
260
  - pass the final-line/PS "so what?" gate before writing findings. The final
256
261
  line must lower commitment, preview a specific next step, answer a
257
262
  trust/category objection, tie proof to buyer value, or be omitted.
@@ -2118,6 +2118,15 @@ signoff is a talk-at shape).
2118
2118
 
2119
2119
  - **each sentence is its own paragraph** — put a blank line between every sentence in the body, even short ones
2120
2120
  - literally: the body should render as a sequence of one-line paragraphs separated by blank lines, not as multi-sentence paragraphs
2121
+ - the approved template must contain the literal blank lines. Do not fake
2122
+ paragraph spacing by putting each sentence on adjacent single-newline lines.
2123
+ If the template or Selected Winner has 3+ consecutive non-empty prose lines
2124
+ with no blank line between them, mark it `revise-message` / `blocked` and
2125
+ rewrite before approval.
2126
+ - bad shape:
2127
+ `saw you in a few conversations about corporate comms, so may be off, but this seemed relevant.\nyour team probably puts a lot of work into getting people on webinars.\nArbor turns those recordings into ready-to-publish content.`
2128
+ - good shape:
2129
+ `saw you in a few conversations about corporate comms, so may be off, but this seemed relevant.\n\nyour team probably puts a lot of work into getting people on webinars.\n\nArbor turns those recordings into ready-to-publish content.`
2121
2130
  - this matches the archived winners (Revvix, Galley, Superposition all paragraph-break per sentence). It gives the reader one idea at a time and lets short sentences breathe
2122
2131
  - mobile scanability is a hard requirement. The winner should read cleanly in a one-thumb scroll on a phone
2123
2132
  - keep most sentences in the **6-14 word** range. If a sentence is above ~18 words or has more than one comma, split it
@@ -2913,6 +2922,7 @@ research → condense → 10 angle agents in parallel → finalizer combines bes
2913
2922
  - Flow skeleton: opener → pain → **what the product IS** → **what it DOES** (one action per line, up to three; prefer short bullets for 3 repeated-subject product actions) → deployment ease → CTA → optional PS
2914
2923
  - A cold reader must be able to state what the product does in one sentence after reading. Require a crisp `Product is an X that does Y` anchor before any action breakdown. Do not gesture at "that chain" / "the stack" / "that work"
2915
2924
  - Blank line between every sentence in the body. The body renders as a sequence of one-line paragraphs separated by blank lines, not as multi-sentence paragraphs. Target 6-10 one-line paragraphs
2925
+ - The approved template must include actual double-newline paragraph breaks. Never approve or return a body with 3+ consecutive non-empty prose lines separated only by single newlines.
2916
2926
  - 5th-grade reading level by default. Cut glue language (handoff, order-to-cash, rip-and-replace). Keep brief-native product/tool names (Shopify, HubSpot)
2917
2927
  - No feature/benefit marketing bullet lists, no "I noticed...", no compliment sandwiches, no em dashes unless the canonical example uses one. Exception: use a short bullet stack when 3 adjacent product actions repeat the same subject pattern and bullets improve mobile readability.
2918
2928
  - No synthesized framing ("at your stage", "in a setup like yours", "running X means...", "X means you're likely...")