marketing-mindset 1.9.0 → 1.10.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 +19 -0
  2. package/bin/cli.js +61 -0
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -137,3 +137,22 @@ difference, and decides: not readable (the interval includes zero), a lead rathe
137
137
  but below the per-arm volume the observed lift would need), or readable. `--alpha` takes the same three
138
138
  values as `sample` (0.05, 0.01, 0.1) and the floor is computed with the same formula, so the answer stays
139
139
  consistent with the plan that produced the test.
140
+
141
+ ## `queue` — how many honest tests your volume can finish
142
+
143
+ Ideas are not the constraint. At a 3% base rate and a +20% relative lift a strict two-arm test needs 13,914
144
+ contacts per arm, so the number of tests a quarter can hold is set by the volume, not the backlog. `queue`
145
+ turns that volume into a dated queue.
146
+
147
+ ```bash
148
+ npx marketing-mindset queue --daily 500 --days 90 --start 2026-09-21
149
+ # floor: 13,914 contacts per arm (27,828 in both arms) at a 3.00% base rate, +20% relative lift, power 0.8, alpha 0.05
150
+ # one honest test: 56 days at 500 contacts a day
151
+ # queue: 1 test in 90 days (62% of the horizon inside a test, 34 days outside one)
152
+ # read-out dates: 2026-11-16
153
+ ```
154
+
155
+ `--json` returns the same object: `floor_per_arm`, `total_contacts`, `days_per_test`, `tests_in_horizon`,
156
+ `read_dates`, `idle_days`, `used_pct` and the three gates. The floor uses the same formula as `sample`, so a
157
+ queue planned here and a test read with `verdict` stay consistent. Two things it refuses to hide: the read-out
158
+ is one look (peeking daily is not covered by alpha), and two live tests in the same arm split the volume.
package/bin/cli.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * marketing-mindset kill the kill rule for a running test
6
6
  * marketing-mindset mde smallest relative lift a fixed per-arm volume can read
7
7
  * marketing-mindset verdict read a finished test: interval, floor, honest verdict
8
+ * marketing-mindset queue volume into a queue of honest tests with read-out dates
8
9
  * marketing-mindset sections list every section of SKILL.md
9
10
  * marketing-mindset read <text> print one section by fuzzy title match
10
11
  * marketing-mindset install copy the skill into your agent's skills directory
@@ -510,6 +511,62 @@ function runVerdict(opts) {
510
511
  return 0;
511
512
  }
512
513
 
514
+ function qAddDays(fromIso, days) {
515
+ const d = new Date(fromIso + "T00:00:00Z");
516
+ d.setUTCDate(d.getUTCDate() + days);
517
+ return d.toISOString().slice(0, 10);
518
+ }
519
+
520
+ function runQueue(opts) {
521
+ const base = Number(opts.base !== undefined ? opts.base : 0.03);
522
+ const lift = Number(opts.lift !== undefined ? opts.lift : 0.2);
523
+ const daily = Number(opts.daily);
524
+ const days = Number(opts.days !== undefined ? opts.days : 90);
525
+ const power = Number(opts.power !== undefined ? opts.power : 0.8);
526
+ const alpha = Number(opts.alpha !== undefined ? opts.alpha : 0.05);
527
+ const start = String(opts.start !== undefined ? opts.start : "");
528
+ const problems = [];
529
+ if (!(base > 0) || !(base < 1)) { problems.push("base rate: a number between 0 and 1 (0.03 = 3%)"); }
530
+ if (!(lift > 0) || !(lift < 5)) { problems.push("lift worth detecting: a fraction, 0.2 = +20% relative"); }
531
+ if (!(daily > 0)) { problems.push("contacts a day: pass --daily 500"); }
532
+ if (!(days > 0)) { problems.push("horizon in days: pass --days 90"); }
533
+ if (!ZB[String(power)]) { problems.push("power must be 0.8 or 0.9"); }
534
+ if (!ZA[String(alpha)]) { problems.push("alpha must be 0.05, 0.01 or 0.1"); }
535
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(start)) { problems.push("start date: pass --start 2026-09-21"); }
536
+ if (problems.length) { console.error("queue: " + problems.join("; ")); return 1; }
537
+
538
+ const per_arm = sampleSize(base, lift, power, alpha);
539
+ const total_contacts = per_arm * 2;
540
+ const days_per_test = Math.ceil(total_contacts / daily);
541
+ const tests_in_horizon = Math.floor(days / days_per_test);
542
+ const read_dates = [];
543
+ for (let i = 0; i < tests_in_horizon; i++) { read_dates.push(qAddDays(start, days_per_test * (i + 1))); }
544
+ const idle_days = days - tests_in_horizon * days_per_test;
545
+ const used_pct = Math.round((tests_in_horizon * days_per_test * 100) / days);
546
+ const gates = [
547
+ "one look only: the floor date is the look that alpha " + alpha + " priced in - reading the same test daily is not covered by it",
548
+ "split audiences: two live tests in one arm halve the contacts a day, so neither reaches its floor",
549
+ tests_in_horizon === 0
550
+ ? "capacity: one honest test does not fit in " + days + " days at " + daily + " contacts a day - plan the queue, not the ideas"
551
+ : "capacity: " + tests_in_horizon + " honest test" + (tests_in_horizon === 1 ? "" : "s") + " fit in " + days + " days, " + idle_days + " days outside a test"
552
+ ];
553
+ const out = {
554
+ base_rate: base, lift: lift, power: power, alpha: alpha, daily: daily, horizon_days: days, start: start,
555
+ floor_per_arm: per_arm, total_contacts: total_contacts, days_per_test: days_per_test,
556
+ tests_in_horizon: tests_in_horizon, read_dates: read_dates, idle_days: idle_days, used_pct: used_pct,
557
+ gates: gates
558
+ };
559
+ if (opts.json) { console.log(JSON.stringify(out, null, 2)); } else {
560
+ console.log("floor: " + fmtNum(per_arm) + " contacts per arm (" + fmtNum(total_contacts) + " in both arms) at a " + (base * 100).toFixed(2) + "% base rate, +" + (lift * 100).toFixed(0) + "% relative lift, power " + power + ", alpha " + alpha);
561
+ console.log("one honest test: " + days_per_test + " days at " + fmtNum(daily) + " contacts a day");
562
+ console.log("queue: " + tests_in_horizon + " test" + (tests_in_horizon === 1 ? "" : "s") + " in " + days + " days (" + used_pct + "% of the horizon inside a test, " + idle_days + " days outside one)");
563
+ if (read_dates.length) { console.log("read-out dates: " + read_dates.join(", ")); }
564
+ else { console.log("read-out date: does not fit in this horizon"); }
565
+ gates.forEach(function (g) { console.log("gate: " + g); });
566
+ }
567
+ return 0;
568
+ }
569
+
513
570
  function fmtNum(n) {
514
571
  return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
515
572
  }
@@ -642,6 +699,8 @@ if (cmd === "sample") {
642
699
  }, null, 2));
643
700
  } else if (cmd === "verdict") {
644
701
  process.exit(runVerdict(flags(rest)));
702
+ } else if (cmd === "queue") {
703
+ process.exit(runQueue(flags(rest)));
645
704
  } else if (cmd === "aeo") {
646
705
  process.exit(runAeo(flags(rest)));
647
706
  } else if (cmd === "brief") {
@@ -685,6 +744,8 @@ Usage:
685
744
  npx marketing-mindset plan dated plan: weekly volume, gates, first honest read
686
745
  --base 0.03 --lift 0.2 --daily 500 [--weeks 4] [--start 2026-09-21]
687
746
  npx marketing-mindset verdict was the difference real, and was the volume ever enough?
747
+ npx marketing-mindset queue how many honest tests your volume can finish, and when each reads
748
+ --daily 500 --days 90 --start 2026-09-21 [--base 0.03] [--lift 0.2] [--power 0.8] [--alpha 0.05]
688
749
  --a 120/4000 --b 168/4000 [--alpha 0.05] [--json]
689
750
  npx marketing-mindset mde what a volume you can afford can actually read
690
751
  --base 0.03 --per-arm 5000 [--power 0.8] [--alpha 0.05] [--json]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "marketing-mindset",
3
- "version": "1.9.0",
4
- "description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, honest verdicts on finished tests, the lift a volume can read, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|verdict|mde|budget|brief|plan|warmup|aeo|jd|sections|read|install.",
3
+ "version": "1.10.0",
4
+ "description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, honest verdicts on finished tests, the lift a volume can read, a queue of readable tests with read-out dates, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|verdict|queue|mde|budget|brief|plan|warmup|aeo|jd|sections|read|install.",
5
5
  "keywords": [
6
6
  "agent-skill",
7
7
  "skill",
@@ -13,6 +13,7 @@
13
13
  "llm",
14
14
  "prompt",
15
15
  "sample-size",
16
+ "test-queue",
16
17
  "ab-test-verdict",
17
18
  "minimum-detectable-effect",
18
19
  "job-description",