import os, requests, time
LN, MV = os.environ["LINEAR_API_KEY"], os.environ["MAVERA_API_KEY"]
LB, MB = "https://api.linear.app/graphql", "https://app.mavera.io/api/v1"
LH = {"Authorization": LN, "Content-Type": "application/json"}
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
query = """query {
issues(filter: {
labels: { every: { name: { in: ["bug", "customer-reported"] } } }
state: { type: { nin: ["completed", "canceled"] } }
}, first: 40, orderBy: createdAt) {
nodes { identifier title description priority labels { nodes { name } } team { name } }
}
}"""
r = requests.post(LB, headers=LH, json={"query": query})
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60)))
r = requests.post(LB, headers=LH, json={"query": query})
r.raise_for_status()
bugs = r.json()["data"]["issues"]["nodes"]
print(f"Fetched {len(bugs)} customer-reported bugs")
bug_block = "\n\n".join(
f"[{b['identifier']}] {b['title']} (priority: {b.get('priority','none')})\n"
f" Team: {(b.get('team') or {}).get('name','Unknown')}\n"
f" Description: {(b.get('description') or '')[:400]}"
for b in bugs)
SEGMENTS = [
{"name": "Enterprise Admin", "desc": "Manages 200+ person org. Bugs block teams. Values stability and SLAs."},
{"name": "Startup Founder", "desc": "5-person team. Tolerates quirks, not data loss or broken integrations."},
{"name": "Product Manager", "desc": "Relies on tool for roadmap planning. Reporting bugs erode stakeholder trust."},
{"name": "Developer (IC)", "desc": "Lives in tool 8+ hrs/day. API and Git integration matter most."},
]
persona_ids = []
for seg in SEGMENTS:
time.sleep(0.3)
p = requests.post(f"{MB}/personas", headers=MH, json={
"name": f"Bug Triage: {seg['name']}", "description": seg["desc"]}).json()
persona_ids.append(p["id"])
fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
"name": "Customer Bug Severity Assessment", "persona_ids": persona_ids,
"questions": [
f"Here are {len(bugs)} open bugs:\n\n{bug_block[:3000]}\n\nRate top 5 most critical for YOUR workflow (1-10). Explain.",
"Which bugs would make you consider switching to a competitor if unfixed for 30 days?",
"Any bugs that seem minor but compound into major frustration over time?",
], "responses_per_persona": 2,
}).json()
for _ in range(30):
time.sleep(5)
data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
if data.get("status") == "completed":
break
for resp in data.get("responses", []):
idx = persona_ids.index(resp["persona_id"]) if resp.get("persona_id") in persona_ids else -1
name = SEGMENTS[idx]["name"] if 0 <= idx < len(SEGMENTS) else "Unknown"
print(f"\n[{name}] Q: {resp.get('question','')[:80]}\n A: {resp.get('answer','')[:400]}")