@agentsean/measure 1.0.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/LICENSE +661 -0
- package/dist/analyze.d.ts +19 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +151 -0
- package/dist/analyze.js.map +1 -0
- package/dist/claims.d.ts +24 -0
- package/dist/claims.d.ts.map +1 -0
- package/dist/claims.js +59 -0
- package/dist/claims.js.map +1 -0
- package/dist/engine.d.ts +20 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +108 -0
- package/dist/engine.js.map +1 -0
- package/dist/estimator.d.ts +17 -0
- package/dist/estimator.d.ts.map +1 -0
- package/dist/estimator.js +91 -0
- package/dist/estimator.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/ladder.d.ts +41 -0
- package/dist/ladder.d.ts.map +1 -0
- package/dist/ladder.js +99 -0
- package/dist/ladder.js.map +1 -0
- package/dist/peek.d.ts +19 -0
- package/dist/peek.d.ts.map +1 -0
- package/dist/peek.js +31 -0
- package/dist/peek.js.map +1 -0
- package/dist/persist.d.ts +66 -0
- package/dist/persist.d.ts.map +1 -0
- package/dist/persist.js +265 -0
- package/dist/persist.js.map +1 -0
- package/dist/power.d.ts +36 -0
- package/dist/power.d.ts.map +1 -0
- package/dist/power.js +130 -0
- package/dist/power.js.map +1 -0
- package/dist/register.d.ts +17 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +86 -0
- package/dist/register.js.map +1 -0
- package/dist/suppress.d.ts +24 -0
- package/dist/suppress.d.ts.map +1 -0
- package/dist/suppress.js +60 -0
- package/dist/suppress.js.map +1 -0
- package/dist/track.d.ts +10 -0
- package/dist/track.d.ts.map +1 -0
- package/dist/track.js +30 -0
- package/dist/track.js.map +1 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +38 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
package/dist/power.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { DEFAULT_EVIDENCE_TIER } from "./types.js";
|
|
2
|
+
import { UNDERPOWERED_MDE } from "./ladder.js";
|
|
3
|
+
/** PLAN / gap-04 Monte-Carlo floors. Traffic does not move pre/post MDE. */
|
|
4
|
+
export const PRE_POST_MDE_28D = 0.8;
|
|
5
|
+
export const PRE_POST_MDE_56D = 0.55;
|
|
6
|
+
export const PRE_POST_MDE_91D = 0.41;
|
|
7
|
+
/** 200 pages, 2,000 clicks/mo, 100 vs 100, 56 days → ~18% (PLAN). */
|
|
8
|
+
export const SPLIT_MDE_2K_56D = 0.18;
|
|
9
|
+
/** Daily peeking vs fixed-horizon null false-positive rate. */
|
|
10
|
+
export const PEEKING_FP_FIXED = 0.047;
|
|
11
|
+
export const PEEKING_FP_DAILY = 0.229;
|
|
12
|
+
/** Naive α=0.05 tests: P(≥1 false win) at 20 changes/month. */
|
|
13
|
+
export const FABRICATED_WIN_20 = 0.64;
|
|
14
|
+
export const SEARCHPILOT_MIN_SESSIONS = 30_000;
|
|
15
|
+
export const SEMRUSH_MIN_CLICKS_100D = 100_000;
|
|
16
|
+
export const DEFAULT_WINDOW_DAYS = 56;
|
|
17
|
+
export const DEFAULT_SPLIT = 0.5;
|
|
18
|
+
const CLICKS_ARM_MDE = [
|
|
19
|
+
[100, 0.75],
|
|
20
|
+
[250, 0.43],
|
|
21
|
+
[500, 0.31],
|
|
22
|
+
[1000, 0.22],
|
|
23
|
+
[1867, 0.18],
|
|
24
|
+
[2500, 0.16],
|
|
25
|
+
[5000, 0.15],
|
|
26
|
+
[10_000, 0.12],
|
|
27
|
+
[25_000, 0.11],
|
|
28
|
+
];
|
|
29
|
+
const PAGES_ARM_MDE = [
|
|
30
|
+
[10, 0.25],
|
|
31
|
+
[25, 0.2],
|
|
32
|
+
[50, 0.17],
|
|
33
|
+
[100, 0.15],
|
|
34
|
+
[250, 0.115],
|
|
35
|
+
[500, 0.099],
|
|
36
|
+
];
|
|
37
|
+
function lerpTable(table, x) {
|
|
38
|
+
if (x <= table[0][0])
|
|
39
|
+
return table[0][1];
|
|
40
|
+
const last = table[table.length - 1];
|
|
41
|
+
if (x >= last[0])
|
|
42
|
+
return last[1];
|
|
43
|
+
for (let i = 1; i < table.length; i++) {
|
|
44
|
+
const [x1, y1] = table[i - 1];
|
|
45
|
+
const [x2, y2] = table[i];
|
|
46
|
+
if (x <= x2) {
|
|
47
|
+
const t = (x - x1) / (x2 - x1);
|
|
48
|
+
return y1 + t * (y2 - y1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return last[1];
|
|
52
|
+
}
|
|
53
|
+
/** Pre/post MDE is a function of window length, not traffic. */
|
|
54
|
+
export function prePostMde(windowDays) {
|
|
55
|
+
if (windowDays <= 28)
|
|
56
|
+
return PRE_POST_MDE_28D;
|
|
57
|
+
if (windowDays <= 56) {
|
|
58
|
+
const t = (windowDays - 28) / 28;
|
|
59
|
+
return PRE_POST_MDE_28D + t * (PRE_POST_MDE_56D - PRE_POST_MDE_28D);
|
|
60
|
+
}
|
|
61
|
+
if (windowDays <= 91) {
|
|
62
|
+
const t = (windowDays - 56) / 35;
|
|
63
|
+
return PRE_POST_MDE_56D + t * (PRE_POST_MDE_91D - PRE_POST_MDE_56D);
|
|
64
|
+
}
|
|
65
|
+
return PRE_POST_MDE_91D;
|
|
66
|
+
}
|
|
67
|
+
export function clicksPerArmInWindow(monthlyClicks, windowDays, pagesPerArm, sitePages) {
|
|
68
|
+
const share = sitePages > 0 ? pagesPerArm / sitePages : DEFAULT_SPLIT;
|
|
69
|
+
return monthlyClicks * (windowDays / 30) * share;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Concurrent-control MDE. Binding constraints are clicks/arm and pages/arm;
|
|
73
|
+
* the floor is ~11% even at huge traffic with only 100 pages/arm.
|
|
74
|
+
*/
|
|
75
|
+
export function splitMde(opts) {
|
|
76
|
+
const windowDays = opts.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
77
|
+
const pagesPerArm = opts.pagesPerArm ?? Math.floor(opts.pageCount / 2);
|
|
78
|
+
const clicks = clicksPerArmInWindow(opts.monthlyClicks, windowDays, pagesPerArm, opts.pageCount);
|
|
79
|
+
const fromClicks = lerpTable(CLICKS_ARM_MDE, clicks);
|
|
80
|
+
const fromPages = lerpTable(PAGES_ARM_MDE, Math.max(pagesPerArm, 1));
|
|
81
|
+
let mde = Math.max(fromClicks, fromPages);
|
|
82
|
+
if (windowDays < 56) {
|
|
83
|
+
const scale = prePostMde(windowDays) / PRE_POST_MDE_56D;
|
|
84
|
+
mde *= Math.min(scale, 1.6);
|
|
85
|
+
}
|
|
86
|
+
else if (windowDays > 56) {
|
|
87
|
+
const scale = prePostMde(windowDays) / PRE_POST_MDE_56D;
|
|
88
|
+
mde *= Math.max(scale, 0.7);
|
|
89
|
+
}
|
|
90
|
+
return Math.round(mde * 1000) / 1000;
|
|
91
|
+
}
|
|
92
|
+
export function naiveFalseWinProbability(testsPerMonth, alpha = 0.05) {
|
|
93
|
+
if (testsPerMonth <= 0)
|
|
94
|
+
return 0;
|
|
95
|
+
return 1 - (1 - alpha) ** testsPerMonth;
|
|
96
|
+
}
|
|
97
|
+
export function sitePowerBrief(opts) {
|
|
98
|
+
const windowDays = opts.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
99
|
+
const pagesPerArm = Math.max(1, Math.floor(opts.pageCount / 2));
|
|
100
|
+
const pre = prePostMde(windowDays);
|
|
101
|
+
const split = splitMde({
|
|
102
|
+
monthlyClicks: opts.monthlyClicks,
|
|
103
|
+
pageCount: Math.max(opts.pageCount, 2),
|
|
104
|
+
windowDays,
|
|
105
|
+
pagesPerArm,
|
|
106
|
+
});
|
|
107
|
+
const belowIndustryBar = opts.monthlyClicks < SEARCHPILOT_MIN_SESSIONS ||
|
|
108
|
+
opts.monthlyClicks * (100 / 30) < SEMRUSH_MIN_CLICKS_100D;
|
|
109
|
+
const underpowered = split > UNDERPOWERED_MDE && pre > UNDERPOWERED_MDE;
|
|
110
|
+
const needed = 1900;
|
|
111
|
+
const message = belowIndustryBar
|
|
112
|
+
? `Most changes on this site will land in evidence tier E (applied, not measurable). That is true of every SEO tool; only Sean says so. A ${opts.pageCount || 200}-page site with ${Math.round(opts.monthlyClicks)} clicks/month still needs ~${pct(split)} lift over ${windowDays} days for 80% power with a concurrent control. SearchPilot wants ${SEARCHPILOT_MIN_SESSIONS.toLocaleString()} organic sessions/month; Semrush SplitSignal wants 300 pages and ${SEMRUSH_MIN_CLICKS_100D.toLocaleString()} clicks per 100 days. Pre/post MDE is ~${pct(pre)} regardless of traffic. We'd need roughly ${needed} clicks per group over 8 weeks.`
|
|
113
|
+
: `Split-test MDE ≈ ${pct(split)} over ${windowDays} days with ${pagesPerArm} pages/arm. Pre/post MDE remains ~${pct(pre)}.`;
|
|
114
|
+
return {
|
|
115
|
+
monthlyClicks: opts.monthlyClicks,
|
|
116
|
+
pageCount: opts.pageCount,
|
|
117
|
+
windowDays,
|
|
118
|
+
pagesPerArm,
|
|
119
|
+
prePostMde: pre,
|
|
120
|
+
splitMde: split,
|
|
121
|
+
belowIndustryBar,
|
|
122
|
+
underpowered,
|
|
123
|
+
typicalTier: DEFAULT_EVIDENCE_TIER,
|
|
124
|
+
message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function pct(n) {
|
|
128
|
+
return `${Math.round(n * 1000) / 10}%`;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=power.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power.js","sourceRoot":"","sources":["../src/power.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
import { type StoredExperiment } from "./persist.js";
|
|
3
|
+
import type { ExperimentSpec } from "./types.js";
|
|
4
|
+
export type RegisterResult = {
|
|
5
|
+
experiment: StoredExperiment;
|
|
6
|
+
plannedMde: number;
|
|
7
|
+
status: "planned" | "refused";
|
|
8
|
+
reason: string | null;
|
|
9
|
+
};
|
|
10
|
+
export declare function monthlyClicksFromMap(preClicks: Record<string, number> | undefined, windowDays: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Create the experiment *before* any change ships. Hypothesis, cohort
|
|
13
|
+
* assignment, and analysis date are frozen at insert.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerExperiment(db: SqliteDatabase, spec: ExperimentSpec, now?: Date): RegisterResult;
|
|
16
|
+
export declare function startExperiment(db: SqliteDatabase, registered: RegisterResult): RegisterResult;
|
|
17
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AASF,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC7C,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE,cAAc,EACpB,GAAG,OAAa,GACf,cAAc,CA6DhB;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,cAAc,GACzB,cAAc,CAOhB"}
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { pages } from "@agentsean/db";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { UNDERPOWERED_MDE } from "./ladder.js";
|
|
4
|
+
import { splitMde, prePostMde, DEFAULT_WINDOW_DAYS } from "./power.js";
|
|
5
|
+
import { insertExperiment, seedDataAnomalies, setExperimentStatus, } from "./persist.js";
|
|
6
|
+
function dayDiff(start, end) {
|
|
7
|
+
const a = Date.parse(`${start}T00:00:00Z`);
|
|
8
|
+
const b = Date.parse(`${end}T00:00:00Z`);
|
|
9
|
+
if (!Number.isFinite(a) || !Number.isFinite(b))
|
|
10
|
+
return DEFAULT_WINDOW_DAYS;
|
|
11
|
+
return Math.max(1, Math.round((b - a) / 86_400_000) + 1);
|
|
12
|
+
}
|
|
13
|
+
export function monthlyClicksFromMap(preClicks, windowDays) {
|
|
14
|
+
if (!preClicks)
|
|
15
|
+
return 0;
|
|
16
|
+
let sum = 0;
|
|
17
|
+
for (const v of Object.values(preClicks))
|
|
18
|
+
sum += v;
|
|
19
|
+
return sum * (30 / Math.max(windowDays, 1));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create the experiment *before* any change ships. Hypothesis, cohort
|
|
23
|
+
* assignment, and analysis date are frozen at insert.
|
|
24
|
+
*/
|
|
25
|
+
export function registerExperiment(db, spec, now = new Date()) {
|
|
26
|
+
seedDataAnomalies(db);
|
|
27
|
+
if (spec.unit === "url") {
|
|
28
|
+
throw new Error("Experiment unit cannot be 'url'. The unit of causal claim is the cohort.");
|
|
29
|
+
}
|
|
30
|
+
const windowDays = dayDiff(spec.postStart, spec.plannedEnd);
|
|
31
|
+
const pageCount = Math.max(spec.treatmentUrls.length + spec.controlUrls.length, db.select().from(pages).where(eq(pages.siteId, spec.siteId)).all().length, 2);
|
|
32
|
+
const monthly = monthlyClicksFromMap(spec.preClicks, dayDiff(spec.preStart, spec.preEnd));
|
|
33
|
+
const plannedMde = spec.design === "uncontrolled"
|
|
34
|
+
? prePostMde(windowDays)
|
|
35
|
+
: splitMde({
|
|
36
|
+
monthlyClicks: Math.max(monthly, 1),
|
|
37
|
+
pageCount,
|
|
38
|
+
windowDays,
|
|
39
|
+
pagesPerArm: spec.controlUrls.length || Math.floor(pageCount / 2),
|
|
40
|
+
});
|
|
41
|
+
const underpowered = plannedMde > UNDERPOWERED_MDE;
|
|
42
|
+
const status = underpowered ? "refused" : "planned";
|
|
43
|
+
const id = insertExperiment(db, spec, plannedMde, status, now);
|
|
44
|
+
const experiment = {
|
|
45
|
+
id,
|
|
46
|
+
siteId: spec.siteId,
|
|
47
|
+
hypothesis: spec.hypothesis,
|
|
48
|
+
interventionKind: spec.interventionKind,
|
|
49
|
+
status,
|
|
50
|
+
design: spec.design,
|
|
51
|
+
unit: spec.unit,
|
|
52
|
+
randomisationSeed: spec.randomisationSeed ?? 1,
|
|
53
|
+
clusterKey: spec.clusterKey ?? null,
|
|
54
|
+
preStart: spec.preStart,
|
|
55
|
+
preEnd: spec.preEnd,
|
|
56
|
+
postStart: spec.postStart,
|
|
57
|
+
plannedEnd: spec.plannedEnd,
|
|
58
|
+
primaryMetric: spec.primaryMetric ?? "clicks",
|
|
59
|
+
secondaryMetric: null,
|
|
60
|
+
plannedMde,
|
|
61
|
+
powerTarget: spec.powerTarget ?? 0.8,
|
|
62
|
+
alpha: spec.alpha ?? 0.05,
|
|
63
|
+
evidenceTier: null,
|
|
64
|
+
peekingBlocked: 1,
|
|
65
|
+
createdAt: now.toISOString(),
|
|
66
|
+
concludedAt: null,
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
experiment,
|
|
70
|
+
plannedMde,
|
|
71
|
+
status,
|
|
72
|
+
reason: underpowered
|
|
73
|
+
? `Refused to start: planned MDE ${Math.round(plannedMde * 100)}% exceeds 40%. Marked not measurable by design.`
|
|
74
|
+
: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function startExperiment(db, registered) {
|
|
78
|
+
if (registered.status === "refused")
|
|
79
|
+
return registered;
|
|
80
|
+
setExperimentStatus(db, registered.experiment.id, "running");
|
|
81
|
+
return {
|
|
82
|
+
...registered,
|
|
83
|
+
experiment: { ...registered.experiment, status: "running" },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IMPRESSIONS_BUG_BEGIN, IMPRESSIONS_BUG_END } from "@agentsean/google";
|
|
2
|
+
import type { PageSeries, SuppressionRule } from "./types.js";
|
|
3
|
+
export type SuppressInput = {
|
|
4
|
+
metric: string;
|
|
5
|
+
preStart: string;
|
|
6
|
+
plannedEnd: string;
|
|
7
|
+
hasControl: boolean;
|
|
8
|
+
plannedMde: number;
|
|
9
|
+
now: Date;
|
|
10
|
+
overlappingAnomalies?: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
metrics: string[];
|
|
13
|
+
}> | undefined;
|
|
14
|
+
overlappingRankingUpdate?: boolean | undefined;
|
|
15
|
+
treatment?: PageSeries[] | undefined;
|
|
16
|
+
control?: PageSeries[] | undefined;
|
|
17
|
+
reserve?: PageSeries[] | undefined;
|
|
18
|
+
cohort404Share?: number | undefined;
|
|
19
|
+
outOfBandEdits?: boolean | undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare const SUPPRESSION_LABEL: Record<SuppressionRule, string>;
|
|
22
|
+
export declare function suppress(input: SuppressInput): SuppressionRule[];
|
|
23
|
+
export { IMPRESSIONS_BUG_BEGIN, IMPRESSIONS_BUG_END };
|
|
24
|
+
//# sourceMappingURL=suppress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppress.d.ts","sourceRoot":"","sources":["../src/suppress.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EAGpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9D,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,IAAI,CAAC;IACV,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,SAAS,CAAC;IAC5E,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/C,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IACrC,OAAO,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAY7D,CAAC;AAMF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAoChE;AAED,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,CAAC"}
|
package/dist/suppress.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { IMPRESSIONS_BUG_BEGIN, IMPRESSIONS_BUG_END, impressionsContaminated, num100Straddle, } from "@agentsean/google";
|
|
2
|
+
import { UNDERPOWERED_MDE } from "./ladder.js";
|
|
3
|
+
import { estimateLift } from "./estimator.js";
|
|
4
|
+
export const SUPPRESSION_LABEL = {
|
|
5
|
+
S1: "GSC data-anomaly window overlaps the outcome metric",
|
|
6
|
+
S2: "Impressions logging error 2025-05-13 → 2026-04-27 (hard block)",
|
|
7
|
+
S3: "&num=100 removal straddles the window (hard block on impressions/position)",
|
|
8
|
+
S4: "No concurrent control — downgrade to applied, not measurable",
|
|
9
|
+
S5: "Google ranking update overlapped an uncontrolled window",
|
|
10
|
+
S6: "Google ranking update overlapped a controlled window (do not suppress; widen CI)",
|
|
11
|
+
S7: "Spillover: control vs reserve differs",
|
|
12
|
+
S8: "Underpowered (planned MDE > 40%)",
|
|
13
|
+
S9: "Peeking: analysis requested before planned_end",
|
|
14
|
+
S10: "Cohort drift: >10% of URLs 404/redirect/deindex",
|
|
15
|
+
S11: "Out-of-band CMS edit on treatment or control pages",
|
|
16
|
+
};
|
|
17
|
+
function isoDate(d) {
|
|
18
|
+
return d.toISOString().slice(0, 10);
|
|
19
|
+
}
|
|
20
|
+
export function suppress(input) {
|
|
21
|
+
const rules = [];
|
|
22
|
+
const metric = input.metric;
|
|
23
|
+
const windowStart = input.preStart;
|
|
24
|
+
const windowEnd = input.plannedEnd;
|
|
25
|
+
if (input.overlappingAnomalies?.some((a) => a.metrics.includes(metric))) {
|
|
26
|
+
rules.push("S1");
|
|
27
|
+
}
|
|
28
|
+
if ((metric === "impressions" || metric === "ctr" || metric === "position") &&
|
|
29
|
+
impressionsContaminated(windowStart, windowEnd)) {
|
|
30
|
+
rules.push("S2");
|
|
31
|
+
}
|
|
32
|
+
if ((metric === "impressions" || metric === "position") &&
|
|
33
|
+
num100Straddle(windowStart, windowEnd)) {
|
|
34
|
+
rules.push("S3");
|
|
35
|
+
}
|
|
36
|
+
if (!input.hasControl) {
|
|
37
|
+
rules.push("S4");
|
|
38
|
+
if (input.overlappingRankingUpdate)
|
|
39
|
+
rules.push("S5");
|
|
40
|
+
}
|
|
41
|
+
else if (input.overlappingRankingUpdate) {
|
|
42
|
+
rules.push("S6");
|
|
43
|
+
}
|
|
44
|
+
if (input.hasControl && input.control && input.reserve && input.reserve.length > 0) {
|
|
45
|
+
const spill = estimateLift(input.control, input.reserve, { nBoot: 400, seed: 7 });
|
|
46
|
+
if (spill.probPositive > 0.9 || spill.probPositive < 0.1)
|
|
47
|
+
rules.push("S7");
|
|
48
|
+
}
|
|
49
|
+
if (input.plannedMde > UNDERPOWERED_MDE)
|
|
50
|
+
rules.push("S8");
|
|
51
|
+
if (isoDate(input.now) < input.plannedEnd)
|
|
52
|
+
rules.push("S9");
|
|
53
|
+
if ((input.cohort404Share ?? 0) > 0.1)
|
|
54
|
+
rules.push("S10");
|
|
55
|
+
if (input.outOfBandEdits)
|
|
56
|
+
rules.push("S11");
|
|
57
|
+
return rules;
|
|
58
|
+
}
|
|
59
|
+
export { IMPRESSIONS_BUG_BEGIN, IMPRESSIONS_BUG_END };
|
|
60
|
+
//# sourceMappingURL=suppress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppress.js","sourceRoot":"","sources":["../src/suppress.ts"],"names":[],"mappings":"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"}
|
package/dist/track.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track A = measured (cohort-able, 56-day window).
|
|
3
|
+
* Track B = obviously-correct fixes applied everywhere, no hold-out, no causal claim.
|
|
4
|
+
* Routing rule: if a competent human SEO would refuse to hold this back as a control, it's Track B.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TRACK_B_KINDS: Set<string>;
|
|
7
|
+
export declare const TRACK_A_KINDS: Set<string>;
|
|
8
|
+
export type Track = "A" | "B";
|
|
9
|
+
export declare function trackForActionKind(kind: string): Track;
|
|
10
|
+
//# sourceMappingURL=track.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../src/track.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,aAOxB,CAAC;AAEH,eAAO,MAAM,aAAa,aAQxB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;AAE9B,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAItD"}
|
package/dist/track.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track A = measured (cohort-able, 56-day window).
|
|
3
|
+
* Track B = obviously-correct fixes applied everywhere, no hold-out, no causal claim.
|
|
4
|
+
* Routing rule: if a competent human SEO would refuse to hold this back as a control, it's Track B.
|
|
5
|
+
*/
|
|
6
|
+
export const TRACK_B_KINDS = new Set([
|
|
7
|
+
"fix_broken_internal_link",
|
|
8
|
+
"add_image_alt",
|
|
9
|
+
"add_canonical",
|
|
10
|
+
"remove_sitemap_404",
|
|
11
|
+
"fix_malformed_schema",
|
|
12
|
+
"repair_redirect_chain",
|
|
13
|
+
]);
|
|
14
|
+
export const TRACK_A_KINDS = new Set([
|
|
15
|
+
"rewrite_title",
|
|
16
|
+
"rewrite_meta_description",
|
|
17
|
+
"rewrite_h1",
|
|
18
|
+
"inject_jsonld",
|
|
19
|
+
"insert_internal_link",
|
|
20
|
+
"refresh_content",
|
|
21
|
+
"create_page",
|
|
22
|
+
]);
|
|
23
|
+
export function trackForActionKind(kind) {
|
|
24
|
+
if (TRACK_B_KINDS.has(kind))
|
|
25
|
+
return "B";
|
|
26
|
+
if (TRACK_A_KINDS.has(kind))
|
|
27
|
+
return "A";
|
|
28
|
+
return "B";
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=track.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track.js","sourceRoot":"","sources":["../src/track.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IACnC,0BAA0B;IAC1B,eAAe;IACf,eAAe;IACf,oBAAoB;IACpB,sBAAsB;IACtB,uBAAuB;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IACnC,eAAe;IACf,0BAA0B;IAC1B,YAAY;IACZ,eAAe;IACf,sBAAsB;IACtB,iBAAiB;IACjB,aAAa;CACd,CAAC,CAAC;AAIH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare const EVIDENCE_TIERS: readonly ["A", "B", "C", "D", "E"];
|
|
2
|
+
export type EvidenceTier = (typeof EVIDENCE_TIERS)[number];
|
|
3
|
+
export declare const EVIDENCE_MEANING: Record<EvidenceTier, string>;
|
|
4
|
+
export declare const DEFAULT_EVIDENCE_TIER: EvidenceTier;
|
|
5
|
+
export declare const EXPERIMENT_STATUSES: readonly ["planned", "running", "analysing", "concluded", "abandoned", "refused"];
|
|
6
|
+
export type ExperimentStatus = (typeof EXPERIMENT_STATUSES)[number];
|
|
7
|
+
export declare const EXPERIMENT_DESIGNS: readonly ["split_cohort", "its_with_control_pool", "uncontrolled"];
|
|
8
|
+
export type ExperimentDesign = (typeof EXPERIMENT_DESIGNS)[number];
|
|
9
|
+
export declare const EXPERIMENT_UNITS: readonly ["page_group", "template", "section"];
|
|
10
|
+
export type ExperimentUnit = (typeof EXPERIMENT_UNITS)[number];
|
|
11
|
+
export declare const COHORT_ARMS: readonly ["treatment", "control", "reserve"];
|
|
12
|
+
export type CohortArm = (typeof COHORT_ARMS)[number];
|
|
13
|
+
export declare const SUPPRESSION_RULES: readonly ["S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11"];
|
|
14
|
+
export type SuppressionRule = (typeof SUPPRESSION_RULES)[number];
|
|
15
|
+
export type PageSeries = {
|
|
16
|
+
url: string;
|
|
17
|
+
preClicks: number;
|
|
18
|
+
postClicks: number;
|
|
19
|
+
preImpressions?: number | undefined;
|
|
20
|
+
postImpressions?: number | undefined;
|
|
21
|
+
};
|
|
22
|
+
export type Estimate = {
|
|
23
|
+
lift: number;
|
|
24
|
+
ciLow: number;
|
|
25
|
+
ciHigh: number;
|
|
26
|
+
ciLevel: number;
|
|
27
|
+
probPositive: number;
|
|
28
|
+
nBoot: number;
|
|
29
|
+
};
|
|
30
|
+
export type PowerBrief = {
|
|
31
|
+
monthlyClicks: number;
|
|
32
|
+
pageCount: number;
|
|
33
|
+
windowDays: number;
|
|
34
|
+
pagesPerArm: number;
|
|
35
|
+
prePostMde: number;
|
|
36
|
+
splitMde: number;
|
|
37
|
+
belowIndustryBar: boolean;
|
|
38
|
+
underpowered: boolean;
|
|
39
|
+
typicalTier: EvidenceTier;
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
export type ClaimRecord = {
|
|
43
|
+
id: string;
|
|
44
|
+
siteId: string;
|
|
45
|
+
changeId: string | null;
|
|
46
|
+
experimentId: string | null;
|
|
47
|
+
evidenceTier: EvidenceTier;
|
|
48
|
+
statement: string;
|
|
49
|
+
metric: string;
|
|
50
|
+
causationClaimed: boolean;
|
|
51
|
+
refusedReason: string | null;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
};
|
|
54
|
+
export type ExperimentSpec = {
|
|
55
|
+
siteId: string;
|
|
56
|
+
hypothesis: string;
|
|
57
|
+
interventionKind: string;
|
|
58
|
+
design: ExperimentDesign;
|
|
59
|
+
unit: ExperimentUnit;
|
|
60
|
+
preStart: string;
|
|
61
|
+
preEnd: string;
|
|
62
|
+
postStart: string;
|
|
63
|
+
plannedEnd: string;
|
|
64
|
+
primaryMetric?: string | undefined;
|
|
65
|
+
powerTarget?: number | undefined;
|
|
66
|
+
alpha?: number | undefined;
|
|
67
|
+
randomisationSeed?: number | undefined;
|
|
68
|
+
clusterKey?: string | undefined;
|
|
69
|
+
treatmentUrls: string[];
|
|
70
|
+
controlUrls: string[];
|
|
71
|
+
reserveUrls?: string[] | undefined;
|
|
72
|
+
preClicks?: Record<string, number> | undefined;
|
|
73
|
+
preImpressions?: Record<string, number> | undefined;
|
|
74
|
+
contentHashAtStart?: Record<string, string> | undefined;
|
|
75
|
+
};
|
|
76
|
+
export type AnalysisInput = {
|
|
77
|
+
experimentId: string;
|
|
78
|
+
now: Date;
|
|
79
|
+
treatment: PageSeries[];
|
|
80
|
+
control: PageSeries[];
|
|
81
|
+
reserve?: PageSeries[] | undefined;
|
|
82
|
+
overlappingIncidentTitles?: string[] | undefined;
|
|
83
|
+
overlappingAnomalies?: Array<{
|
|
84
|
+
id: string;
|
|
85
|
+
metrics: string[];
|
|
86
|
+
}> | undefined;
|
|
87
|
+
metric?: string | undefined;
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const EVIDENCE_TIERS = ["A", "B", "C", "D", "E"];
|
|
2
|
+
export const EVIDENCE_MEANING = {
|
|
3
|
+
A: "Controlled experiment with a matched cohort, pre-registered, sufficient power",
|
|
4
|
+
B: "Matched-cohort observational, effect exceeds MDE",
|
|
5
|
+
C: "Pre/post with a Google-update annotation join, effect exceeds MDE",
|
|
6
|
+
D: "Directional signal only, below MDE",
|
|
7
|
+
E: "Applied; not measurable at this site's traffic volume",
|
|
8
|
+
};
|
|
9
|
+
export const DEFAULT_EVIDENCE_TIER = "E";
|
|
10
|
+
export const EXPERIMENT_STATUSES = [
|
|
11
|
+
"planned",
|
|
12
|
+
"running",
|
|
13
|
+
"analysing",
|
|
14
|
+
"concluded",
|
|
15
|
+
"abandoned",
|
|
16
|
+
"refused",
|
|
17
|
+
];
|
|
18
|
+
export const EXPERIMENT_DESIGNS = [
|
|
19
|
+
"split_cohort",
|
|
20
|
+
"its_with_control_pool",
|
|
21
|
+
"uncontrolled",
|
|
22
|
+
];
|
|
23
|
+
export const EXPERIMENT_UNITS = ["page_group", "template", "section"];
|
|
24
|
+
export const COHORT_ARMS = ["treatment", "control", "reserve"];
|
|
25
|
+
export const SUPPRESSION_RULES = [
|
|
26
|
+
"S1",
|
|
27
|
+
"S2",
|
|
28
|
+
"S3",
|
|
29
|
+
"S4",
|
|
30
|
+
"S5",
|
|
31
|
+
"S6",
|
|
32
|
+
"S7",
|
|
33
|
+
"S8",
|
|
34
|
+
"S9",
|
|
35
|
+
"S10",
|
|
36
|
+
"S11",
|
|
37
|
+
];
|
|
38
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAGjE,MAAM,CAAC,MAAM,gBAAgB,GAAiC;IAC5D,CAAC,EAAE,+EAA+E;IAClF,CAAC,EAAE,kDAAkD;IACrD,CAAC,EAAE,mEAAmE;IACtE,CAAC,EAAE,oCAAoC;IACvC,CAAC,EAAE,uDAAuD;CAC3D,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAiB,GAAG,CAAC;AAEvD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,SAAS;IACT,SAAS;IACT,WAAW;IACX,WAAW;IACX,WAAW;IACX,SAAS;CACD,CAAC;AAGX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,cAAc;IACd,uBAAuB;IACvB,cAAc;CACN,CAAC;AAGX,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAG/E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AAGxE,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;CACG,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentsean/measure",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22.19.0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"drizzle-orm": "0.45.2",
|
|
20
|
+
"@agentsean/db": "1.0.0",
|
|
21
|
+
"@agentsean/google": "1.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "~5.9.3",
|
|
25
|
+
"vitest": "^4.1.11"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/seziro-team/agentsean.git",
|
|
33
|
+
"directory": "packages/measure"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/seziro-team/agentsean#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/seziro-team/agentsean/issues"
|
|
38
|
+
},
|
|
39
|
+
"author": "Seziro (https://seziro.com)",
|
|
40
|
+
"description": "Agent Sean — measure module",
|
|
41
|
+
"keywords": [
|
|
42
|
+
"seo",
|
|
43
|
+
"agent",
|
|
44
|
+
"agentsean",
|
|
45
|
+
"seo-automation"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
50
|
+
"test": "vitest run --config ../../vitest.config.ts"
|
|
51
|
+
}
|
|
52
|
+
}
|