marketing-mindset 1.6.2 → 1.7.0

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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/bin/cli.js +105 -2
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -82,3 +82,15 @@ npx -y marketing-mindset plan --base 0.03 --lift 0.2 --daily 500 --weeks 4
82
82
  ```
83
83
 
84
84
  The floor from `sample`/`budget` laid out on a calendar: contacts per week, cumulative volume against the floor, the first date the test can honestly be read, the mechanics-only first week, and an honest verdict when the volume cannot reach the floor inside the plan. `--json` for agents. Work with me: https://axelfreeman.com/marketing-engineer.html
85
+
86
+ ## `brief` — the plan as markdown you can send
87
+
88
+ ```
89
+ npx marketing-mindset brief --base 0.03 --lift 0.2 --cpl 4 --daily 500 --start 2026-09-21
90
+ ```
91
+
92
+ One page: the floor in contacts per arm and in total, what reaching it costs at your price per contact,
93
+ the dated read, a week-by-week table with the percentage of the floor covered, the three gates, and the
94
+ kill rule. `--json` returns the same object for an agent or a script.
95
+
96
+ Method, packages and the paid version of this work: https://axelfreeman.com/marketing-engineer.html
package/bin/cli.js CHANGED
@@ -380,6 +380,107 @@ function runPlan(opts) {
380
380
  return 0;
381
381
  }
382
382
 
383
+
384
+ /* brief — the whole plan as one markdown page a client can keep: the floor, what reaching it costs,
385
+ * the dates, the gate for each week and the kill rule. --json returns the same object for an agent. */
386
+ function fmtNum(n) {
387
+ return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
388
+ }
389
+ function addDays(iso, n) {
390
+ const d = new Date(iso + "T00:00:00Z");
391
+ d.setUTCDate(d.getUTCDate() + n);
392
+ return d.toISOString().slice(0, 10);
393
+ }
394
+ function runBrief(opts) {
395
+ const base = Number(opts.base === undefined ? 0 : opts.base);
396
+ const lift = Number(opts.lift === undefined ? 0 : opts.lift);
397
+ if (!isFinite(base) || base <= 0 || base >= 1) {
398
+ console.error("brief: --base must be a rate between 0 and 1 (got: " + JSON.stringify(opts.base) + ")");
399
+ return 1;
400
+ }
401
+ if (!isFinite(lift) || lift <= 0) {
402
+ console.error("brief: --lift must be a positive relative lift, e.g. 0.2 for +20%");
403
+ return 1;
404
+ }
405
+ const cpl = Number(opts.cpl === undefined ? 0 : opts.cpl);
406
+ if (!isFinite(cpl) || cpl <= 0) {
407
+ console.error("brief: --cpl must be a positive cost per contact (got: " + JSON.stringify(opts.cpl) + ")");
408
+ return 1;
409
+ }
410
+ const daily = Number(opts.daily === undefined ? 0 : opts.daily);
411
+ if (opts.daily !== undefined && (!isFinite(daily) || daily <= 0)) {
412
+ console.error("brief: --daily must be a positive number of contacts per day");
413
+ return 1;
414
+ }
415
+ const weeks = opts.weeks === undefined ? 4 : Number(opts.weeks);
416
+ if (!isFinite(weeks) || weeks < 1 || weeks > 52 || Math.floor(weeks) !== weeks) {
417
+ console.error("brief: --weeks must be a whole number between 1 and 52");
418
+ return 1;
419
+ }
420
+ const power = opts.power === undefined ? 0.8 : opts.power;
421
+ const alpha = opts.alpha === undefined ? 0.05 : opts.alpha;
422
+ const start = typeof opts.start === "string" && /^\d{4}-\d{2}-\d{2}$/.test(opts.start)
423
+ ? opts.start : new Date().toISOString().slice(0, 10);
424
+
425
+ const perArm = sampleSize(base, lift, power, alpha);
426
+ const need = perArm * 2;
427
+ const cost = Math.round(need * cpl * 100) / 100;
428
+ const days = daily > 0 ? Math.ceil(need / daily) : null;
429
+ const readDate = days ? addDays(start, days - 1) : null;
430
+ const rows = [];
431
+ for (let w = 1; w <= weeks; w++) {
432
+ const planned = daily > 0 ? daily * 7 : 0;
433
+ const cumulative = planned * w;
434
+ rows.push({
435
+ week: w, start: addDays(start, (w - 1) * 7), end: addDays(start, w * 7 - 1),
436
+ planned_contacts: planned, cumulative,
437
+ against_floor_pct: Math.round(Math.min(100, (cumulative / need) * 100) * 10) / 10
438
+ });
439
+ }
440
+ const gates = [
441
+ "gate 1 (week 1): the tracked event fires on a real lead - if it does not, stop and fix the event, the channel stays closed",
442
+ "gate 2 (50% of floor): counts per arm look like the plan; if one arm is 30% behind, the split is broken, not the offer",
443
+ "gate 3 (read date): read the result at the declared floor or later, never earlier - below the floor the difference is noise"
444
+ ];
445
+ const killRule = "kill if conversions are at or under the floor on the read date; double only when the target rate is hit at or above the floor";
446
+ const result = {
447
+ base_rate: base, target_rate: Math.round(base * (1 + lift) * 10000) / 10000, relative_lift: lift,
448
+ power, alpha, cost_per_contact: cpl, per_arm: perArm, contacts_needed: need,
449
+ cost_to_read_the_test: cost, daily_contacts: daily > 0 ? daily : null,
450
+ days_to_read: days, first_honest_read: readDate, start_date: start, weeks: rows,
451
+ gates, kill_rule: killRule,
452
+ offer: "https://axelfreeman.com/marketing-engineer.html",
453
+ method: "two-proportion floor; same formula as `marketing-mindset sample`"
454
+ };
455
+ if (opts.json) {
456
+ console.log(JSON.stringify(result, null, 2));
457
+ return 0;
458
+ }
459
+ const pct = (x) => (x * 100).toFixed(2);
460
+ console.log("# Campaign brief - the honest test plan\n");
461
+ console.log("Base rate " + pct(base) + "% -> target " + pct(base * (1 + lift)) + "% (relative lift +"
462
+ + (lift * 100).toFixed(0) + "%), power " + power + ", alpha " + alpha);
463
+ console.log("Floor: " + fmtNum(perArm) + " contacts per arm | " + fmtNum(need) + " in total");
464
+ console.log("At $" + cpl + " per contact: $" + fmtNum(cost) + " to read the test");
465
+ if (days) {
466
+ console.log("At " + fmtNum(daily) + " contacts/day: " + days + " days -> first honest read " + readDate);
467
+ } else {
468
+ console.log("Daily volume not given: add --daily to date the read");
469
+ }
470
+ console.log("\n## Weeks");
471
+ console.log("| Week | Dates | Contacts | Cumulative | % of floor |");
472
+ console.log("| --- | --- | --- | --- | --- |");
473
+ rows.forEach((r) => console.log("| " + r.week + " | " + r.start + " .. " + r.end + " | " + fmtNum(r.planned_contacts)
474
+ + " | " + fmtNum(r.cumulative) + " | " + r.against_floor_pct.toFixed(1) + "% |"));
475
+ console.log("\n## Gates");
476
+ gates.forEach((g) => console.log("- " + g));
477
+ console.log("\n## Kill rule");
478
+ console.log("- " + killRule);
479
+ console.log("\nMethod and packages: " + result.offer);
480
+ console.log("Tools: https://axelfreeman.github.io/marketing-mindset/tools/email-test-planner.html");
481
+ return 0;
482
+ }
483
+
383
484
  const cmd = (process.argv[2] || "").toLowerCase();
384
485
  const rest = process.argv.slice(3);
385
486
 
@@ -414,6 +515,8 @@ if (cmd === "sample") {
414
515
  }, null, 2));
415
516
  } else if (cmd === "aeo") {
416
517
  process.exit(runAeo(flags(rest)));
518
+ } else if (cmd === "brief") {
519
+ process.exit(runBrief(flags(rest)));
417
520
  } else if (cmd === "plan") {
418
521
  process.exit(runPlan(flags(rest)));
419
522
  } else if (cmd === "budget") {
@@ -444,12 +547,12 @@ Usage:
444
547
  --base 0.03 --lift 0.2 --power 0.8 --alpha 0.05
445
548
  npx marketing-mindset kill kill rule for a running test
446
549
  --base 0.03 --n 1500 --lift 0.2
550
+ npx marketing-mindset brief the whole plan as markdown you can send to a client
551
+ --base 0.03 --lift 0.2 --cpl 4 --daily 500 [--weeks 4] [--start 2026-09-21] [--json]
447
552
  npx marketing-mindset budget what an honest test costs before you can read it
448
553
  --base 0.03 --lift 0.2 --cpl 4 [--daily 500]
449
554
  npx marketing-mindset plan dated plan: weekly volume, gates, first honest read
450
555
  --base 0.03 --lift 0.2 --daily 500 [--weeks 4] [--start 2026-09-21]
451
- npx marketing-mindset plan dated plan: weekly volume, gates, first honest read
452
- --base 0.03 --lift 0.2 --daily 500 [--weeks 4] [--start 2026-09-21]
453
556
  npx marketing-mindset warmup day-by-day sending ramp with stop rules
454
557
  --start 2026-09-21 --target 200 --days 21 --mailboxes 4
455
558
  npx marketing-mindset jd marketing engineer job description
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "marketing-mindset",
3
- "version": "1.6.2",
4
- "description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|budget|warmup|aeo|jd|sections|read|install.",
3
+ "version": "1.7.0",
4
+ "description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|budget|brief|plan|warmup|aeo|jd|sections|read|install.",
5
5
  "keywords": [
6
6
  "agent-skill",
7
7
  "skill",