-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnorth_star_experiment.py
More file actions
149 lines (124 loc) · 5.25 KB
/
Copy pathnorth_star_experiment.py
File metadata and controls
149 lines (124 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env python3
"""Generate a single weekly experiment brief tied to the North Star gap."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
from scripts import north_star_ops
def _load_ops_payload(repo_root: Path) -> dict[str, Any]:
path = repo_root / "marketing" / "data" / "north_star_ops.json"
if path.exists():
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
payload = {}
if isinstance(payload, dict) and payload.get("next_experiment"):
return payload
return north_star_ops.build_ops_payload(repo_root)
def _measurement_window_days(primary_metric: str) -> int:
return 30 if primary_metric == "open_to_completed_rate" else 7
def _implementation_checklist(primary_focus: str) -> list[str]:
if primary_focus == "activation":
return [
"Keep launch defaults at 0s to 30s on both platforms.",
"Keep setup-screen previews visible for countdown and drill voice cues.",
"Verify first-session setup and start flow with smoke evidence on iOS and Android.",
]
return [
"Promote repeat-loop usage without degrading the default first-session flow.",
"Track repeat usage behavior with existing timer analytics parity.",
"Verify loop and stop behavior with mobile tests before merge.",
]
def build_experiment_brief(repo_root: Path) -> dict[str, Any]:
repo_root = repo_root.resolve()
ops = _load_ops_payload(repo_root)
experiment = ops["next_experiment"]
primary_metric = str(ops["primary_metric"])
return {
"generated_at": ops["generated_at"],
"status": "proposed",
"primary_focus": ops["primary_focus"],
"recommended_next_action": ops["recommended_next_action"],
"experiment": experiment,
"measurement_plan": {
"metric": primary_metric,
"baseline": ops["current_value"],
"target": ops["target_value"],
"gap": ops["gap"],
"window_days": _measurement_window_days(primary_metric),
},
"implementation_checklist": _implementation_checklist(str(ops["primary_focus"])),
"proof_commands": [
"python3 -m pytest -q scripts/tests/",
"cd native-android && ./gradlew testDebugUnitTest",
"cd native-ios && xcodebuild test -project RandomTimer.xcodeproj -scheme RandomTimer -destination 'platform=iOS Simulator,id=<SIMULATOR_ID>' -skip-testing:RandomTimerUITests -quiet CODE_SIGNING_ALLOWED=NO",
],
"warnings": ops.get("warnings", []),
}
def _render_markdown(payload: dict[str, Any]) -> str:
experiment = payload["experiment"]
measurement = payload["measurement_plan"]
checklist = "\n".join(f"- {item}" for item in payload["implementation_checklist"])
proof = "\n".join(f"- `{item}`" for item in payload["proof_commands"])
warnings = payload.get("warnings", [])
warning_lines = "\n".join(f"- {item}" for item in warnings) if warnings else "- none"
return "\n".join(
[
"# North Star Experiment",
"",
f"- Generated: {payload['generated_at']}",
f"- Status: {payload['status']}",
f"- Primary Focus: {payload['primary_focus']}",
"",
"## Experiment",
f"- Slug: {experiment['slug']}",
f"- Target Metric: {experiment['target_metric']}",
f"- Hypothesis: {experiment['hypothesis']}",
f"- Owner: {experiment['owner']}",
"",
"## Measurement Plan",
f"- Metric: {measurement['metric']}",
f"- Baseline: {measurement['baseline']}",
f"- Target: {measurement['target']}",
f"- Gap: {measurement['gap']}",
f"- Window Days: {measurement['window_days']}",
"",
"## Recommended Next Action",
payload["recommended_next_action"],
"",
"## Implementation Checklist",
checklist,
"",
"## Proof Commands",
proof,
"",
"## Warnings",
warning_lines,
"",
]
)
def run(repo_root: Path) -> dict[str, Any]:
repo_root = repo_root.resolve()
data_dir = repo_root / "marketing" / "data"
data_dir.mkdir(parents=True, exist_ok=True)
payload = build_experiment_brief(repo_root)
output_json = data_dir / "north_star_experiment.json"
output_markdown = data_dir / "north_star_experiment.md"
output_json.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
output_markdown.write_text(_render_markdown(payload), encoding="utf-8")
return {
"status": "ok",
"output_json": str(output_json),
"output_markdown": str(output_markdown),
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Build the weekly North Star experiment brief.")
parser.add_argument("--repo-root", default=".", help="Repository root")
return parser.parse_args()
def main() -> None:
args = parse_args()
result = run(Path(args.repo_root))
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()