marketing-mindset 1.10.0 → 1.11.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.
- package/README.md +21 -0
- package/bin/cli.js +62 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -156,3 +156,24 @@ npx marketing-mindset queue --daily 500 --days 90 --start 2026-09-21
|
|
|
156
156
|
`read_dates`, `idle_days`, `used_pct` and the three gates. The floor uses the same formula as `sample`, so a
|
|
157
157
|
queue planned here and a test read with `verdict` stay consistent. Two things it refuses to hide: the read-out
|
|
158
158
|
is one look (peeking daily is not covered by alpha), and two live tests in the same arm split the volume.
|
|
159
|
+
|
|
160
|
+
## `cost` — what one readable verdict costs to finish
|
|
161
|
+
|
|
162
|
+
A test budget is not spent on ideas, it is spent on reaching a floor. `cost` prices one readable verdict: the
|
|
163
|
+
contacts a strict two-arm test needs, multiplied by what a contact costs you, plus the days it takes to send them.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npx marketing-mindset cost --cost 0.02 --daily 500 --budget 5000
|
|
167
|
+
# floor: 13,914 contacts per arm (27,828 in both arms) at a 3.00% base rate, +20% relative lift
|
|
168
|
+
# time: 56 days at 500 contacts a day
|
|
169
|
+
# cost of one readable test: 556.56 (278.28 per arm) at 0.02 per contact
|
|
170
|
+
# cost of one conversion: 0.67 at the base rate, 0.56 at the lifted rate
|
|
171
|
+
# budget: 8 fully funded test(s) a month at a budget of 5000
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`--json` returns the same object: `floor_per_arm`, `total_contacts`, `days_per_test`, `cost_per_arm`,
|
|
175
|
+
`cost_per_test`, `cost_per_conversion_base`, `cost_per_conversion_target`, `tests_per_budget`,
|
|
176
|
+
`months_to_fund_one` and the three gates. Money is expressed in whatever unit you pass to `--cost`; no currency is
|
|
177
|
+
assumed. The floor uses the same formula as `sample`, so a budget planned here, a queue planned with `queue` and a
|
|
178
|
+
test read with `verdict` stay consistent. Two things it refuses to hide: stopping before the floor buys a sample
|
|
179
|
+
rather than a result, and money buys tests while volume limits how many can run at once.
|
package/bin/cli.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
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
8
|
* marketing-mindset queue volume into a queue of honest tests with read-out dates
|
|
9
|
+
* marketing-mindset cost the cost of one readable verdict, and funded tests per budget
|
|
9
10
|
* marketing-mindset sections list every section of SKILL.md
|
|
10
11
|
* marketing-mindset read <text> print one section by fuzzy title match
|
|
11
12
|
* marketing-mindset install copy the skill into your agent's skills directory
|
|
@@ -567,6 +568,63 @@ function runQueue(opts) {
|
|
|
567
568
|
return 0;
|
|
568
569
|
}
|
|
569
570
|
|
|
571
|
+
function cRound2(x) { return Math.round(x * 100) / 100; }
|
|
572
|
+
|
|
573
|
+
function runCost(opts) {
|
|
574
|
+
const base = Number(opts.base !== undefined ? opts.base : 0.03);
|
|
575
|
+
const lift = Number(opts.lift !== undefined ? opts.lift : 0.2);
|
|
576
|
+
const cost = Number(opts.cost);
|
|
577
|
+
const daily = Number(opts.daily);
|
|
578
|
+
const budget = opts.budget !== undefined ? Number(opts.budget) : 0;
|
|
579
|
+
const power = Number(opts.power !== undefined ? opts.power : 0.8);
|
|
580
|
+
const alpha = Number(opts.alpha !== undefined ? opts.alpha : 0.05);
|
|
581
|
+
const problems = [];
|
|
582
|
+
if (!(base > 0) || !(base < 1)) { problems.push("base rate: a number between 0 and 1 (0.03 = 3%)"); }
|
|
583
|
+
if (!(lift > 0) || !(lift < 5)) { problems.push("lift worth detecting: a fraction, 0.2 = +20% relative"); }
|
|
584
|
+
if (!(cost > 0)) { problems.push("cost per contact: pass --cost 0.02 in your own currency"); }
|
|
585
|
+
if (!(daily > 0)) { problems.push("contacts a day: pass --daily 500"); }
|
|
586
|
+
if (!(budget >= 0)) { problems.push("monthly budget: pass --budget 5000, or leave it out"); }
|
|
587
|
+
if (!ZB[String(power)]) { problems.push("power must be 0.8 or 0.9"); }
|
|
588
|
+
if (!ZA[String(alpha)]) { problems.push("alpha must be 0.05, 0.01 or 0.1"); }
|
|
589
|
+
if (problems.length) { console.error("cost: " + problems.join("; ")); return 1; }
|
|
590
|
+
|
|
591
|
+
const per_arm = sampleSize(base, lift, power, alpha);
|
|
592
|
+
const total_contacts = per_arm * 2;
|
|
593
|
+
const days_per_test = Math.ceil(total_contacts / daily);
|
|
594
|
+
const cost_per_arm = cRound2(per_arm * cost);
|
|
595
|
+
const cost_per_test = cRound2(total_contacts * cost);
|
|
596
|
+
const cost_per_conversion = cRound2(cost / base);
|
|
597
|
+
const cost_per_conversion_target = cRound2(cost / (base * (1 + lift)));
|
|
598
|
+
const tests_per_budget = budget > 0 ? Math.floor(budget / cost_per_test) : 0;
|
|
599
|
+
const months_to_fund_one = (budget > 0 && tests_per_budget === 0) ? Math.ceil(cost_per_test / budget) : 0;
|
|
600
|
+
const gates = [
|
|
601
|
+
"a verdict costs the whole floor: stopping before " + fmtNum(total_contacts) + " contacts produces a sample, not a result",
|
|
602
|
+
"cost per contact is not cost per result: at a " + (base * 100).toFixed(2) + "% base rate one conversion costs about " + cost_per_conversion + " in contacts",
|
|
603
|
+
tests_per_budget === 0
|
|
604
|
+
? "budget: one fully funded test needs " + cost_per_test + ", which this budget reaches in " + months_to_fund_one + " month(s)"
|
|
605
|
+
: "budget: " + tests_per_budget + " fully funded test(s) a month - volume, not money, sets how many can run at once"
|
|
606
|
+
];
|
|
607
|
+
const out = {
|
|
608
|
+
base_rate: base, lift: lift, power: power, alpha: alpha, cost_per_contact: cost, daily: daily, budget: budget,
|
|
609
|
+
floor_per_arm: per_arm, total_contacts: total_contacts, days_per_test: days_per_test,
|
|
610
|
+
cost_per_arm: cost_per_arm, cost_per_test: cost_per_test,
|
|
611
|
+
cost_per_conversion_base: cost_per_conversion, cost_per_conversion_target: cost_per_conversion_target,
|
|
612
|
+
tests_per_budget: tests_per_budget, months_to_fund_one: months_to_fund_one, gates: gates
|
|
613
|
+
};
|
|
614
|
+
if (opts.json) { console.log(JSON.stringify(out, null, 2)); } else {
|
|
615
|
+
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");
|
|
616
|
+
console.log("time: " + days_per_test + " days at " + fmtNum(daily) + " contacts a day");
|
|
617
|
+
console.log("cost of one readable test: " + cost_per_test + " (" + cost_per_arm + " per arm) at " + cost + " per contact");
|
|
618
|
+
console.log("cost of one conversion: " + cost_per_conversion + " at the base rate, " + cost_per_conversion_target + " at the lifted rate");
|
|
619
|
+
if (budget > 0) {
|
|
620
|
+
console.log("budget: " + tests_per_budget + " fully funded test(s) a month at a budget of " + budget);
|
|
621
|
+
if (tests_per_budget === 0) { console.log("budget: one test needs " + months_to_fund_one + " month(s) of this budget"); }
|
|
622
|
+
} else { console.log("budget: not given - pass --budget to see how many funded tests it carries"); }
|
|
623
|
+
gates.forEach(function (g) { console.log("gate: " + g); });
|
|
624
|
+
}
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
|
|
570
628
|
function fmtNum(n) {
|
|
571
629
|
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
572
630
|
}
|
|
@@ -701,6 +759,8 @@ if (cmd === "sample") {
|
|
|
701
759
|
process.exit(runVerdict(flags(rest)));
|
|
702
760
|
} else if (cmd === "queue") {
|
|
703
761
|
process.exit(runQueue(flags(rest)));
|
|
762
|
+
} else if (cmd === "cost") {
|
|
763
|
+
process.exit(runCost(flags(rest)));
|
|
704
764
|
} else if (cmd === "aeo") {
|
|
705
765
|
process.exit(runAeo(flags(rest)));
|
|
706
766
|
} else if (cmd === "brief") {
|
|
@@ -745,6 +805,8 @@ Usage:
|
|
|
745
805
|
--base 0.03 --lift 0.2 --daily 500 [--weeks 4] [--start 2026-09-21]
|
|
746
806
|
npx marketing-mindset verdict was the difference real, and was the volume ever enough?
|
|
747
807
|
npx marketing-mindset queue how many honest tests your volume can finish, and when each reads
|
|
808
|
+
npx marketing-mindset cost what one readable verdict costs to finish, and how many a budget funds
|
|
809
|
+
--cost 0.02 --daily 500 [--budget 5000] [--base 0.03] [--lift 0.2] [--power 0.8] [--alpha 0.05]
|
|
748
810
|
--daily 500 --days 90 --start 2026-09-21 [--base 0.03] [--lift 0.2] [--power 0.8] [--alpha 0.05]
|
|
749
811
|
--a 120/4000 --b 168/4000 [--alpha 0.05] [--json]
|
|
750
812
|
npx marketing-mindset mde what a volume you can afford can actually read
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "marketing-mindset",
|
|
3
|
-
"version": "1.
|
|
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.",
|
|
3
|
+
"version": "1.11.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, the cost of one readable verdict, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|verdict|queue|cost|mde|budget|brief|plan|warmup|aeo|jd|sections|read|install.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skill",
|
|
7
7
|
"skill",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"prompt",
|
|
15
15
|
"sample-size",
|
|
16
16
|
"test-queue",
|
|
17
|
+
"cost-per-verdict",
|
|
17
18
|
"ab-test-verdict",
|
|
18
19
|
"minimum-detectable-effect",
|
|
19
20
|
"job-description",
|