Leon4gr45 commited on
Commit
69f9a4b
·
verified ·
1 Parent(s): bfe95fb

Upload folder using huggingface_hub (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/use-available-models.ts +58 -0
  2. lib/utils.ts +40 -0
  3. lib/validation/index.ts +29 -0
  4. next.config.mjs +68 -0
  5. package-lock.json +0 -0
  6. package.json +90 -0
  7. postcss.config.mjs +8 -0
  8. product-eval/.claude-plugin/marketplace.json +16 -0
  9. product-eval/.claude-plugin/plugin.json +19 -0
  10. product-eval/.codex-plugin/plugin.json +36 -0
  11. product-eval/.gitignore +12 -0
  12. product-eval/CONNECTORS.md +21 -0
  13. product-eval/DATA-CONTRACT.md +149 -0
  14. product-eval/LICENSE +21 -0
  15. product-eval/README.md +156 -0
  16. product-eval/evals/checks/validate_plugin.py +286 -0
  17. product-eval/evals/trigger/calibrate.json +90 -0
  18. product-eval/evals/trigger/critique.json +90 -0
  19. product-eval/evals/trigger/decision-readiness.json +82 -0
  20. product-eval/evals/trigger/design-test.json +82 -0
  21. product-eval/evals/trigger/discover-sources.json +86 -0
  22. product-eval/evals/trigger/drift-check.json +26 -0
  23. product-eval/evals/trigger/gather-evidence.json +82 -0
  24. product-eval/evals/trigger/pressure-test.json +23 -0
  25. product-eval/evals/trigger/prioritize-backlog.json +23 -0
  26. product-eval/evals/trigger/scorecard.json +82 -0
  27. product-eval/evals/trigger/start.json +102 -0
  28. product-eval/evals/trigger/synthesize.json +28 -0
  29. product-eval/evals/trigger/to-build-brief.json +90 -0
  30. product-eval/evals/trigger/write-brief.json +26 -0
  31. product-eval/evals/triggering.json +33 -0
  32. product-eval/examples/README.md +39 -0
  33. product-eval/examples/activation-q3/context.md +13 -0
  34. product-eval/examples/activation-q3/decisions-log.md +34 -0
  35. product-eval/examples/activation-q3/evidence/E1.md +17 -0
  36. product-eval/examples/activation-q3/evidence/E10.md +17 -0
  37. product-eval/examples/activation-q3/evidence/E11.md +17 -0
  38. product-eval/examples/activation-q3/evidence/E12.md +19 -0
  39. product-eval/examples/activation-q3/evidence/E2.md +17 -0
  40. product-eval/examples/activation-q3/evidence/E3.md +17 -0
  41. product-eval/examples/activation-q3/evidence/E4.md +17 -0
  42. product-eval/examples/activation-q3/evidence/E5.md +17 -0
  43. product-eval/examples/activation-q3/evidence/E6.md +17 -0
  44. product-eval/examples/activation-q3/evidence/E7.md +17 -0
  45. product-eval/examples/activation-q3/evidence/E8.md +17 -0
  46. product-eval/examples/activation-q3/evidence/E9.md +17 -0
  47. product-eval/examples/activation-q3/identities.md +25 -0
  48. product-eval/examples/activation-q3/scorecard.html +305 -0
  49. product-eval/examples/activation-q3/scores.md +41 -0
  50. product-eval/examples/activation-q3/themes.md +57 -0
lib/use-available-models.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { useCallback, useEffect, useState } from "react"
4
+ import { fetchModelsFromProvider, setDeploymentModel, setDiscoveredBrowserModel, type AISettings } from "@/lib/ai-settings"
5
+ import { sanitizeModels, type AvailableModel } from "@/lib/model-selection"
6
+
7
+ /** Shared registry for settings and the main UI. Discovery never changes selection. */
8
+ export function useAvailableModels(settings: AISettings) {
9
+ const [models, setModels] = useState<AvailableModel[]>([])
10
+ const [defaultModel, setDefaultModel] = useState("")
11
+ const [loading, setLoading] = useState(false)
12
+ const [error, setError] = useState<string | null>(null)
13
+ const [revision, setRevision] = useState(0)
14
+ const refresh = useCallback(() => setRevision(n => n + 1), [])
15
+
16
+ useEffect(() => {
17
+ setModels([])
18
+ setDefaultModel("")
19
+ setDiscoveredBrowserModel("")
20
+ }, [settings.apiKey, settings.provider, settings.customBaseUrl])
21
+
22
+ useEffect(() => {
23
+ let cancelled = false
24
+ setLoading(true)
25
+ setError(null)
26
+ const timer = setTimeout(async () => {
27
+ try {
28
+ if (settings.apiKey.trim()) {
29
+ const items = await fetchModelsFromProvider(settings.provider, settings.apiKey.trim(), settings.customBaseUrl)
30
+ if (!cancelled) {
31
+ const available = sanitizeModels(items)
32
+ setModels(available)
33
+ setDiscoveredBrowserModel(available[0]?.id || "")
34
+ }
35
+ } else {
36
+ const response = await fetch("/api/v1/llm/models", { cache: "no-store" })
37
+ if (!response.ok) throw new Error(response.status === 401 ? "Sign in to retrieve models." : "Could not retrieve the provider model list.")
38
+ const data = await response.json()
39
+ if (!cancelled) {
40
+ setDeploymentModel(data.defaultModel || data.models?.[0]?.id || "")
41
+ setDefaultModel(data.defaultModel || "")
42
+ setModels(sanitizeModels(data.models, data.defaultModel))
43
+ setError(data.discoveryStatus === "available" ? null :
44
+ data.discoveryStatus === "auth_error" ? "Model provider authentication failed. Check OPENAI_API in deployment secrets." :
45
+ !data.configured ? "No model provider configured. Set OPENAI_URL, OPENAI_MODEL and OPENAI_API in the deployment environment." :
46
+ `Could not retrieve the provider model list. Using configured default model: ${data.defaultModel || "none"}`)
47
+ }
48
+ }
49
+ } catch {
50
+ if (!cancelled) setError("Could not retrieve the provider model list. Your selected model is unchanged.")
51
+ } finally {
52
+ if (!cancelled) setLoading(false)
53
+ }
54
+ }, 300)
55
+ return () => { cancelled = true; clearTimeout(timer) }
56
+ }, [settings.apiKey, settings.provider, settings.customBaseUrl, revision])
57
+ return { models, defaultModel, loading, error, refresh }
58
+ }
lib/utils.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+ import { useState, useEffect } from 'react'
4
+ import type { TextBlock } from '@/components/tile-card'
5
+
6
+ export function cn(...inputs: ClassValue[]) {
7
+ return twMerge(clsx(inputs))
8
+ }
9
+
10
+ /**
11
+ * Returns the platform modifier key symbol: '⌘' on macOS/iOS, 'Ctrl' elsewhere.
12
+ * Starts as '⌘' on the server (SSR) and corrects on the client after mount.
13
+ */
14
+ export function useModKey(): string {
15
+ const [mod, setMod] = useState('⌘')
16
+ useEffect(() => {
17
+ const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform)
18
+ if (!isMac) setMod('Ctrl')
19
+ }, [])
20
+ return mod
21
+ }
22
+
23
+ /**
24
+ * Returns the set of block IDs that are "connected" to the hovered block,
25
+ * based on shared category or influencedBy relationships.
26
+ * Used by tiling-area and kanban-area for the connection-hover dimming effect.
27
+ */
28
+ export function getRelatedIds(hoveredId: string, blocks: TextBlock[]): Set<string> {
29
+ const hovered = blocks.find(b => b.id === hoveredId)
30
+ if (!hovered) return new Set()
31
+
32
+ const related = new Set<string>([hoveredId])
33
+ blocks.forEach(b => {
34
+ if (b.id === hoveredId) return
35
+ const hoveredPointsToB = hovered.influencedBy?.includes(b.id) ?? false
36
+ const bPointsToHovered = b.influencedBy?.includes(hoveredId) ?? false
37
+ if (hoveredPointsToB || bPointsToHovered) related.add(b.id)
38
+ })
39
+ return related
40
+ }
lib/validation/index.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { KNOWLEDGE_EDGE_TYPES, KNOWLEDGE_NODE_KINDS, type EvidenceRecord, type HypothesisRecord, type KnowledgeEdge, type KnowledgeNode, type SynthesisResult, type Workspace } from "../domain/types"
2
+ import { canPromote, qualificationForScore, scoreHypothesis } from "../scoring/hypothesis"
3
+ export interface ValidationResult { valid:boolean; errors:string[] }
4
+ const result=(errors:string[]):ValidationResult=>({valid:errors.length===0,errors})
5
+ export function validateNode(n:KnowledgeNode){const e:string[]=[];if(!n?.id)e.push("node id required");if(!KNOWLEDGE_NODE_KINDS.includes(n?.kind))e.push("invalid node kind");if(!n?.workspaceId)e.push("workspace required");if(!n?.title)e.push("title required");if(!n?.provenance?.origin||!n.provenance.createdAt)e.push("provenance required");if(!Number.isInteger(n?.version)||n.version<1)e.push("positive version required");return result(e)}
6
+ export function validateEdge(e:KnowledgeEdge,nodeIds?:Set<string>){const x:string[]=[];if(!e?.id||!e.sourceId||!e.targetId)x.push("edge identity/endpoints required");if(!KNOWLEDGE_EDGE_TYPES.includes(e?.type))x.push("invalid edge type");if(e.sourceId===e.targetId)x.push("self edge prohibited");if(nodeIds&&(!nodeIds.has(e.sourceId)||!nodeIds.has(e.targetId)))x.push("unresolved endpoint");if(!e?.provenance?.origin)x.push("edge provenance required");return result(x)}
7
+ export function validateEvidence(e:EvidenceRecord){const x:string[]=[];if(!e.id||!e.workspaceId||!e.claim.trim())x.push("evidence identity/claim required");if(!Number.isInteger(e.strength)||e.strength<1||e.strength>5)x.push("strength must be 1..5");if(!Number.isFinite(e.weight)||e.weight<0)x.push("non-negative weight required");if(!e.sourceType)x.push("source type required");if(!e.ingestedAt||!e.provenance?.origin)x.push("evidence provenance required");return result(x)}
8
+ export function validateTheme(n:KnowledgeNode){const x=validateNode(n).errors;if(n.kind!=="theme")x.push("theme kind required");if(typeof n.metadata.clusterKey!=="string")x.push("clusterKey required");return result(x)}
9
+ export function validateSynthesis(s:SynthesisResult){const x:string[]=[];if(!/^S\d{3}$/.test(s.synthesisType))x.push("invalid synthesis type");if(!s.inputHash||!s.runId||!s.provenance?.origin)x.push("hash/run/provenance required");if(!Array.isArray(s.derivedFrom)||!Array.isArray(s.unknowns)||!Array.isArray(s.generatedHypotheses))x.push("synthesis arrays required");if(s.generationConfidence<0||s.generationConfidence>100)x.push("generation confidence out of range");return result(x)}
10
+ export function validateHypothesis(h:HypothesisRecord){const x:string[]=[];if(!h.id||!h.statement.trim()||!h.hypothesisType)x.push("hypothesis identity/statement/type required");if(h.evidenceScore< -100||h.evidenceScore>100)x.push("score out of range");if(h.confidence<0||h.confidence>100)x.push("confidence out of range");if(h.qualification!==qualificationForScore(h.evidenceScore))x.push("qualification mismatch");return result(x)}
11
+ export function validateExperiment(n:KnowledgeNode){const x=validateNode(n).errors;if(n.kind!=="experiment")x.push("experiment kind required");if(!n.metadata.threshold&&!n.metadata.passThreshold)x.push("experiment threshold required");return result(x)}
12
+ export function validateDecision(n:KnowledgeNode){const x=validateNode(n).errors;if(n.kind!=="decision")x.push("decision kind required");if(!n.metadata.rationale)x.push("decision rationale required");return result(x)}
13
+ export function validateBrainPromotion(w:Workspace){const x:string[]=[];for(const h of w.hypotheses.filter(h=>w.brain.promotedIds.includes(h.id)))if(!canPromote(h))x.push(`${h.id}: promotion gates not met`);return result(x)}
14
+ export function validateAdjudications(w: Workspace): ValidationResult {
15
+ const errors: string[] = [], ids = new Set<string>()
16
+ const evidenceIds = new Set(w.evidence.flatMap(e => [e.id,...(e.aliases || [])]))
17
+ for (const a of w.adjudications || []) {
18
+ if (!a || !a.id || ids.has(a.id)) { errors.push("invalid/duplicate adjudication identity"); continue }
19
+ ids.add(a.id)
20
+ if (!w.hypotheses.some(h=>h.id===a.hypothesisId) || !evidenceIds.has(a.evidenceId)) errors.push(`adjudication ${a.id}: unresolved target`)
21
+ if (!["supports","contradicts","neutral","uncertain"].includes(a.relation) || !["direct","indirect","contextual"].includes(a.directness)) errors.push(`adjudication ${a.id}: invalid interpretation`)
22
+ if (![a.relevance,a.confidence].every(n=>Number.isFinite(n)&&n>=0&&n<=100)) errors.push(`adjudication ${a.id}: invalid interpretation certainty`)
23
+ if (!a.inputHash || !a.runId || !a.provider || !a.promptVersion || !a.schemaVersion || !Number.isFinite(Date.parse(a.createdAt)) || !Array.isArray(a.sourceEvidenceIds) || !Array.isArray(a.semanticContextIds)) errors.push(`adjudication ${a.id}: provenance required`)
24
+ }
25
+ return result(errors)
26
+ }
27
+ export function validateWorkspace(w:Workspace):ValidationResult{const errors:string[]=[];if(w.schemaVersion!==1)errors.push("unsupported schema");const ids=new Set(w.nodes.map(n=>n.id));for(const n of w.nodes)errors.push(...validateNode(n).errors.map(e=>`node ${n.id}: ${e}`));for(const e of w.edges)errors.push(...validateEdge(e,ids).errors.map(x=>`edge ${e.id}: ${x}`));const evid=new Set(w.evidence.map(e=>e.id));for(const e of w.evidence)errors.push(...validateEvidence(e).errors.map(x=>`evidence ${e.id}: ${x}`));for(const h of w.hypotheses){errors.push(...validateHypothesis(h).errors.map(x=>`hypothesis ${h.id}: ${x}`));for(const id of[...h.supportingEvidence,...h.contradictingEvidence])if(!evid.has(id))errors.push(`hypothesis ${h.id}: missing evidence ${id}`);const recomputed=scoreHypothesis(h,w.evidence,Date.parse(h.lastEvaluatedAt??w.updatedAt));if(recomputed.evidenceScore!==h.evidenceScore)errors.push(`hypothesis ${h.id}: score mismatch`)}for(const s of w.syntheses)errors.push(...validateSynthesis(s).errors.map(x=>`synthesis ${s.id}: ${x}`));errors.push(...validateBrainPromotion(w).errors,...validateAdjudications(w).errors);return result(errors)}
28
+ export const validateGitSnapshot=(w:Workspace)=>validateWorkspace(w)
29
+ export function validateWorkflowContracts(w:Workspace){const x:string[]=[];for(const t of w.researchTasks){if(!t.question||!t.objective||!Array.isArray(t.hypothesisIds))x.push(`task ${t.id}: invalid contract`);if(t.attempts>t.maxAttempts)x.push(`task ${t.id}: attempts exceeded`)}return result(x)}
next.config.mjs ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ output: "standalone",
4
+ typescript: {
5
+ // Build errors are intentionally ignored — see CLAUDE.md
6
+ ignoreBuildErrors: true,
7
+ },
8
+ images: {
9
+ unoptimized: true,
10
+ },
11
+ async headers() {
12
+ return [
13
+ {
14
+ // Apply security headers to every route
15
+ source: "/(.*)",
16
+ headers: [
17
+ {
18
+ // Prevent framing (clickjacking)
19
+ key: "X-Frame-Options",
20
+ value: "DENY",
21
+ },
22
+ {
23
+ // Stop MIME-type sniffing
24
+ key: "X-Content-Type-Options",
25
+ value: "nosniff",
26
+ },
27
+ {
28
+ // Limit referrer info sent to third-party origins
29
+ key: "Referrer-Policy",
30
+ value: "strict-origin-when-cross-origin",
31
+ },
32
+ {
33
+ // Permissions policy — disable features the app doesn't use
34
+ key: "Permissions-Policy",
35
+ value: "camera=(), microphone=(), geolocation=()",
36
+ },
37
+ {
38
+ // Content Security Policy
39
+ // - default-src self: everything defaults to same-origin
40
+ // - script-src: Next.js needs 'unsafe-inline' + 'unsafe-eval' in dev;
41
+ // nonces are the proper fix but require custom server — this is the
42
+ // pragmatic baseline for a static/Vercel deployment.
43
+ // - connect-src: https: allows any user-configured custom endpoint
44
+ // - img-src: data URIs for inline images, blob for canvas exports
45
+ // - style-src unsafe-inline: Tailwind injects inline styles at runtime
46
+ key: "Content-Security-Policy",
47
+ value: [
48
+ "default-src 'self'",
49
+ "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cloud.umami.is",
50
+ "style-src 'self' 'unsafe-inline'",
51
+ // Allow all HTTPS so user-configured custom base URLs (arbitrary
52
+ // OpenAI-compatible endpoints) are not blocked by CSP. Enumerating
53
+ // specific provider domains is incompatible with a custom-URL feature.
54
+ // http://localhost:* covers local providers (Ollama, LM Studio, vLLM).
55
+ "connect-src 'self' https: http://localhost:*",
56
+ "img-src 'self' data: blob: https://i.ytimg.com",
57
+ "font-src 'self' data:",
58
+ "frame-src https://www.youtube-nocookie.com https://www.youtube.com",
59
+ "frame-ancestors 'none'",
60
+ ].join("; "),
61
+ },
62
+ ],
63
+ },
64
+ ]
65
+ },
66
+ }
67
+
68
+ export default nextConfig
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nodepad",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "eslint .",
10
+ "test": "tsx --test tests/**/*.test.ts",
11
+ "test:registry": "tsx --test tests/synthesis-registry.test.ts",
12
+ "validate:fixture": "tsx scripts/validate-fixture.ts",
13
+ "heartbeat": "tsx worker/heartbeat.ts",
14
+ "dev:all": "sh -c 'next dev & tsx worker/heartbeat.ts; wait'",
15
+ "test:predeploy:unit": "tsx --import ./scripts/predeploy-env.ts --test tests/**/*.test.ts",
16
+ "test:predeploy:smoke": "tsx scripts/predeploy-smoke.ts",
17
+ "test:predeploy:docker": "tsx scripts/predeploy-docker.ts",
18
+ "test:predeploy": "tsx scripts/predeploy.ts"
19
+ },
20
+ "dependencies": {
21
+ "@hookform/resolvers": "^3.9.1",
22
+ "@radix-ui/react-accordion": "1.2.12",
23
+ "@radix-ui/react-alert-dialog": "1.1.15",
24
+ "@radix-ui/react-aspect-ratio": "1.1.8",
25
+ "@radix-ui/react-avatar": "1.1.11",
26
+ "@radix-ui/react-checkbox": "1.3.3",
27
+ "@radix-ui/react-collapsible": "1.1.12",
28
+ "@radix-ui/react-context-menu": "2.2.16",
29
+ "@radix-ui/react-dialog": "1.1.15",
30
+ "@radix-ui/react-dropdown-menu": "2.1.16",
31
+ "@radix-ui/react-hover-card": "1.1.15",
32
+ "@radix-ui/react-label": "2.1.8",
33
+ "@radix-ui/react-menubar": "1.1.16",
34
+ "@radix-ui/react-navigation-menu": "1.2.14",
35
+ "@radix-ui/react-popover": "1.1.15",
36
+ "@radix-ui/react-progress": "1.1.8",
37
+ "@radix-ui/react-radio-group": "1.3.8",
38
+ "@radix-ui/react-scroll-area": "1.2.10",
39
+ "@radix-ui/react-select": "2.2.6",
40
+ "@radix-ui/react-separator": "1.1.8",
41
+ "@radix-ui/react-slider": "1.3.6",
42
+ "@radix-ui/react-slot": "1.2.4",
43
+ "@radix-ui/react-switch": "1.2.6",
44
+ "@radix-ui/react-tabs": "1.1.13",
45
+ "@radix-ui/react-toast": "1.2.15",
46
+ "@radix-ui/react-toggle": "1.1.10",
47
+ "@radix-ui/react-toggle-group": "1.1.11",
48
+ "@radix-ui/react-tooltip": "1.2.8",
49
+ "@tailwindcss/typography": "^0.5.19",
50
+ "@types/d3": "^7.4.3",
51
+ "autoprefixer": "^10.4.20",
52
+ "class-variance-authority": "^0.7.1",
53
+ "clsx": "^2.1.1",
54
+ "cmdk": "1.1.1",
55
+ "d3": "^7.9.0",
56
+ "date-fns": "4.1.0",
57
+ "embla-carousel-react": "8.6.0",
58
+ "framer-motion": "^11.18.0",
59
+ "input-otp": "1.4.2",
60
+ "lucide-react": "^0.564.0",
61
+ "next": "^16.2.2",
62
+ "next-themes": "^0.4.6",
63
+ "react": "19.2.4",
64
+ "react-day-picker": "9.13.2",
65
+ "react-dom": "19.2.4",
66
+ "react-hook-form": "^7.54.1",
67
+ "react-markdown": "^10.1.0",
68
+ "react-resizable-panels": "^2.1.7",
69
+ "recharts": "2.15.0",
70
+ "remark-gfm": "^4.0.1",
71
+ "sonner": "^1.7.1",
72
+ "tailwind-merge": "^3.3.1",
73
+ "vaul": "^1.1.2",
74
+ "zod": "^3.24.1"
75
+ },
76
+ "devDependencies": {
77
+ "@tailwindcss/postcss": "^4.2.0",
78
+ "@types/node": "^22",
79
+ "@types/react": "19.2.14",
80
+ "@types/react-dom": "19.2.3",
81
+ "eslint": "^9.39.5",
82
+ "eslint-config-next": "^16.3.8",
83
+ "postcss": "^8.5",
84
+ "shadcn": "^4.0.0",
85
+ "tailwindcss": "^4.2.0",
86
+ "tsx": "^4.23.15",
87
+ "tw-animate-css": "1.3.3",
88
+ "typescript": "5.7.3"
89
+ }
90
+ }
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ '@tailwindcss/postcss': {},
5
+ },
6
+ }
7
+
8
+ export default config
product-eval/.claude-plugin/marketplace.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "product-eval-marketplace",
3
+ "owner": {
4
+ "name": "Narek Gevorgyan"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "product-eval",
9
+ "source": "./",
10
+ "version": "0.14.2",
11
+ "description": "Evidence-gated product decisions: scope product context and evidence, then rank opportunities, decide on one bet, pressure-test plans, and write briefs/specs/scorecards.",
12
+ "homepage": "https://github.com/sparkline-ventures/product-eval",
13
+ "repository": "https://github.com/sparkline-ventures/product-eval"
14
+ }
15
+ ]
16
+ }
product-eval/.claude-plugin/plugin.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "product-eval",
3
+ "version": "0.14.2",
4
+ "description": "Evidence-gated product decisions that start by scoping product context, evidence sources, and the desired deliverable before ranking opportunities, deciding on bets, pressure-testing plans, or writing outputs. Keeps evidence gathering, problem framing, Value/Confidence scoring, readiness gates, and audit files behind a simpler user experience.",
5
+ "author": {
6
+ "name": "Narek Gevorgyan"
7
+ },
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/sparkline-ventures/product-eval",
10
+ "repository": "https://github.com/sparkline-ventures/product-eval",
11
+ "keywords": [
12
+ "product-management",
13
+ "prioritization",
14
+ "evidence",
15
+ "roadmap",
16
+ "decision-readiness",
17
+ "pressure-test"
18
+ ]
19
+ }
product-eval/.codex-plugin/plugin.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "product-eval",
3
+ "version": "0.14.2",
4
+ "description": "Evidence-gated product decisions that start by scoping product context, evidence sources, and the desired deliverable before ranking opportunities, deciding on bets, pressure-testing plans, or writing outputs.",
5
+ "author": {
6
+ "name": "Narek Gevorgyan"
7
+ },
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/sparkline-ventures/product-eval",
10
+ "repository": "https://github.com/sparkline-ventures/product-eval",
11
+ "keywords": [
12
+ "product-management",
13
+ "prioritization",
14
+ "evidence",
15
+ "roadmap",
16
+ "decision-readiness",
17
+ "pressure-test"
18
+ ],
19
+ "skills": "./skills/",
20
+ "interface": {
21
+ "displayName": "product-eval",
22
+ "shortDescription": "Scoped, evidence-gated product decisions",
23
+ "longDescription": "Set up product context and evidence sources, then rank opportunities, decide whether one bet is ready, pressure-test plans, and write briefs, build briefs, or scorecards from scoped product evidence.",
24
+ "developerName": "Narek Gevorgyan",
25
+ "category": "Productivity",
26
+ "capabilities": [
27
+ "Interactive",
28
+ "Write"
29
+ ],
30
+ "defaultPrompt": [
31
+ "Set up product-eval for my product.",
32
+ "Help me decide what to build next.",
33
+ "Help me prepare a decision report."
34
+ ]
35
+ }
36
+ }
product-eval/.gitignore ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OS / editor cruft
2
+ .DS_Store
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.pyc
7
+ .venv/
8
+
9
+ # Note: product-eval.plugin (the built package) is intentionally committed.
10
+ # evals/checks/validate_plugin.py verifies it byte-matches the source tree,
11
+ # so rebuild it after any change: rm -f product-eval.plugin && \
12
+ # zip -r -X -q /tmp/pe.plugin . -x '*.plugin' && mv /tmp/pe.plugin product-eval.plugin
product-eval/CONNECTORS.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Connectors
2
+
3
+ This plugin is tool-agnostic in spirit: it works in terms of **evidence source categories**, and uses whatever you connect in each. Nothing is required, `discover-sources` detects what's available, CSV/upload is a universal fallback, and the plugin honestly caps decision confidence by the sources you actually have.
4
+
5
+ The full category → example-tool mapping lives in `skills/discover-sources/references/source-catalog.md`. Summary:
6
+
7
+ | Category | Example tools | Evidence role |
8
+ |---|---|---|
9
+ | Product / behavioral analytics | Amplitude, PostHog, Mixpanel, GA4 | first-party quantitative (strongest) |
10
+ | Reliability / logs / errors | Datadog, Sentry, New Relic | first-party quantitative |
11
+ | Support / tickets | Zendesk, Intercom, Freshdesk | ticket clusters |
12
+ | Voice of customer / calls | Fireflies, Gong, churn & NPS surveys | transcripts / surveys |
13
+ | CRM / sales | Salesforce, HubSpot | PFRs, lost-deal reasons |
14
+ | Feedback / idea mgmt | Productboard, Canny, Aha! | feature requests |
15
+ | Reviews / app stores | G2, Capterra, App/Play Store | third-party qualitative |
16
+ | Community | Reddit, Hacker News, Discord | third-party qualitative |
17
+ | Market / SEO / demand | Semrush, Ahrefs, SimilarWeb | market signal |
18
+ | Issue trackers / changelogs | GitHub, GitLab | bug/feature signal + freshness checks |
19
+ | Identity (CDP) | Segment, RudderStack | resolves the same user/account across the above |
20
+
21
+ Connect any subset. The more first-party quantitative sources you connect (analytics, logs) and the more source-type diversity you have, the higher the confidence ceiling the plugin can reach.
product-eval/DATA-CONTRACT.md ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Contract
2
+
3
+ The file shapes every skill reads and writes inside an investigation scope at `.product-eval/<scope>/`. Keeping these stable is what lets skills hand off reliably. Files are markdown with YAML front-matter (or YAML lists), human-readable and machine-parseable.
4
+
5
+ All paths below are relative to `.product-eval/<scope>/`, except `.product-eval/calibration.yml`, which is workspace-level. Exact computed fields (`weight`, `confidence`, `value`) are stored for audit, calibration, and scorecards. User-facing output should lead with bands and verdicts, not bare point estimates.
6
+
7
+ ## context.md (front-matter)
8
+ ```yaml
9
+ ---
10
+ scope: tula-q3-eval
11
+ job: evaluate # explore | evaluate | validate | pressure-test | draft
12
+ stage: evaluate # sets the crispness bar: explore = lenient, evaluate/validate = strict
13
+ product: "one-line description"
14
+ north_star: "metric"
15
+ personas: [admin, end_user, buyer]
16
+ updated: 2026-06-26
17
+ ---
18
+ ```
19
+
20
+ ## evidence/<id>.md (one file per item)
21
+ ```yaml
22
+ ---
23
+ id: E1
24
+ claim: "state the pain, not the solution"
25
+ source_type: product_analytics # taxonomy below
26
+ source_ref: "Amplitude › Activation funnel"
27
+ persona: [client]
28
+ funnel_stage: activation
29
+ impact_type: activation
30
+ strength: 5 # 1-5 (evidence-quality rubric)
31
+ recency_days: 20
32
+ weight: 12 # computed: base-by-strength × recency factor (evidence-quality rubric)
33
+ person_id: null
34
+ account_id: acct_acme # if identity-resolved
35
+ corroborated_by: [E2]
36
+ stale: false # true if a changelog/release supersedes it
37
+ ---
38
+ optional quote / notes
39
+ ```
40
+ `source_type` taxonomy: product_analytics, support_ticket, sales_call, churn_survey, customer_interview, internal_data, internal_observation, feature_request, app_review, community_post, social_media, competitor_analysis, market_research, blog_or_article.
41
+
42
+ ## identities.md (list)
43
+ ```yaml
44
+ - entity: acct_acme
45
+ grain: account # person | account
46
+ matched_records: [intercom:org_812, hubspot:acct_55, amplitude:grp_acme]
47
+ method: email_domain # email | user_id | email_domain | fuzzy
48
+ match_confidence: high # high | medium | low
49
+ ```
50
+
51
+ ## themes.md (list)
52
+ ```yaml
53
+ - id: T1
54
+ name: "problem-framed, 5-10 words"
55
+ members: [E1, E2]
56
+ personas: [client]
57
+ distinct_accounts: 9
58
+ total_weight: 19
59
+ source_types: [product_analytics, support_ticket]
60
+ trend: up # emerging | up | flat | down | regression
61
+ corroboration: cross-source
62
+ contradictions: []
63
+ confidence: 67 # computed (scoring-framework Confidence formula)
64
+ confidence_band: moderate # low | moderate | high; preferred for chat output
65
+ gaps: ["spam vs ignored?", "funnel confound: agency-abandoned projects"]
66
+ ```
67
+
68
+ ## scores.md (list)
69
+ ```yaml
70
+ - problem: T1
71
+ value: 72 # 0-100 (scoring-framework: persona + funnel + frequency + competitive)
72
+ value_inputs: {persona: 15, funnel: 22, frequency: 25, competitive: 10}
73
+ value_band: high # low | moderate | high; preferred for chat output
74
+ confidence: 67 # 0-100 (scoring-framework Confidence formula)
75
+ confidence_band: moderate # low | moderate | high; preferred for chat output
76
+ recommendation: "Run a research sprint" # ranking tier from the score-to-recommendation matrix
77
+ rationale: "short reasoning"
78
+ override: null # {field, to, reason} if a human overrides
79
+ ```
80
+
81
+ ## decisions-log.md (append-only entries)
82
+ ```yaml
83
+ - date: 2026-06-26
84
+ item: T1
85
+ event: readiness # readiness | override | critique | drift | doc
86
+ verdict: "Run a research sprint"
87
+ confidence: 67
88
+ confidence_band: moderate
89
+ evidence: [E1, E2]
90
+ open: ["spam vs ignored", "funnel confound"]
91
+ contested: []
92
+ note: "short note"
93
+ outcome: # optional; appended later once the bet has closed, read by calibrate
94
+ problem_was_real: confirmed # confirmed | refuted | partial (the calibration target)
95
+ solution_outcome: succeeded # succeeded | failed | mixed (context only, does not tune constants)
96
+ ```
97
+
98
+ `outcome` is added to a `readiness` entry only when the result is known (often a quarter later). `calibrate` reads it to score the constants: it tunes against `problem_was_real`, never `solution_outcome`. See the calibrate reference.
99
+
100
+ ## calibration.yml (workspace-level, optional)
101
+
102
+ Lives at `.product-eval/calibration.yml`, workspace/product level, **not** per-scope. Overrides the shipped scoring defaults for this team/product; written by `calibrate`, read by the scoring skills on top of the defaults (anything omitted → shipped default). Schema in the calibrate reference. Absent → the universal defaults apply.
103
+
104
+ ## Computed fields, single sources of truth
105
+ - `strength` (1-5) and `weight`, `skills/decision-readiness/references/evidence-quality.md`.
106
+ - `confidence` (0-100), `skills/prioritize-backlog/references/scoring-framework.md` (the Confidence formula); stored exactly, displayed as `confidence_band` (Low/Moderate/High) with an optional bounded ±10 logged adjustment, and overridable via `.product-eval/calibration.yml`.
107
+ - `value` (0-100) and the recommendation matrix, same scoring-framework reference; store exact `value`, display `value_band` in chat.
108
+
109
+ Keep these schemas **additive**: skills may add fields, but must not rename or remove the ones above.
110
+
111
+ ## Validation (preflight conformance check)
112
+
113
+ Skills trust each other's files. A malformed `evidence/`, `themes.md`, or `scores.md` must fail **loudly**, not degrade a downstream skill silently. So every consuming skill runs this preflight on the files it is about to read, and if any check fails it **stops and reports the exact file, field, and violation** rather than guessing past it or proceeding on a partial read. The checks are mechanical (no judgement), so a team can also run them as a script; this section is the single source of truth for what "conformant" means.
114
+
115
+ **Band rule (used everywhere):** `band(x)` = Low if `x < 40`, Moderate if `40 <= x <= 69`, High if `x >= 70`. A stored `*_band` that disagrees with its stored number is a violation.
116
+
117
+ **`evidence/<id>.md`**
118
+ - Required keys present: `id`, `claim`, `source_type`, `strength`, `weight`, `stale`.
119
+ - `id` is unique across the folder and matches the filename (`E1` in `E1.md`).
120
+ - `source_type` is in the taxonomy above; `strength` is an integer 1 to 5; `weight` is a number `>= 0`; `recency_days` (if present) is a non-negative integer; `stale` is a boolean.
121
+ - Every id in `corroborated_by` / `contradicts` resolves to a real evidence file.
122
+ - `person_id` / `account_id`, when not null, resolve to an entity in `identities.md`.
123
+
124
+ **`themes.md`**
125
+ - Each theme has a unique `id`, a `name`, and a non-empty `members` list; every member resolves to a real evidence id.
126
+ - `total_weight` equals the sum of member weights within rounding tolerance (`+/- 1`).
127
+ - `source_types` is a subset of the taxonomy and is consistent with the members' source types.
128
+ - `confidence` is present (0 to 100) and `confidence_band` satisfies the band rule.
129
+ - Every id in `contradictions` resolves to a real evidence id.
130
+
131
+ **`scores.md`**
132
+ - Each entry's `problem` resolves to a theme id in `themes.md`.
133
+ - `value` is 0 to 100 and `value_inputs` (`persona + funnel + frequency + competitive`) sums to `value`.
134
+ - `value_band` and `confidence_band` satisfy the band rule.
135
+ - `confidence` equals the same item's `confidence` in `themes.md` (one source of truth; readiness reads, it does not recompute a different number).
136
+ - `recommendation` is one of the four verdicts (Decide now / Run a research sprint / Do not commit yet / Deprioritize) and matches what the score-to-recommendation matrix produces from (`value`, `confidence`), unless `override` is set and carries a reason.
137
+ - `override` is null or has `{field, to, reason}`.
138
+
139
+ **`decisions-log.md`**
140
+ - Append-only: existing entries are never edited or deleted, only added to.
141
+ - Each entry has `date`, `item`, `event`, `verdict`; `verdict` is in the four-verdict vocabulary; `event` is one of readiness / override / critique / drift / doc.
142
+ - Every id in `evidence` / `contested` resolves to a real evidence id; `confidence_band` satisfies the band rule.
143
+ - If `outcome` is present: `problem_was_real` is one of confirmed / refuted / partial, and `solution_outcome` (if present) is one of succeeded / failed / mixed.
144
+
145
+ **Cross-file**
146
+ - Every theme referenced by `scores.md` and `decisions-log.md` exists in `themes.md`.
147
+ - `context.md` front-matter is present and has at least `scope`, `job`, `stage`.
148
+
149
+ A passing preflight means the files are structurally sound and internally consistent; it does **not** vouch for whether the evidence is true. These checks are also implemented as an optional script, `scripts/validate_scope.py` (run `python scripts/validate_scope.py .product-eval/<scope>/`; needs PyYAML, exits non-zero on any violation), for CI or a pre-commit hook. `examples/activation-q3/` is a conformant fixture to test the checks against.
product-eval/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Narek Gevorgyan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
product-eval/README.md ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # product-eval
2
+
3
+ **Find what's worth building next, with the evidence to prove it.**
4
+
5
+ ![A product-eval decision scorecard, generated from real support tickets](docs/scorecard.png)
6
+
7
+ product-eval discovers and analyzes real customer problems from the tools where they already live: support tickets (Zendesk), CRM (Salesforce, HubSpot), issue trackers (Jira), product analytics, and reviews. It weighs the evidence, ranks what matters, and builds a prioritized, evidence-backed narrative of the problem worth solving.
8
+
9
+ When a bet is ready, hand it off: export a brief or decision memo, or pass a build spec straight to a wireframing tool or an AI coding agent like Codex or Claude Code to ship an MVP. It runs in both Claude Code and Codex, and keeps the rigor (evidence weighting, readiness gates, audit files) behind a simple, outcome-first chat.
10
+
11
+ It helps you answer four practical questions:
12
+
13
+ - **Rank opportunities:** what should we build next?
14
+ - **Decide on one bet:** is this worth building now, or do we need more proof?
15
+ - **Pressure-test a plan:** where is this roadmap, PRD, or strategy weak before we commit?
16
+ - **Write the output:** turn an approved decision into a brief, decision memo, build spec, or visual scorecard.
17
+
18
+ The plugin keeps the rigor behind the scenes: it can discover available sources, gather and weight evidence, synthesize themes into buildable problems, score Value and Confidence, check decision readiness, and produce decision artifacts. Users should not need to choose those internal steps directly; they can describe the outcome they want.
19
+
20
+ ## Install
21
+
22
+ ![The product-eval plugin and its 14 skills](docs/install-page.png)
23
+
24
+ **Cowork:** open the `.plugin` file. It renders as an installable card; click to add it.
25
+
26
+ **Claude Code:** add this directory or its hosting repo as a plugin marketplace, then install:
27
+
28
+ ```text
29
+ /plugin marketplace add <path-or-repo>
30
+ /plugin install product-eval
31
+ ```
32
+
33
+ **Codex:** this source is Codex-compatible through `.codex-plugin/plugin.json`. For Codex marketplace installation, place or symlink this folder as `<marketplace-root>/plugins/product-eval`, make sure that marketplace root is registered with Codex, then install `product-eval` from that marketplace. This repo cleanup does not create or edit global Codex marketplace config such as `~/.agents/plugins/marketplace.json`.
34
+
35
+ **Quick install (either tool, no marketplace):** clone this repo and run `./install.sh`. It symlinks the 14 skills into `~/.claude/skills` and `~/.agents/skills`, so a later `git pull` keeps them current. In Codex, invoke a skill explicitly with `$<skill-name>` (for example `$start`).
36
+
37
+ After installing, start by setting up the product context and decision:
38
+
39
+ - "Set up product-eval for my product."
40
+ - "Help me decide what to build next."
41
+ - "Help me prepare a decision report."
42
+ - "Pressure-test this Q3 roadmap using the evidence in our current scope."
43
+
44
+ On first run, product-eval should ask for the product, target users, decision goal, scope/time horizon, available evidence sources, and the metric or business constraint that should shape the recommendation. It should not rank a vague area like "onboarding" or judge a bet like "SSO" until the product context and evidence plan are clear, unless you explicitly ask for an outside-in first pass with a lower confidence ceiling.
45
+
46
+ Each step should end with a **Next move** that points toward delivery: upload the evidence, rank the candidates, run the readiness call, pressure-test the plan, write the decision memo, render the scorecard, or hand a build brief to Codex/design.
47
+
48
+ ## 60-second demo
49
+
50
+ Point it at your customer signal and ask for the outcome. For example, hand it a support-ticket export in Codex:
51
+
52
+ > "These are our support tickets. Can you analyze them?"
53
+
54
+ product-eval runs the full chain (gather and weight evidence, synthesize themes, score Value and Confidence, run the readiness gate) and returns a ranked shortlist with a verdict on each.
55
+
56
+ ![Ranking opportunities from a Zendesk ticket export, in Codex](docs/demo-rank.png)
57
+
58
+ Ask for a scorecard and it builds a self-contained, shareable HTML dashboard:
59
+
60
+ > "Can you build a scorecard?"
61
+
62
+ ![product-eval generating a decision scorecard from the evidence](docs/demo-scorecard-codex.png)
63
+
64
+ No data of your own? It also works top-down from a domain ("research the observability tooling space and tell me what's worth building"), reasoning from public signal and stating the resulting confidence ceiling. You can also open a ready-made example with zero setup: `examples/activation-q3/scorecard.html`.
65
+
66
+ ## Outcome Modes
67
+
68
+ ### Rank Opportunities
69
+
70
+ Use this when you have a product area, evidence pile, or backlog and want a ranked shortlist. The plugin gathers or reads evidence, turns themes into buildable problem statements, scores each item by Value and Confidence, and returns a ranked table with the best next move.
71
+
72
+ ### Decide On One Bet
73
+
74
+ Use this when you have one idea or problem and need a yes/no readiness call. The output leads with one of three outcomes:
75
+
76
+ - **Decide now:** enough credible, diverse evidence to commit.
77
+ - **Run a research sprint:** promising, but one specific proof gap should be closed first.
78
+ - **Do not commit yet:** evidence is too weak, vague, stale, or single-source.
79
+
80
+ Evidence math is kept visible for audit, but the decision is presented as a banded verdict rather than false precision.
81
+
82
+ ### Pressure-Test A Plan
83
+
84
+ Use this when a roadmap, PRD, strategy, or bet already exists and you want the weak spots surfaced before committing. The output is blocking issues, suggestions, strengths, and a ready / needs-fixes / not-ready verdict.
85
+
86
+ ### Write The Output
87
+
88
+ Use this after a decision has enough evidence or when you need a decision artifact:
89
+
90
+ - **Opportunity Brief:** persuade and align humans around the problem, why now, impact, and ask.
91
+ - **Decision Memo:** force a specific yes/no or option choice with criteria, metrics, tradeoffs, and deadline.
92
+ - **Build Brief:** hand an approved decision to a code or design agent with testable requirements.
93
+ - **Scorecard:** render ranked opportunities and verdicts as a shareable HTML dashboard.
94
+
95
+ Use **critique** when you want an independent adversarial read of findings, a problem statement, a brief, or an FAQ. Use **pressure-test** when you already have a roadmap, PRD, plan, or bet and want to find the weak spots before committing.
96
+
97
+ ## Decision Model
98
+
99
+ A decision passes a two-stage gate:
100
+
101
+ 1. **Sufficiency gate:** blocks if evidence weight is too low, all evidence is weak, evidence comes from only one independent source type, or the problem statement is vague.
102
+ 2. **Confidence verdict:** combines evidence weight, source diversity, contradictions, and Value into a recommendation.
103
+
104
+ The chat output should show **bands first**: Low / Moderate / High Confidence and the resulting verdict. Exact Value and Confidence numbers are stored in `.product-eval/<scope>/` files for audit, calibration, and scorecards.
105
+
106
+ ## State: `.product-eval/<scope>/`
107
+
108
+ All investigation state lives inside the user's project repo so it versions with code and can be reviewed in pull requests. Each investigation gets its own scope:
109
+
110
+ ```text
111
+ .product-eval/
112
+ <scope>/
113
+ context.md business context: product, north star, personas
114
+ sources.md discovered sources, coverage, confidence ceiling
115
+ evidence/ one file per evidence item
116
+ identities.md resolved person/account map across sources
117
+ themes.md problem-framed themes with gaps and confidence
118
+ scores.md exact Value + Confidence numbers for audit
119
+ decisions-log.md append-only verdicts, overrides, drift, docs
120
+ snapshots/ timestamped copies for drift checks
121
+ docs/ briefs and decision memos
122
+ build/ agent-ready build/design briefs
123
+ calibration.yml optional workspace-level scoring overrides
124
+ ```
125
+
126
+ If `.product-eval/<scope>/` does not exist, the first run creates or proposes a short scope name. Skills degrade gracefully when files or sources are missing and state the resulting confidence ceiling.
127
+
128
+ ## Validation
129
+
130
+ Run the plugin/package checks:
131
+
132
+ ```text
133
+ python3 evals/checks/validate_plugin.py
134
+ ```
135
+
136
+ Run the scope fixture validator after installing its dependency:
137
+
138
+ ```text
139
+ python3 -m pip install -r scripts/requirements.txt
140
+ python3 scripts/validate_scope.py examples/activation-q3
141
+ ```
142
+
143
+ ## Design Principles
144
+
145
+ - **Outcome-first UX.** Users ask for Rank, Decide, Pressure-test, or Write; internal skills do the routing.
146
+ - **Evidence as described, not ground truth.** The plugin rates credibility, diversity, and recency. It does not prove that every cited number is objectively true.
147
+ - **Honest ceilings.** Every verdict states the maximum confidence the connected or provided sources allow and how to raise it.
148
+ - **Evidence is weighted, not counted.** Strong first-party findings outweigh many weak anecdotes.
149
+ - **Plain files, no daemon.** Skills read and write markdown/YAML under `.product-eval/<scope>/`; git provides history.
150
+
151
+ ## Roadmap
152
+
153
+ - Calibration and reality-testing against more closed decisions.
154
+ - Trigger eval hardening for the outcome modes and scorecard.
155
+ - Cross-scope portfolio view: a roll-up of what is decided, waiting on evidence, or drifting across investigations.
156
+ - Scale-out clustering for large evidence corpora when map-reduce synthesis is not enough.
product-eval/evals/checks/validate_plugin.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Validate product-eval plugin source, evals, and packaged artifact."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import re
8
+ import sys
9
+ import zipfile
10
+ from pathlib import Path
11
+
12
+
13
+ ROOT = Path(__file__).resolve().parents[2]
14
+ PACKAGE = ROOT / "product-eval.plugin"
15
+ PARENT_PACKAGE = ROOT.parent / "product-eval.plugin"
16
+ CLAUDE_MANIFEST = ROOT / ".claude-plugin" / "plugin.json"
17
+ CLAUDE_MARKETPLACE = ROOT / ".claude-plugin" / "marketplace.json"
18
+ CODEX_MANIFEST = ROOT / ".codex-plugin" / "plugin.json"
19
+ EXCLUDED_PATH_PREFIXES = (
20
+ "skills/critique/",
21
+ "evals/trigger/critique.json",
22
+ "evals/checks/validate_plugin.py",
23
+ )
24
+ IGNORED_SOURCE_NAMES = {".DS_Store", "product-eval.plugin"}
25
+ LEGACY_PM_PATHS = (
26
+ ".pm/context",
27
+ ".pm/sources",
28
+ ".pm/evidence",
29
+ ".pm/identities",
30
+ ".pm/themes",
31
+ ".pm/scores",
32
+ ".pm/decisions-log",
33
+ ".pm/snapshots",
34
+ )
35
+ ALLOWED_FRONTMATTER = {"name", "description", "license", "allowed-tools", "metadata"}
36
+
37
+
38
+ def fail(message: str) -> None:
39
+ print(f"ERROR: {message}", file=sys.stderr)
40
+ raise SystemExit(1)
41
+
42
+
43
+ def load_json(path: Path):
44
+ try:
45
+ with path.open("r", encoding="utf-8") as handle:
46
+ return json.load(handle)
47
+ except Exception as exc: # noqa: BLE001 - validation script should report exact file.
48
+ fail(f"invalid JSON in {path.relative_to(ROOT)}: {exc}")
49
+
50
+
51
+ def read_archive_json(archive: zipfile.ZipFile, path: str):
52
+ try:
53
+ return json.loads(archive.read(path))
54
+ except Exception as exc: # noqa: BLE001 - package validator should report exact file.
55
+ fail(f"packaged {path} missing or invalid: {exc}")
56
+
57
+
58
+ def manifest_identity(payload: dict, label: str) -> tuple[str, str]:
59
+ name = payload.get("name")
60
+ version = payload.get("version")
61
+ if not isinstance(name, str) or not name.strip():
62
+ fail(f"{label} missing name")
63
+ if not isinstance(version, str) or not version.strip():
64
+ fail(f"{label} missing version")
65
+ return name, version
66
+
67
+
68
+ def marketplace_entry(payload: dict, label: str, expected_name: str) -> dict:
69
+ plugins = payload.get("plugins")
70
+ if not isinstance(plugins, list) or not plugins:
71
+ fail(f"{label} missing plugins array")
72
+ for entry in plugins:
73
+ if isinstance(entry, dict) and entry.get("name") == expected_name:
74
+ return entry
75
+ if len(plugins) == 1 and isinstance(plugins[0], dict):
76
+ return plugins[0]
77
+ fail(f"{label} missing plugin entry for {expected_name!r}")
78
+
79
+
80
+ def validate_identity(label: str, name: str, version: str, expected_name: str, expected_version: str) -> None:
81
+ if name != expected_name:
82
+ fail(f"{label} name mismatch: {name!r} != {expected_name!r}")
83
+ if version != expected_version:
84
+ fail(f"{label} version mismatch: {version!r} != {expected_version!r}")
85
+
86
+
87
+ def validate_json() -> None:
88
+ for path in sorted((ROOT / "evals").rglob("*.json")):
89
+ load_json(path)
90
+
91
+
92
+ def validate_manifest_identity() -> None:
93
+ if not CODEX_MANIFEST.exists():
94
+ fail("missing .codex-plugin/plugin.json")
95
+
96
+ claude_manifest = load_json(CLAUDE_MANIFEST)
97
+ codex_manifest = load_json(CODEX_MANIFEST)
98
+ marketplace = load_json(CLAUDE_MARKETPLACE)
99
+
100
+ expected_name, expected_version = manifest_identity(
101
+ claude_manifest, ".claude-plugin/plugin.json"
102
+ )
103
+ if ROOT.name != expected_name:
104
+ fail(f"plugin folder name {ROOT.name!r} does not match manifest name {expected_name!r}")
105
+
106
+ codex_name, codex_version = manifest_identity(codex_manifest, ".codex-plugin/plugin.json")
107
+ validate_identity(
108
+ ".codex-plugin/plugin.json",
109
+ codex_name,
110
+ codex_version,
111
+ expected_name,
112
+ expected_version,
113
+ )
114
+
115
+ entry = marketplace_entry(
116
+ marketplace, ".claude-plugin/marketplace.json", expected_name
117
+ )
118
+ entry_name = entry.get("name")
119
+ entry_version = entry.get("version")
120
+ validate_identity(
121
+ ".claude-plugin/marketplace.json plugin entry",
122
+ entry_name,
123
+ entry_version,
124
+ expected_name,
125
+ expected_version,
126
+ )
127
+
128
+
129
+ def validate_route_coverage() -> None:
130
+ skills = sorted(path.parent.name for path in (ROOT / "skills").glob("*/SKILL.md"))
131
+ triggering = load_json(ROOT / "evals" / "triggering.json")
132
+ listed = sorted(triggering.get("skills", []))
133
+ if listed != skills:
134
+ fail(f"triggering.json skills do not match source skills: listed={listed}, source={skills}")
135
+
136
+ trigger_files = sorted(path.stem for path in (ROOT / "evals" / "trigger").glob("*.json"))
137
+ if trigger_files != skills:
138
+ fail(f"per-skill trigger evals do not match source skills: evals={trigger_files}, source={skills}")
139
+
140
+ for skill in skills:
141
+ cases = load_json(ROOT / "evals" / "trigger" / f"{skill}.json")
142
+ if not isinstance(cases, list) or not cases:
143
+ fail(f"missing trigger cases for {skill}")
144
+ positives = [case for case in cases if case.get("should_trigger") is True]
145
+ negatives = [case for case in cases if case.get("should_trigger") is False]
146
+ if not positives or not negatives:
147
+ fail(f"{skill} trigger eval must include positive and negative cases")
148
+
149
+
150
+ def parse_simple_frontmatter(path: Path) -> dict[str, str]:
151
+ text = path.read_text(encoding="utf-8")
152
+ if not text.startswith("---\n"):
153
+ fail(f"{path.relative_to(ROOT)} missing YAML frontmatter")
154
+ end = text.find("\n---", 4)
155
+ if end == -1:
156
+ fail(f"{path.relative_to(ROOT)} has unterminated YAML frontmatter")
157
+
158
+ data: dict[str, str] = {}
159
+ for raw_line in text[4:end].splitlines():
160
+ line = raw_line.strip()
161
+ if not line or line.startswith("#"):
162
+ continue
163
+ if ":" not in line:
164
+ fail(f"{path.relative_to(ROOT)} has unsupported frontmatter line: {raw_line!r}")
165
+ key, value = line.split(":", 1)
166
+ key = key.strip()
167
+ if key not in ALLOWED_FRONTMATTER:
168
+ fail(f"{path.relative_to(ROOT)} has unexpected frontmatter key {key!r}")
169
+ data[key] = value.strip().strip('"').strip("'")
170
+ return data
171
+
172
+
173
+ def validate_skill_frontmatter() -> None:
174
+ for skill_md in sorted((ROOT / "skills").glob("*/SKILL.md")):
175
+ rel = skill_md.relative_to(ROOT)
176
+ frontmatter = parse_simple_frontmatter(skill_md)
177
+ name = frontmatter.get("name")
178
+ description = frontmatter.get("description")
179
+ if not name:
180
+ fail(f"{rel} missing name")
181
+ if not re.fullmatch(r"[a-z0-9-]{1,64}", name):
182
+ fail(f"{rel} has invalid skill name {name!r}")
183
+ if name != skill_md.parent.name:
184
+ fail(f"{rel} name {name!r} does not match folder {skill_md.parent.name!r}")
185
+ if not description:
186
+ fail(f"{rel} missing description")
187
+ if "<" in description or ">" in description:
188
+ fail(f"{rel} description contains angle brackets")
189
+ if len(description) > 1024:
190
+ fail(f"{rel} description is too long ({len(description)} chars)")
191
+ lower_description = description.lower()
192
+ if "do not trigger" in lower_description or "deprecated" in lower_description:
193
+ fail(f"{rel} is still packaged as a routable skill but its description says not to trigger it")
194
+
195
+
196
+ def validate_pm_paths() -> None:
197
+ for path in sorted(ROOT.rglob("*")):
198
+ if not path.is_file():
199
+ continue
200
+ rel = path.relative_to(ROOT).as_posix()
201
+ if rel.startswith(EXCLUDED_PATH_PREFIXES):
202
+ continue
203
+ if path.name in IGNORED_SOURCE_NAMES:
204
+ continue
205
+ try:
206
+ text = path.read_text(encoding="utf-8")
207
+ except UnicodeDecodeError:
208
+ continue
209
+ for legacy in LEGACY_PM_PATHS:
210
+ if legacy in text:
211
+ fail(f"legacy unscoped path {legacy!r} found in {rel}")
212
+
213
+
214
+ def source_files() -> set[str]:
215
+ files: set[str] = set()
216
+ for path in ROOT.rglob("*"):
217
+ if not path.is_file():
218
+ continue
219
+ if path.name in IGNORED_SOURCE_NAMES:
220
+ continue
221
+ files.add(path.relative_to(ROOT).as_posix())
222
+ return files
223
+
224
+
225
+ def validate_package() -> None:
226
+ if PARENT_PACKAGE.exists():
227
+ fail(f"stale parent-level product-eval.plugin exists at {PARENT_PACKAGE}")
228
+ if not PACKAGE.exists():
229
+ fail("product-eval.plugin is missing")
230
+
231
+ source = source_files()
232
+ with zipfile.ZipFile(PACKAGE) as archive:
233
+ packaged_files = {
234
+ name
235
+ for name in archive.namelist()
236
+ if not name.endswith("/")
237
+ and Path(name).name not in IGNORED_SOURCE_NAMES
238
+ }
239
+ missing = sorted(source - packaged_files)
240
+ extra = sorted(packaged_files - source)
241
+ if missing or extra:
242
+ fail(f"package/source file mismatch: missing={missing}, extra={extra}")
243
+
244
+ for rel in sorted(source):
245
+ if archive.read(rel) != (ROOT / rel).read_bytes():
246
+ fail(f"package/source content mismatch: {rel}")
247
+
248
+ packaged_claude_manifest = read_archive_json(archive, ".claude-plugin/plugin.json")
249
+ packaged_codex_manifest = read_archive_json(archive, ".codex-plugin/plugin.json")
250
+ packaged_marketplace = read_archive_json(archive, ".claude-plugin/marketplace.json")
251
+
252
+ source_manifest = load_json(CLAUDE_MANIFEST)
253
+ expected_name, expected_version = manifest_identity(
254
+ source_manifest, ".claude-plugin/plugin.json"
255
+ )
256
+ for label, payload in (
257
+ ("packaged .claude-plugin/plugin.json", packaged_claude_manifest),
258
+ ("packaged .codex-plugin/plugin.json", packaged_codex_manifest),
259
+ ):
260
+ name, version = manifest_identity(payload, label)
261
+ validate_identity(label, name, version, expected_name, expected_version)
262
+
263
+ packaged_entry = marketplace_entry(
264
+ packaged_marketplace, "packaged .claude-plugin/marketplace.json", expected_name
265
+ )
266
+ validate_identity(
267
+ "packaged .claude-plugin/marketplace.json plugin entry",
268
+ packaged_entry.get("name"),
269
+ packaged_entry.get("version"),
270
+ expected_name,
271
+ expected_version,
272
+ )
273
+
274
+
275
+ def main() -> None:
276
+ validate_json()
277
+ validate_manifest_identity()
278
+ validate_route_coverage()
279
+ validate_skill_frontmatter()
280
+ validate_pm_paths()
281
+ validate_package()
282
+ print("product-eval validation passed")
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
product-eval/evals/trigger/calibrate.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "the confidence numbers feel too generous lately, can we tune the thresholds against these 8 past launches where we already know how they turned out",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "our model keeps flashing Go on stuff that later flopped. here are 12 shipped bets with what actually happened, fix the weights so it stops doing that",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "i think the gate cutoff is too loose. check it against past calls and tell me what number to move it to",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "validate the scoring against calibration/cases.md and flag where the verdict didn't match the real outcome",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "we've got a sheet of 15 old feature decisions with their actual results. use them to see if our evidence weights are even right",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "the crispness bar feels off, sanity-check it against the problems we actually shipped vs the ones we killed",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "three things it scored Research turned out great. that's a false-negative pattern, right? adjust the constants",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "backtest the scoring formula on our 2025 launches and recommend new threshold values",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "are the diversity factors and the gate threshold actually calibrated? here are 10 past decisions with known outcomes to test against",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "i don't trust the confidence math anymore. we have a stack of past bets and their outcomes, figure out where the scoring is systematically wrong",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "is it worth building SSO before our enterprise push, and do we even have enough data to make that call yet?",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "does the SSO bet have enough evidence behind it to greenlight, and what's still missing?",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "the confidence score on this one opportunity looks way too high. is it actually ready to decide or not?",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "rank these 8 feature ideas by what's most worth doing, we've got analytics plus support data",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "review our north-star and activation metrics for the month and flag what moved",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "our activation rate dropped 4 points last week, dig into the numbers and tell me why",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "poke holes in our Q3 roadmap before the leadership review on friday",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "grade how crisp this problem statement is: 'onboarding is bad'",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "has our top opportunity drifted since last month, did confidence actually improve or are we just spinning?",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is",
80
+ "should_trigger": false
81
+ },
82
+ {
83
+ "query": "turn this pile of 30 user interview transcripts into the top themes ranked by how often they came up",
84
+ "should_trigger": false
85
+ },
86
+ {
87
+ "query": "build me a scorecard of this quarter's product metrics with the trends called out for the exec review",
88
+ "should_trigger": false
89
+ }
90
+ ]
product-eval/evals/trigger/critique.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "grade how crisp this problem statement is: 'onboarding is bad'",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "is this one-liner actually sharp enough to take into a brief? 'we need better reporting'",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "poke holes in the findings from our churn investigation before I write them up",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "red-team this FAQ we drafted for the SSO opportunity brief, attack the weak answers",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "is this crisp enough? 'users want a faster dashboard' - score it and tell me what's missing",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "before we move from evidence to scoring, can someone fresh tear into our themes handoff and flag what's not ready",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "put these findings in front of a skeptical customer, a CFO, and an engineer and see where they fall apart",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "gate this opportunity brief before it advances to the decision stage, don't rubber-stamp it",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "my one-liner is 'reduce friction in checkout' - how tight is it and can you rewrite it sharper",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "give the conclusions in this synthesis doc a fresh adversarial read from someone who wasn't in the room",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "does this problem statement sit at the right altitude and actually name a persona, or is it mush?",
44
+ "should_trigger": true
45
+ },
46
+ {
47
+ "query": "poke holes in our Q3 roadmap before the leadership review on friday",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "stress-test our whole go-to-market plan for the enterprise tier before we commit to it",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "adversarially pressure-test this PRD and the bet behind it before we green-light the sprint",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "let's brainstorm some new directions for our analytics product, i want a sharp thinking partner",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "be my sparring partner and help me explore the problem space around activation, throw ideas at me",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "is it worth building SSO before our enterprise push? do we even have the data to call it?",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "synthesize the evidence we just pulled into the main problem-themes",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "here are our raw survey responses and a stack of support tickets, make sense of them into findings",
80
+ "should_trigger": false
81
+ },
82
+ {
83
+ "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is",
84
+ "should_trigger": false
85
+ },
86
+ {
87
+ "query": "reprioritize the roadmap into now/next/later now that the SSO bet jumped the queue",
88
+ "should_trigger": false
89
+ }
90
+ ]
product-eval/evals/trigger/decision-readiness.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "is it worth building SSO before our enterprise push? do we even have the data to call it?",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "do we have enough evidence to greenlight the in-app notifications rebuild, or are we just guessing?",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "are we ready to commit to the billing rework? tell me what's still missing before i say yes",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "we've got ~40 intercom tickets and an amplitude drop-off chart pointing at onboarding step 3. is that actually enough to decide to fix it?",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "honestly, what's the evidence behind shipping a mobile app this half? strong enough to pull the trigger or not?",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "before i tell eng to start, can you judge whether the dark-mode request has enough signal to justify it",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "i keep going back and forth on the API rate-limit feature. do we have the right to make this call yet, and what gaps should i close first?",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "gut check: is team-permissions a defensible build given what we actually know, or do we need more data before committing?",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "we think churned accounts wanted Slack alerts. is that conviction earned by the evidence, or wishful thinking? want a yes/no and the holes",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "my PM swears the saved-views feature is a slam dunk. can you sanity-check whether the evidence actually backs committing to it?",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "rank these 8 feature ideas by what's most worth doing, we have analytics + support data",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "which of our three enterprise bets has the strongest case? line them up and score them so we know where to spend",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "poke holes in our Q3 roadmap before the leadership review on friday",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "stress-test this PRD for the new onboarding flow before we commit to it next sprint",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "review our north-star and activation metrics for the month and flag what moved",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "how did retention and DAU trend last quarter vs target? give me the scorecard",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "here are 200 raw survey responses, make sense of them and rank the findings by frequency",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "grade how crisp this problem statement is: 'onboarding is bad'",
80
+ "should_trigger": false
81
+ }
82
+ ]
product-eval/evals/trigger/design-test.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "decision-readiness says run a research sprint for SSO — design the cheapest test to close the gap",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "how do we validate this billing bet before engineering starts building?",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "what's the cheapest experiment that would de-risk the notifications idea?",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "turn these readiness gaps into a concrete validation test with a pass fail threshold",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "we need a smoke test for the enterprise audit logs bet before committing the quarter",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "design a quick fake-door test to see if customers actually want scheduled csv exports",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "the idea is promising but evidence is thin; tell me the fastest research sprint to run",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "what would prove or disprove this onboarding hypothesis in two weeks?",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "give me the riskiest assumption, method, sample, instrumentation, duration and pass fail bar",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "de-risk this feature idea with a small experiment before we write a spec",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "pull the recurring complaints from zendesk and g2 and grade how solid each signal is",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "is SSO worth building before the enterprise push, do we have enough evidence?",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "rank these 8 feature ideas by what's most worth doing",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "write the opportunity brief for the approved billing decision",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "turn the approved decision into a build spec for claude code",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "pressure-test our Q3 roadmap before leadership review",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "what product-data sources do I have connected?",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "synthesize the evidence we pulled into problem themes",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "make a scorecard from the ranked opportunities",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "update our Now Next Later roadmap with the new SSO epic",
80
+ "should_trigger": false
81
+ }
82
+ ]
product-eval/evals/trigger/discover-sources.json ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "what data do I even have to make product decisions? scan my connectors and tell me the confidence ceiling",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "scan my sources and tell me where product insights could realistically come from",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "what can I connect for product decisions right now? show me whats callable and whats missing",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "im starting fresh in this repo, before we frame anything map out where my product data actually lives",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "do i have intercom, amplitude, and zendesk hooked up? give me an inventory of my product-data sources and how strong each can get",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "whats the best decision confidence i can realistically hit given only the sources i have access to today?",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "build me a source map of my product evidence and flag what i'd have to upload as a CSV since it cant be connected",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "i just want the landscape of where signal could come from, not the actual data pulled yet",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "refresh the source inventory in .product-eval/onboarding/sources.md, i think i wired up linear and pendo since we last looked",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "new project, what's the ceiling on how confident any call could be with what i've got connected?",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "now that you know where my data lives, go ahead and pull everything from intercom and amplitude into the scope",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "collect the last 90 days of support tickets and reviews and normalize them so we can analyze",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "synthesize the evidence we just gathered into the main problem themes",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "i have a folder of 30 user-interview transcripts, pull out the main themes and insights for me",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "here are 200 raw survey responses we just got back, make sense of them and rank the findings",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "review our north-star and activation metrics for the month and flag what moved",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "put together our monthly product scorecard and call out the biggest trend changes",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "give me a competitive brief on Linear vs Jira for our sales battlecard",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "update our Now/Next/Later roadmap, move the billing rework to Next and add the SSO epic to Now",
80
+ "should_trigger": false
81
+ },
82
+ {
83
+ "query": "is it worth building SSO before our enterprise push? do we even have enough evidence to call it?",
84
+ "should_trigger": false
85
+ }
86
+ ]
product-eval/evals/trigger/drift-check.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"query": "what changed since last month", "should_trigger": true},
3
+ {"query": "has this investigation drifted since we last looked at it", "should_trigger": true},
4
+ {"query": "compare the current state of the onboarding scope to the snapshot from last quarter and tell me what moved", "should_trigger": true},
5
+ {"query": "is the confidence trajectory on this bet actually improving, or are we just churning without new evidence coming in", "should_trigger": true},
6
+ {"query": "audit this scope before we re-decide — what's changed since the last decision was logged", "should_trigger": true},
7
+ {"query": "did our problem statement drift away from the evidence since the first version of the brief", "should_trigger": true},
8
+ {"query": "diff the billing investigation now versus the prior version — scores, evidence, and framing", "should_trigger": true},
9
+ {"query": "we snapshotted this scope a month ago — what evidence got added or retired, and how did Value and Confidence move", "should_trigger": true},
10
+ {"query": "are we accumulating real confidence on the activation problem over time, or has it just been flat churn", "should_trigger": true},
11
+ {"query": "show me what moved in this investigation since the last git ref", "should_trigger": true},
12
+ {"query": "has the narrative on the enterprise-churn bet been quietly reframed compared to the earlier brief", "should_trigger": true},
13
+ {"query": "set up a weekly digest of what moved across my investigations", "should_trigger": true},
14
+ {"query": "between the snapshot and now, which themes are new, which faded, and which got merged for this scope", "should_trigger": true},
15
+ {"query": "some of our evidence may have gone stale after the last release — compare against the prior snapshot and flag what got retired", "should_trigger": true},
16
+
17
+ {"query": "what's trending across the evidence we just gathered — which themes are getting worse month over month", "should_trigger": false},
18
+ {"query": "cluster the evidence in this scope into problem themes with confidence reasoning", "should_trigger": false},
19
+ {"query": "review our product metrics dashboard for the month and flag what moved on activation and retention", "should_trigger": false},
20
+ {"query": "our north-star metric dropped 8% this week, dig into what changed in the funnel", "should_trigger": false},
21
+ {"query": "rank these opportunities by value and confidence and tell me what to build next", "should_trigger": false},
22
+ {"query": "pull the latest zendesk tickets and g2 reviews into the scope", "should_trigger": false},
23
+ {"query": "do we have enough evidence to decide on the SSO bet yet", "should_trigger": false},
24
+ {"query": "let's brainstorm some new directions for our analytics product", "should_trigger": false},
25
+ {"query": "compare how our competitors solved onboarding versus how we did", "should_trigger": false}
26
+ ]
product-eval/evals/trigger/gather-evidence.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "dig into the onboarding drop-off for me - go through intercom, amplitude, and our linear issues and tell me which findings actually hold up",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "collect data on the billing complaints across our support tickets and app store reviews, last 90 days",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "find signal for the slow-export problem - pull from wherever we have data and rate how strong each piece is",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "i exported ~150 intercom convos to a csv plus a g2 dump, parse them and turn them into evidence items with strengths",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "ok discover-sources is done, now actually go pull the tickets and analytics for the checkout friction area",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "scrape the recent reddit + community threads about our competitor and our own churn survey, normalize it and dedupe the same users across sources",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "we keep hearing notifications are noisy - go get me the actual receipts from pendo, the nps verbatims, and support, and tell me how much to trust each",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "pull every lost-deal reason from the crm and the support tickets tagged 'sso' and weight them into evidence cards",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "harvest signal on mobile crashes: error groups from datadog, app reviews, and the 40 support tickets, then strength-rate and resolve duplicate reporters",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "synthesize the evidence we just pulled into themes",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "what data do I even have to make product decisions? scan my connectors and tell me the confidence ceiling",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "cluster this into the main problems - what patterns pop out of the signal we already collected?",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "give me a competitive brief on Linear vs Jira for our sales battlecard",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "do a teardown of Notion vs us - where are we losing on features and what do I put on the sales one-pager?",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "review our north-star and activation metrics for the month and flag what moved",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "which of our connected sources actually have usable product data, and how high can confidence get given what's available?",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "is it worth building SSO before our enterprise push? do we even have the data to call it?",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "new churn data came in - reshuffle our Now/Next/Later and tell me what slips to make room for the retention work",
80
+ "should_trigger": false
81
+ }
82
+ ]
product-eval/evals/trigger/pressure-test.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"query": "poke holes in our Q3 roadmap before the leadership review on friday", "should_trigger": true},
3
+ {"query": "stress-test this bet before we commit the whole quarter to it", "should_trigger": true},
4
+ {"query": "pressure-test the PRD for the new billing system, where's it weak", "should_trigger": true},
5
+ {"query": "red-team our go-to-market plan for the enterprise launch", "should_trigger": true},
6
+ {"query": "challenge the assumptions baked into this roadmap, i think we're kidding ourselves", "should_trigger": true},
7
+ {"query": "before we greenlight building SSO, tear the plan apart and find the holes", "should_trigger": true},
8
+ {"query": "we're about to commit to the mobile rewrite, what are the hidden assumptions and scope risks", "should_trigger": true},
9
+ {"query": "give this plan a hard critic review, find the weak grounding before we ship it", "should_trigger": true},
10
+ {"query": "what could go wrong with this roadmap? surface the soft spots before we present it", "should_trigger": true},
11
+ {"query": "play devil's advocate on our Q4 plan, what are we not seeing", "should_trigger": true},
12
+
13
+ {"query": "let's brainstorm some new directions for our analytics product, i want a sharp thinking partner", "should_trigger": false},
14
+ {"query": "help me come up with fresh feature ideas for our onboarding flow, just spitball with me", "should_trigger": false},
15
+ {"query": "i want to explore the problem space around developer retention and generate some options", "should_trigger": false},
16
+ {"query": "be my thinking partner, let's ideate on what we could build next in the observability space", "should_trigger": false},
17
+ {"query": "grade how crisp this problem statement is: 'onboarding is bad'", "should_trigger": false},
18
+ {"query": "is it worth building SSO before our enterprise push? do we even have the data to call it?", "should_trigger": false},
19
+ {"query": "update our Now/Next/Later roadmap, move the billing rework to Next and add the SSO epic to Now", "should_trigger": false},
20
+ {"query": "write a PRD for the new CSV export feature so engineering can pick it up", "should_trigger": false},
21
+ {"query": "review our north-star and activation metrics for the month and flag what moved", "should_trigger": false},
22
+ {"query": "write the weekly stakeholder update for leadership about the platform migration", "should_trigger": false}
23
+ ]
product-eval/evals/trigger/prioritize-backlog.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"query": "rank these 8 feature ideas by what's most worth doing, we have analytics and support data", "should_trigger": true},
3
+ {"query": "here's a list of 12 opportunities from our backlog, score them by value and confidence and tell me what to build", "should_trigger": true},
4
+ {"query": "prioritize my backlog for next quarter based on the evidence we've gathered", "should_trigger": true},
5
+ {"query": "i've got 15 problems competing for the same eng team, which ones are actually worth doing first", "should_trigger": true},
6
+ {"query": "score these opportunities and map each to a recommendation: build now, gather more, or drop", "should_trigger": true},
7
+ {"query": "we have evidence for some of these and basically nothing for others, rank them anyway and flag the low-confidence ones", "should_trigger": true},
8
+ {"query": "sequence these feature ideas by what's most worth doing given the data we have", "should_trigger": true},
9
+ {"query": "out of these 9 candidate bets, which should we tackle first, use value and confidence", "should_trigger": true},
10
+ {"query": "prioritize these: SSO, audit logs, SAML, bulk export, SCIM, rank by evidence-backed value", "should_trigger": true},
11
+ {"query": "what's the highest-leverage thing to build from this list of opportunities", "should_trigger": true},
12
+
13
+ {"query": "update our Now/Next/Later roadmap, move the billing rework to Next and add the SSO epic to Now", "should_trigger": false},
14
+ {"query": "reprioritize our roadmap, bump the mobile redesign up and push the API v2 work to later", "should_trigger": false},
15
+ {"query": "build me a Now / Next / Later roadmap view from scratch for the platform team", "should_trigger": false},
16
+ {"query": "the analytics dependency slipped two weeks, shift the roadmap timelines accordingly", "should_trigger": false},
17
+ {"query": "plan our next sprint, size the backlog against the team's capacity, we've got two people on PTO", "should_trigger": false},
18
+ {"query": "is it worth building SSO before our enterprise push? do we even have the data to call it?", "should_trigger": false},
19
+ {"query": "ok i've got ~200 intercom tickets exported and our amplitude funnel open, what should we actually build next quarter for onboarding", "should_trigger": false},
20
+ {"query": "review our north-star and activation metrics for the month and flag what moved", "should_trigger": false},
21
+ {"query": "write a PRD for the new CSV export feature so engineering can pick it up", "should_trigger": false},
22
+ {"query": "give me a competitive brief on Linear vs Jira for our sales battlecard", "should_trigger": false}
23
+ ]
product-eval/evals/trigger/scorecard.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "make a scorecard from the ranked opportunities in this product-eval scope",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "turn the prioritization results into a visual dashboard i can share with leadership",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "show me the ranked opportunities as an interactive html scorecard with value, confidence, verdicts and gaps",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "after that backlog ranking, create the scorecard view with the value-confidence quadrant",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "render a product decision dashboard from .product-eval/onboarding/scores.md and decisions-log.md",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "give me a rich visual output for the readiness verdicts and opportunity ranking",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "build a shareable html page of the opportunities, confidence bands, evidence weight, and open gaps",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "make this prioritization easier to scan with cards, bands, and the top customer quotes",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "open a scorecard for the current investigation so PMs can sort and act on the top bets",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "can you generate the decision dashboard after running prioritize-backlog",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "review our north-star and activation metrics for the month and flag what moved",
44
+ "should_trigger": false
45
+ },
46
+ {
47
+ "query": "build me a scorecard of this quarter's product metrics with trends called out for the exec review",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "rank these 8 feature ideas by what's most worth doing, we have analytics and support data",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "is it worth building SSO before our enterprise push? do we have enough evidence to call it?",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "write the decision memo for the billing rework with options and success criteria",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "turn the approved decision into a spec i can hand straight to claude code",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "poke holes in our Q3 roadmap before the leadership review on friday",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "what data do I have connected for product decisions?",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "pull the recurring complaints from zendesk and g2 and grade how solid each signal is",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "draft the opportunity brief for the notifications problem",
80
+ "should_trigger": false
81
+ }
82
+ ]
product-eval/evals/trigger/start.json ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "start",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "set up product-eval for my product",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "help me decide what to build next, but first ask me what context and data you need",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "help me prepare a decision report and set up the product context first",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "hey just installed this, help me get going — where do i even start?",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "what can this plugin actually do for me?",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "i want to build something in the observability tooling space but don't know what — help me find the real problems worth solving",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "should we build an AI changelog generator? honestly not sure, just point me in the right direction",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "i'm a PM at a fintech trying to find our next big bet — how does this thing even work?",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "help me figure out what to work on next, i don't even know which step to begin with",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "explore the API-monitoring market for me and show me where the gaps are",
44
+ "should_trigger": true
45
+ },
46
+ {
47
+ "query": "we want to figure out what to build next for our payments product — take it from the top",
48
+ "should_trigger": true
49
+ },
50
+ {
51
+ "query": "give me the lay of the land here and kick off an investigation into the CI/CD space",
52
+ "should_trigger": true
53
+ },
54
+ {
55
+ "query": "synthesize the evidence we just pulled into the main problem themes",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "cluster the signal we already gathered into a handful of themes",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "rank these 8 feature ideas by what's most worth building — we already have the analytics and support data",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "poke holes in our Q3 roadmap before the leadership review on friday",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "red-team this PRD and tell me where it falls apart before we commit",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "what product data do i even have to work with? scan my connectors and tell me the confidence ceiling",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "draft the opportunity brief for the notifications problem — both the narrative and the FAQ version",
80
+ "should_trigger": false
81
+ },
82
+ {
83
+ "query": "let's brainstorm some fresh directions for our analytics product, i want a sharp thinking partner",
84
+ "should_trigger": false
85
+ },
86
+ {
87
+ "query": "be my sounding board for a bit — i want to riff on new ideas for our developer tools",
88
+ "should_trigger": false
89
+ },
90
+ {
91
+ "query": "i've got 30 user-interview transcripts in a folder, pull out the main themes and insights for me",
92
+ "should_trigger": false
93
+ },
94
+ {
95
+ "query": "update our Now/Next/Later roadmap — move the billing rework to Next and add the SSO epic to Now",
96
+ "should_trigger": false
97
+ },
98
+ {
99
+ "query": "reprioritize the roadmap, that dependency slipped so push the search revamp to Later",
100
+ "should_trigger": false
101
+ }
102
+ ]
product-eval/evals/trigger/synthesize.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"query": "synthesize the evidence we just pulled into themes", "should_trigger": true},
3
+ {"query": "ok we ran gather-evidence yesterday and pulled a bunch of tickets, reviews and analytics into the scope, now cluster all that into the main problem themes", "should_trigger": true},
4
+ {"query": "what are the recurring patterns across the evidence we collected for the onboarding scope, and how confident should we be in each theme", "should_trigger": true},
5
+ {"query": "group the weighted evidence items in .product-eval/onboarding/evidence into problem clusters for me", "should_trigger": true},
6
+ {"query": "we've got the signal gathered for this investigation, what's trending over time and what themes pop out", "should_trigger": true},
7
+ {"query": "turn the evidence we gathered into themes i can feed into prioritization", "should_trigger": true},
8
+ {"query": "now that the data's in for this scope, find the clusters and tell me where the gaps are", "should_trigger": true},
9
+ {"query": "what problem-themes emerge from all the evidence in this investigation, and which ones are getting worse month over month", "should_trigger": true},
10
+ {"query": "consolidate everything we collected on the billing pain into a few clear themes with confidence reasoning", "should_trigger": true},
11
+ {"query": "we pulled g2 reviews, intercom and amplitude into the scope last week, summarize the patterns into themes", "should_trigger": true},
12
+ {"query": "can you frame this up properly? right now all we've got is 'onboarding feels clunky' and i need it sharp enough that someone could actually build against it", "should_trigger": true},
13
+ {"query": "users keep dropping off somewhere in the signup flow. help me turn that into a real problem statement", "should_trigger": true},
14
+ {"query": "the eng lead handed me 'add SSO' as the ask, but that feels like a solution dressed up as a requirement. what's the real underlying problem here?", "should_trigger": true},
15
+ {"query": "everyone keeps saying 'make the dashboard better' and it's going nowhere because it's so broad. reshape it into an actual problem we could solve", "should_trigger": true},
16
+ {"query": "'enterprise customers churn' is one giant blob. split it into a few separate problems that are each small enough to build for", "should_trigger": true},
17
+
18
+ {"query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me", "should_trigger": false},
19
+ {"query": "just got back from 12 customer interviews, here are my raw notes, help me make sense of them and pull out themes", "should_trigger": false},
20
+ {"query": "we ran a survey and got 400 open-text responses in a csv, synthesize them into key insights ranked by frequency", "should_trigger": false},
21
+ {"query": "here's a dump of 200 support tickets exported from zendesk, what are the common themes and what should we prioritize", "should_trigger": false},
22
+ {"query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is", "should_trigger": false},
23
+ {"query": "rank these 8 feature ideas by what's most worth doing, we have analytics and support data", "should_trigger": false},
24
+ {"query": "update our Now/Next/Later roadmap, move the billing rework to Next and add the SSO epic to Now", "should_trigger": false},
25
+ {"query": "review our north-star and activation metrics for the month and flag what moved", "should_trigger": false},
26
+ {"query": "let's brainstorm some new directions for our analytics product, i want a sharp thinking partner", "should_trigger": false},
27
+ {"query": "draft the opportunity brief for the notifications problem, both the narrative and the FAQ version", "should_trigger": false}
28
+ ]
product-eval/evals/trigger/to-build-brief.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "query": "the SSO bet got approved, can you turn it into a spec i can hand straight to claude code?",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "the notifications brief got approved yesterday — make it buildable, give me an agent-ready build spec",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "convert our approved opportunity brief into a build spec with given/when/then acceptance criteria",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "we signed off on the csv export bet — strip the prose and emit a structured spec claude code can just execute",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "run the design mode on the approved onboarding decision so i can hand the screen-by-screen breakdown to figma",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "we greenlit the billing rework, now produce the implementation spec for an AI builder with scope in/out and an MVP slice first",
24
+ "should_trigger": true
25
+ },
26
+ {
27
+ "query": "take this approved brief and make a figma-ready spec: every screen, its empty/loading/error states, and the primary CTA per view",
28
+ "should_trigger": true
29
+ },
30
+ {
31
+ "query": "decision's been signed off, just give me a literal testable build doc to drop into codex, no narrative",
32
+ "should_trigger": true
33
+ },
34
+ {
35
+ "query": "hand this approved billing brief to claude code with the data model, edge cases, success metrics and instrumentation",
36
+ "should_trigger": true
37
+ },
38
+ {
39
+ "query": "it passed readiness last week, can you turn it into something an agent can actually build from?",
40
+ "should_trigger": true
41
+ },
42
+ {
43
+ "query": "the export feature got the go-ahead — break it into screens and states for our designer to mock in figma",
44
+ "should_trigger": true
45
+ },
46
+ {
47
+ "query": "write a PRD for the new CSV export feature so engineering can pick it up",
48
+ "should_trigger": false
49
+ },
50
+ {
51
+ "query": "draft a feature spec with goals, non-goals and acceptance criteria so the devs can start the export work",
52
+ "should_trigger": false
53
+ },
54
+ {
55
+ "query": "draft the opportunity brief for the notifications problem, both the narrative and the FAQ version",
56
+ "should_trigger": false
57
+ },
58
+ {
59
+ "query": "write up the human-readable brief for the SSO opportunity — problem, why now, impact, the ask",
60
+ "should_trigger": false
61
+ },
62
+ {
63
+ "query": "write a decision memo for the SSO bet — options, falsifiable criteria, metrics, the ask and a deadline",
64
+ "should_trigger": false
65
+ },
66
+ {
67
+ "query": "put together the memo so leadership can decide on billing rework: the options, what would prove us wrong, and the deadline",
68
+ "should_trigger": false
69
+ },
70
+ {
71
+ "query": "is it worth building SSO before our enterprise push? do we even have the evidence to call it yet?",
72
+ "should_trigger": false
73
+ },
74
+ {
75
+ "query": "poke holes in our Q3 roadmap and the export PRD before the leadership review on friday",
76
+ "should_trigger": false
77
+ },
78
+ {
79
+ "query": "update our Now/Next/Later roadmap, move the billing rework to Next and add the SSO epic to Now",
80
+ "should_trigger": false
81
+ },
82
+ {
83
+ "query": "implement this approved figma design as react code for the onboarding flow",
84
+ "should_trigger": false
85
+ },
86
+ {
87
+ "query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me",
88
+ "should_trigger": false
89
+ }
90
+ ]
product-eval/evals/trigger/write-brief.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"query": "draft the opportunity brief for the notifications problem, both the narrative and the FAQ version", "should_trigger": true},
3
+ {"query": "write the opportunity brief for the onboarding pain now that it passed the readiness gate", "should_trigger": true},
4
+ {"query": "draft the narrative version of the brief: problem, why now, impact, the ask", "should_trigger": true},
5
+ {"query": "write the FAQ version of the brief for the billing opportunity", "should_trigger": true},
6
+ {"query": "put together the human decision doc for leadership on the SSO opportunity", "should_trigger": true},
7
+ {"query": "we decided to move forward, write up the brief that explains the problem and what we're asking for", "should_trigger": true},
8
+ {"query": "turn this investigation into an opportunity brief i can circulate to the exec team", "should_trigger": true},
9
+ {"query": "write the brief: problem statement, why-now, expected impact, and the ask", "should_trigger": true},
10
+ {"query": "draft both versions of the opportunity brief for the churn problem, report and FAQ", "should_trigger": true},
11
+ {"query": "i need the opportunity brief for the analytics problem written up as a narrative for the team", "should_trigger": true},
12
+ {"query": "write the decision memo for the SSO bet, options, success/failure criteria, metrics scorecard, and an explicit ask for friday", "should_trigger": true},
13
+ {"query": "draft a recommendation memo on whether to build the in-app notifications center, weigh at least 3 options including do-nothing and recommend one", "should_trigger": true},
14
+ {"query": "i need an options memo for the onboarding rework: choices, tradeoffs, and what i'm asking the exec team to decide by july 15", "should_trigger": true},
15
+ {"query": "write the doc that forces a build-vs-buy call on the analytics pipeline, with options, what we'd measure, and a recommendation", "should_trigger": true},
16
+
17
+ {"query": "write a PRD for the new CSV export feature so engineering can pick it up", "should_trigger": false},
18
+ {"query": "turn this feature idea into a spec with goals, non-goals, and acceptance criteria for the eng team", "should_trigger": false},
19
+ {"query": "scope the bulk-export feature into a PRD with success metrics and phased milestones", "should_trigger": false},
20
+ {"query": "write the engineering spec for the new webhooks system so the devs can start", "should_trigger": false},
21
+ {"query": "turn the approved decision into a spec i can hand straight to claude code", "should_trigger": false},
22
+ {"query": "write the weekly stakeholder update for leadership about the platform migration", "should_trigger": false},
23
+ {"query": "give me a competitive brief on Linear vs Jira for our sales battlecard", "should_trigger": false},
24
+ {"query": "ok i've got ~200 intercom tickets exported and our amplitude funnel open, what should we actually build next quarter for onboarding", "should_trigger": false},
25
+ {"query": "review our north-star and activation metrics for the month and flag what moved", "should_trigger": false}
26
+ ]
product-eval/evals/triggering.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "purpose": "Routing / collision evals. Each query maps to the skill that SHOULD win. 'anthropic-pm:*' and 'none' mark cases that must NOT trigger one of our skills (near-misses against Anthropic's product-management plugin). To run the per-skill description optimizer (skill-creator run_loop.py), transform per skill: queries whose expected == <skill> are should_trigger:true, all others false.",
3
+ "skills": ["start","discover-sources","gather-evidence","synthesize","prioritize-backlog","decision-readiness","design-test","pressure-test","critique","write-brief","to-build-brief","scorecard","drift-check","calibrate"],
4
+ "cases": [
5
+ {"id": 1, "query": "ok i've got ~200 intercom tickets exported and our amplitude funnel open — what should we actually build next quarter for onboarding?", "expected": "start", "note": "setup, then evaluate job and chains"},
6
+ {"id": 2, "query": "is it worth building SSO before our enterprise push? do we even have the data to call it?", "expected": "decision-readiness"},
7
+ {"id": 3, "query": "rank these 8 feature ideas by what's most worth doing — we have analytics + support data", "expected": "prioritize-backlog"},
8
+ {"id": 4, "query": "synthesize the evidence we just pulled into themes", "expected": "synthesize"},
9
+ {"id": 5, "query": "i have a pile of 30 user-interview transcripts in a folder, pull out the main themes and insights for me", "expected": "anthropic-pm:synthesize-research", "note": "raw research outside our pipeline — must NOT trigger synthesize"},
10
+ {"id": 6, "query": "poke holes in our Q3 roadmap before the leadership review on friday", "expected": "pressure-test"},
11
+ {"id": 7, "query": "let's brainstorm some new directions for our analytics product, i want a sharp thinking partner", "expected": "anthropic-pm:product-brainstorming", "note": "generative — must NOT trigger pressure-test/critique"},
12
+ {"id": 8, "query": "update our Now/Next/Later roadmap — move the billing rework to Next and add the SSO epic to Now", "expected": "anthropic-pm:roadmap-update", "note": "roadmap edit — must NOT trigger prioritize-backlog"},
13
+ {"id": 9, "query": "write a PRD for the new CSV export feature so engineering can pick it up", "expected": "anthropic-pm:write-spec", "note": "engineering spec — must NOT trigger write-brief"},
14
+ {"id": 10, "query": "draft the opportunity brief for the notifications problem — both the narrative and the FAQ version", "expected": "write-brief"},
15
+ {"id": 11, "query": "grade how crisp this problem statement is: 'onboarding is bad'", "expected": "critique", "note": "crispness grader"},
16
+ {"id": 12, "query": "what data do I even have to make product decisions? scan my connectors and tell me the confidence ceiling", "expected": "discover-sources"},
17
+ {"id": 13, "query": "i want to build something in the observability tooling space but not sure what — find the real problems worth solving", "expected": "start", "note": "explore job"},
18
+ {"id": 14, "query": "turn the approved decision into a spec i can hand straight to claude code", "expected": "to-build-brief"},
19
+ {"id": 15, "query": "has our top opportunity drifted since last month? did confidence actually improve or are we spinning?", "expected": "drift-check"},
20
+ {"id": 16, "query": "the confidence numbers feel too generous — tune the thresholds against these 8 past launches we know the outcomes of", "expected": "calibrate"},
21
+ {"id": 17, "query": "give me a competitive brief on Linear vs Jira for our sales battlecard", "expected": "anthropic-pm:competitive-brief", "note": "must NOT trigger gather-evidence/pressure-test"},
22
+ {"id": 18, "query": "review our north-star and activation metrics for the month and flag what moved", "expected": "anthropic-pm:metrics-review", "note": "must NOT trigger drift-check"},
23
+ {"id": 19, "query": "write the weekly stakeholder update for leadership about the platform migration", "expected": "anthropic-pm:stakeholder-update", "note": "none of ours"},
24
+ {"id": 20, "query": "pull the recurring complaints from zendesk, our g2 reviews, and the churn survey and grade how solid each signal is", "expected": "gather-evidence"},
25
+ {"id": 21, "query": "decision-readiness says run a research sprint for SSO — design the cheapest test to close the gap", "expected": "design-test"},
26
+ {"id": 22, "query": "make a scorecard from the ranked opportunities in this product-eval scope", "expected": "scorecard"},
27
+ {"id": 23, "query": "write the decision memo for the SSO bet — options, metrics, falsifiable criteria, and the explicit ask", "expected": "write-brief"},
28
+ {"id": 24, "query": "what should we build next for activation if we have a csv of support tickets and some funnel data?", "expected": "start", "note": "Rank opportunities outcome with setup"},
29
+ {"id": 25, "query": "users keep dropping off somewhere in signup; turn that into a real problem statement", "expected": "synthesize", "note": "problem framing is now inside synthesize"},
30
+ {"id": 26, "query": "set up product-eval for my product", "expected": "start", "note": "first-run onboarding"},
31
+ {"id": 27, "query": "help me prepare a decision report and ask me what context you need first", "expected": "start", "note": "write outcome but missing source/audience setup"}
32
+ ]
33
+ }
product-eval/examples/README.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Examples
2
+
3
+ ## `activation-q3/`: a complete sample investigation
4
+
5
+ A synthetic, self-contained investigation scope for the fictional product "Pulse" (a B2B product-analytics workspace). It mirrors exactly what the plugin writes to `.product-eval/<scope>/` in a real project, and it serves two purposes:
6
+
7
+ 1. **A `scorecard` reference output.** `activation-q3/scorecard.html` is the rendered decision scorecard for this scope: open it in a browser to see the KPI cards, the Value by Confidence quadrant, the four color-coded verdict cards (with verbatim quotes, top complaint, the ask, and "why it ranks here"), the glossary, and the honest footer.
8
+ 2. **A conformance fixture.** Every file conforms to `DATA-CONTRACT.md`, and every number was computed from the shipped scoring defaults, so it is the fixture the validation preflight (and `scripts/validate_scope.py`) is tested against.
9
+
10
+ ### What's inside
11
+
12
+ | File | Role |
13
+ |---|---|
14
+ | `context.md` | scope front-matter (product, north star, personas) |
15
+ | `evidence/E1.md` ... `E12.md` | 12 normalized, weighted evidence items (E12 is a flagged contradiction) |
16
+ | `identities.md` | resolved cross-system identities (same account across Zendesk + CRM + analytics; same email across Zendesk + CRM) |
17
+ | `themes.md` | 4 problem-framed themes with computed confidence |
18
+ | `scores.md` | Value/Confidence and a recommendation per theme |
19
+ | `decisions-log.md` | readiness verdicts (append-only) |
20
+ | `scorecard.html` | the rendered scorecard |
21
+
22
+ ### The four opportunities (one of each verdict)
23
+
24
+ | Theme | Value | Confidence | Verdict |
25
+ |---|---|---|---|
26
+ | T1 New admins can't finish setup without CS help | 72 (High) | 89 (High) | **Decide now** |
27
+ | T2 Teams can't share saved views across the org | 70 (High) | 63 (Moderate) | **Run a research sprint** |
28
+ | T3 Alert noise pushes users to mute critical alerts | 60 (Moderate) | 24 (Low) | **Do not commit yet** |
29
+ | T4 Users want a dark-mode dashboard | 35 (Low) | 12 (Low) | **Deprioritize** |
30
+
31
+ T3 shows how a directly contradicting first-party signal (E12) drives confidence below the gate even when value is real.
32
+
33
+ ### Verify it
34
+
35
+ ```
36
+ python ../scripts/validate_scope.py activation-q3/ # -> OK (needs PyYAML)
37
+ ```
38
+
39
+ All numbers are illustrative. No real customer data.
product-eval/examples/activation-q3/context.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ scope: activation-q3
3
+ job: evaluate
4
+ stage: evaluate
5
+ product: "Pulse, a B2B product-analytics workspace for SaaS teams"
6
+ north_star: "weekly activated workspaces"
7
+ personas: [admin, end_user, buyer]
8
+ updated: 2026-06-29
9
+ ---
10
+
11
+ Synthetic example investigation shipped with the product-eval plugin. All numbers
12
+ are illustrative and were computed from the shipped scoring defaults so the files
13
+ can double as a validation fixture. No real customer data.
product-eval/examples/activation-q3/decisions-log.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Decisions log for activation-q3 (append-only)
2
+
3
+ - date: 2026-06-29
4
+ item: T1
5
+ event: readiness
6
+ verdict: "Decide now"
7
+ confidence: 89
8
+ confidence_band: high
9
+ evidence: [E1, E2, E3, E4]
10
+ open: ["which connector is worst", "trial-spam check on the 38%"]
11
+ contested: []
12
+ note: "Passed the sufficiency gate; first-party analytics + tickets + interviews + churn agree."
13
+
14
+ - date: 2026-06-29
15
+ item: T2
16
+ event: readiness
17
+ verdict: "Run a research sprint"
18
+ confidence: 63
19
+ confidence_band: moderate
20
+ evidence: [E5, E6, E7]
21
+ open: ["how many seats would share", "no first-party re-creation data"]
22
+ contested: []
23
+ note: "High value, but the cheapest validating test should run before committing (see design-test)."
24
+
25
+ - date: 2026-06-29
26
+ item: T3
27
+ event: readiness
28
+ verdict: "Do not commit yet"
29
+ confidence: 24
30
+ confidence_band: low
31
+ evidence: [E8, E9]
32
+ open: ["measure actual mute-rate"]
33
+ contested: [E12]
34
+ note: "Hypothesis contradicted by first-party analytics; do not act until measured."
product-eval/examples/activation-q3/evidence/E1.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E1
3
+ claim: "38% of new workspaces never finish setup, dropping at the data-source connection step (up from 24% last quarter)"
4
+ source_type: product_analytics
5
+ source_ref: "Amplitude > Activation funnel"
6
+ persona: [admin]
7
+ funnel_stage: activation
8
+ impact_type: activation
9
+ strength: 5
10
+ recency_days: 20
11
+ weight: 12
12
+ person_id: null
13
+ account_id: acct_acme
14
+ corroborated_by: [E2, E3]
15
+ stale: false
16
+ ---
17
+ Funnel: signup to first-connected-source. The cliff is the integrations step, not signup.
product-eval/examples/activation-q3/evidence/E10.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E10
3
+ claim: "6 scattered requests for a dark-mode dashboard over the past year"
4
+ source_type: feature_request
5
+ source_ref: "Productboard > misc (6 requests)"
6
+ persona: [end_user]
7
+ funnel_stage: engagement
8
+ impact_type: engagement
9
+ strength: 2
10
+ recency_days: 300
11
+ weight: 1
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: []
15
+ stale: false
16
+ ---
17
+ Low frequency, spread thin over 12 months.
product-eval/examples/activation-q3/evidence/E11.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E11
3
+ claim: "Occasional app-store style reviews ask for dark mode"
4
+ source_type: app_review
5
+ source_ref: "Capterra (sparse)"
6
+ persona: [end_user]
7
+ funnel_stage: engagement
8
+ impact_type: engagement
9
+ strength: 2
10
+ recency_days: 200
11
+ weight: 1
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: []
15
+ stale: false
16
+ ---
17
+ Aesthetic preference, no business-pain signal.
product-eval/examples/activation-q3/evidence/E12.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E12
3
+ claim: "Alert-channel engagement is flat quarter over quarter, not declining"
4
+ source_type: product_analytics
5
+ source_ref: "Amplitude > Alerts engagement"
6
+ persona: [end_user]
7
+ funnel_stage: engagement
8
+ impact_type: engagement
9
+ strength: 4
10
+ recency_days: 25
11
+ weight: 7
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: []
15
+ stale: false
16
+ contradicts: [E8]
17
+ ---
18
+ Direct first-party data that contradicts the "users are muting alerts" hypothesis (E8).
19
+ Kept and flagged: this is the unresolved material contradiction that drives T3's confidence penalty.
product-eval/examples/activation-q3/evidence/E2.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E2
3
+ claim: "Admins get stuck at the data-source connection step and open tickets to finish setup"
4
+ source_type: support_ticket
5
+ source_ref: "Zendesk > tag:onboarding (27 tickets / 6 weeks)"
6
+ persona: [admin]
7
+ funnel_stage: activation
8
+ impact_type: activation
9
+ strength: 4
10
+ recency_days: 30
11
+ weight: 7
12
+ person_id: null
13
+ account_id: acct_acme
14
+ corroborated_by: [E1, E3]
15
+ stale: false
16
+ ---
17
+ "I connected Postgres but nothing shows up, what am I missing?" Recurring across 27 tickets.
product-eval/examples/activation-q3/evidence/E3.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E3
3
+ claim: "In 4 onboarding calls, admins needed CS hand-holding to complete setup"
4
+ source_type: customer_interview
5
+ source_ref: "Onboarding calls (4 accounts)"
6
+ persona: [admin]
7
+ funnel_stage: activation
8
+ impact_type: activation
9
+ strength: 4
10
+ recency_days: 15
11
+ weight: 7
12
+ person_id: person_jordan
13
+ account_id: acct_acme
14
+ corroborated_by: [E1, E2]
15
+ stale: false
16
+ ---
17
+ "We almost gave up before your CS person walked us through the connectors."
product-eval/examples/activation-q3/evidence/E4.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E4
3
+ claim: "3 churned accounts named 'never got it set up' as the reason for leaving"
4
+ source_type: churn_survey
5
+ source_ref: "Churn survey Q2 (3 of 11 responses)"
6
+ persona: [admin, buyer]
7
+ funnel_stage: retention
8
+ impact_type: retention
9
+ strength: 4
10
+ recency_days: 120
11
+ weight: 5
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: []
15
+ stale: false
16
+ ---
17
+ Setup friction reads through to retention, not just activation.
product-eval/examples/activation-q3/evidence/E5.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E5
3
+ claim: "19 feature requests asking for views/dashboards shared across the whole org"
4
+ source_type: feature_request
5
+ source_ref: "Productboard > top requests"
6
+ persona: [admin, end_user]
7
+ funnel_stage: expansion
8
+ impact_type: expansion
9
+ strength: 3
10
+ recency_days: 25
11
+ weight: 4
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: [E6, E7]
15
+ stale: false
16
+ ---
17
+ Today views are per-user; teams rebuild the same view repeatedly.
product-eval/examples/activation-q3/evidence/E6.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E6
3
+ claim: "Two mid-market prospects asked about org-shared views during deals"
4
+ source_type: sales_call
5
+ source_ref: "Gong > deal notes (2 deals)"
6
+ persona: [buyer]
7
+ funnel_stage: expansion
8
+ impact_type: competitive
9
+ strength: 4
10
+ recency_days: 18
11
+ weight: 7
12
+ person_id: null
13
+ account_id: acct_northwind
14
+ corroborated_by: [E5]
15
+ stale: false
16
+ ---
17
+ Raised as a 'will our team adopt this' question, an expansion signal.
product-eval/examples/activation-q3/evidence/E7.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E7
3
+ claim: "A G2 review wants team-shared saved views"
4
+ source_type: app_review
5
+ source_ref: "G2 review (4-star)"
6
+ persona: [end_user]
7
+ funnel_stage: expansion
8
+ impact_type: competitive
9
+ strength: 3
10
+ recency_days: 140
11
+ weight: 3
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: [E5]
15
+ stale: false
16
+ ---
17
+ "Love it solo, but my team can't see my views."
product-eval/examples/activation-q3/evidence/E8.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E8
3
+ claim: "CS suspects users mute alert channels because of noise; one large account muted everything"
4
+ source_type: internal_observation
5
+ source_ref: "CS Slack thread + 1 account"
6
+ persona: [end_user]
7
+ funnel_stage: retention
8
+ impact_type: retention
9
+ strength: 3
10
+ recency_days: 22
11
+ weight: 4
12
+ person_id: null
13
+ account_id: acct_acme
14
+ corroborated_by: []
15
+ stale: false
16
+ ---
17
+ Hypothesis, not measured. Flagged as contradicted by E-contra below.
product-eval/examples/activation-q3/evidence/E9.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ id: E9
3
+ claim: "A low-engagement community thread complains about alert spam"
4
+ source_type: community_post
5
+ source_ref: "Reddit r/devops (low engagement)"
6
+ persona: [end_user]
7
+ funnel_stage: engagement
8
+ impact_type: cost
9
+ strength: 2
10
+ recency_days: 150
11
+ weight: 1
12
+ person_id: null
13
+ account_id: null
14
+ corroborated_by: []
15
+ stale: false
16
+ ---
17
+ Thin signal; included for completeness.
product-eval/examples/activation-q3/identities.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Resolved identities for activation-q3
2
+ # Shows the cross-system mapping: the same account/person stitched across
3
+ # Zendesk + CRM + analytics so multi-touchpoint signal collapses to one entity.
4
+
5
+ - entity: acct_acme
6
+ grain: account
7
+ matched_records: [zendesk:org_1183, hubspot:acct_55, amplitude:grp_acme]
8
+ method: email_domain # acme.io
9
+ match_confidence: high
10
+ # Appears in E1 (analytics), E2 (ticket), E3 (interview), E8 (observation):
11
+ # one account, four touchpoints, escalating -> a severity signal, not four voices.
12
+
13
+ - entity: person_jordan
14
+ grain: person
15
+ matched_records: [zendesk:user_991, hubspot:contact_77]
16
+ method: email # jordan@acme.io present verbatim in both systems
17
+ match_confidence: high
18
+ # The canonical case: same email in a Zendesk ticket AND the CRM -> one person,
19
+ # enriched with CRM context (admin at Acme, mid-market, renewal in 90 days).
20
+
21
+ - entity: acct_northwind
22
+ grain: account
23
+ matched_records: [gong:acct_nw, hubspot:acct_204]
24
+ method: email_domain # northwind.com
25
+ match_confidence: high
product-eval/examples/activation-q3/scorecard.html ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Decision Scorecard, activation-q3 (Pulse)</title>
7
+ <style>
8
+ :root{
9
+ --bg:#f7f8fa; --card:#ffffff; --ink:#15181d; --muted:#5b6471; --line:#e5e8ee;
10
+ --green:#16a34a; --green-bg:#e7f6ec; --amber:#c2780b; --amber-bg:#fdf3df;
11
+ --grey:#5b6471; --grey-bg:#eef0f4; --red:#dc2626; --red-bg:#fbe9e9;
12
+ --bar:#dfe3ea; --accent:#3b5bdb;
13
+ }
14
+ *{box-sizing:border-box}
15
+ body{margin:0;background:var(--bg);color:var(--ink);
16
+ font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
17
+ .wrap{max-width:1040px;margin:0 auto;padding:28px 22px 60px}
18
+ header.top{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;gap:12px;margin-bottom:6px}
19
+ .brand{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);font-weight:700}
20
+ h1{font-size:24px;margin:4px 0 2px}
21
+ .sub{color:var(--muted);font-size:14px}
22
+ .summary{margin:14px 0 22px;padding:12px 16px;background:var(--card);border:1px solid var(--line);
23
+ border-radius:10px;font-size:14.5px}
24
+ .summary b{font-weight:650}
25
+ .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:22px}
26
+ .kpi{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:14px 16px}
27
+ .kpi .n{font-size:26px;font-weight:700;line-height:1.1}
28
+ .kpi .l{font-size:12.5px;color:var(--muted);margin-top:3px}
29
+ .kpi .dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;vertical-align:middle}
30
+ .panel{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-bottom:22px}
31
+ .panel h2{font-size:16px;margin:0 0 4px}
32
+ .panel .hint{color:var(--muted);font-size:13px;margin:0 0 12px}
33
+ .quad-flex{display:flex;flex-wrap:wrap;gap:18px;align-items:center}
34
+ .legend{font-size:13px;min-width:190px}
35
+ .legend div{margin:6px 0;display:flex;align-items:center;gap:8px}
36
+ .sw{width:12px;height:12px;border-radius:3px;display:inline-block}
37
+ .toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:14px}
38
+ .toolbar span{font-size:13px;color:var(--muted);margin-right:2px}
39
+ .toolbar button{font:inherit;font-size:13px;border:1px solid var(--line);background:var(--card);
40
+ color:var(--ink);border-radius:999px;padding:5px 12px;cursor:pointer}
41
+ .toolbar button.active{background:var(--ink);color:#fff;border-color:var(--ink)}
42
+ .card{background:var(--card);border:1px solid var(--line);border-left-width:5px;border-radius:10px;
43
+ padding:16px 18px;margin-bottom:14px}
44
+ .card .row1{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;align-items:baseline}
45
+ .card h3{font-size:17px;margin:0}
46
+ .verdict{font-size:12.5px;font-weight:700;padding:3px 10px;border-radius:999px;white-space:nowrap}
47
+ .bands{display:flex;flex-wrap:wrap;gap:18px;margin:12px 0 6px}
48
+ .band{min-width:150px;flex:1}
49
+ .band .lab{font-size:12px;color:var(--muted);display:flex;justify-content:space-between}
50
+ .band .barwrap{height:8px;background:var(--bar);border-radius:6px;margin-top:4px;overflow:hidden}
51
+ .band .fill{height:100%;border-radius:6px}
52
+ .band .bandname{font-weight:650;font-size:13.5px}
53
+ .meta{display:flex;flex-wrap:wrap;gap:16px;font-size:13px;color:var(--muted);margin:8px 0 10px}
54
+ .meta b{color:var(--ink);font-weight:650}
55
+ blockquote{margin:10px 0;padding:8px 14px;border-left:3px solid var(--line);
56
+ background:#fafbfc;color:#2c313a;font-style:italic;border-radius:0 6px 6px 0;font-size:13.5px}
57
+ .pa{font-size:13.5px;margin:6px 0}
58
+ .pa .k{font-weight:650}
59
+ .why{font-size:13.5px;margin:10px 0 4px;padding:8px 12px;background:#f3f6ff;border-radius:7px}
60
+ .gaps{font-size:13px;color:var(--muted);margin-top:8px}
61
+ .gaps .k{font-weight:650;color:var(--ink)}
62
+ details.glossary{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:6px 20px;margin-bottom:22px}
63
+ details.glossary summary{cursor:pointer;font-weight:650;padding:12px 0;font-size:15px}
64
+ details.glossary dl{margin:0 0 12px}
65
+ details.glossary dt{font-weight:650;margin-top:10px;font-size:13.5px}
66
+ details.glossary dd{margin:2px 0 0;color:var(--muted);font-size:13.5px}
67
+ footer.honest{background:#10141b;color:#e7eaf0;border-radius:12px;padding:18px 20px;font-size:14px}
68
+ footer.honest b{color:#fff}
69
+ footer.honest .move{margin-top:8px;color:#aeb7c6}
70
+ .tag{font-size:11.5px;color:var(--muted)}
71
+ @media(max-width:560px){.bands{gap:10px}}
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <div class="wrap">
76
+ <header class="top">
77
+ <div>
78
+ <div class="brand">product-eval &nbsp;/&nbsp; decision scorecard</div>
79
+ <h1>activation-q3</h1>
80
+ <div class="sub">Pulse, a B2B product-analytics workspace. North star: weekly activated workspaces. Generated 2026-06-29.</div>
81
+ </div>
82
+ </header>
83
+
84
+ <div class="summary" id="summary"></div>
85
+
86
+ <div class="kpis" id="kpis"></div>
87
+
88
+ <div class="panel">
89
+ <h2>Value &times; Confidence</h2>
90
+ <p class="hint">Each opportunity is plotted by how big it could be (Value, vertical) against how sure we are it is real (Confidence, horizontal). The gate lines sit at 70. The top-right box is where a bet is both worth it and proven enough to commit.</p>
91
+ <div class="quad-flex">
92
+ <svg viewBox="0 0 480 380" width="480" height="380" role="img" aria-label="Value by Confidence quadrant">
93
+ <!-- quadrant tints -->
94
+ <rect x="337" y="16" width="79" height="97" fill="var(--green-bg)"></rect>
95
+ <rect x="46" y="16" width="291" height="97" fill="#fcfcfd"></rect>
96
+ <!-- axes -->
97
+ <line x1="46" y1="16" x2="46" y2="340" stroke="var(--line)"></line>
98
+ <line x1="46" y1="340" x2="464" y2="340" stroke="var(--line)"></line>
99
+ <!-- gate lines at 70 -->
100
+ <line x1="337" y1="16" x2="337" y2="340" stroke="#c8ccd6" stroke-dasharray="4 4"></line>
101
+ <line x1="46" y1="113" x2="464" y2="113" stroke="#c8ccd6" stroke-dasharray="4 4"></line>
102
+ <text x="341" y="30" font-size="10" fill="#9aa1ad">conf gate 70</text>
103
+ <text x="50" y="110" font-size="10" fill="#9aa1ad">value 70</text>
104
+ <!-- axis labels -->
105
+ <text x="255" y="366" font-size="12" fill="#5b6471" text-anchor="middle">Confidence &rarr;</text>
106
+ <text x="14" y="180" font-size="12" fill="#5b6471" text-anchor="middle" transform="rotate(-90 14 180)">Value &rarr;</text>
107
+ <text x="46" y="356" font-size="10" fill="#9aa1ad" text-anchor="middle">0</text>
108
+ <text x="464" y="356" font-size="10" fill="#9aa1ad" text-anchor="middle">100</text>
109
+ <!-- points -->
110
+ <g>
111
+ <circle cx="416" cy="107" r="8" fill="var(--green)"></circle>
112
+ <text x="416" y="96" font-size="11" fill="#15181d" text-anchor="middle" font-weight="700">T1</text>
113
+ <circle cx="308" cy="113" r="8" fill="var(--amber)"></circle>
114
+ <text x="304" y="102" font-size="11" fill="#15181d" text-anchor="end" font-weight="700">T2</text>
115
+ <circle cx="146" cy="146" r="8" fill="var(--grey)"></circle>
116
+ <text x="146" y="135" font-size="11" fill="#15181d" text-anchor="middle" font-weight="700">T3</text>
117
+ <circle cx="96" cy="227" r="8" fill="var(--red)"></circle>
118
+ <text x="96" y="216" font-size="11" fill="#15181d" text-anchor="middle" font-weight="700">T4</text>
119
+ </g>
120
+ </svg>
121
+ <div class="legend">
122
+ <div><span class="sw" style="background:var(--green)"></span> Decide now</div>
123
+ <div><span class="sw" style="background:var(--amber)"></span> Run a research sprint</div>
124
+ <div><span class="sw" style="background:var(--grey)"></span> Do not commit yet</div>
125
+ <div><span class="sw" style="background:var(--red)"></span> Deprioritize</div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+
130
+ <div class="toolbar">
131
+ <span>Sort by</span>
132
+ <button data-sort="tier" class="active">Recommendation</button>
133
+ <button data-sort="value">Value</button>
134
+ <button data-sort="confidence">Confidence</button>
135
+ <button data-sort="weight">Evidence weight</button>
136
+ </div>
137
+
138
+ <div id="cards"></div>
139
+
140
+ <details class="glossary">
141
+ <summary>How to read this</summary>
142
+ <dl>
143
+ <dt>Value (0 to 100)</dt>
144
+ <dd>If this problem is real, how big is it? Built from persona weight, funnel location, frequency, and competitive pressure (each 0 to 25). It is about potential impact, not certainty.</dd>
145
+ <dt>Confidence (0 to 100)</dt>
146
+ <dd>How sure are we the problem is real and worth acting on? Computed from total evidence weight, source-type diversity, and unresolved contradictions. Reported as a band (Low / Moderate / High); the number is for audit.</dd>
147
+ <dt>Evidence weight</dt>
148
+ <dd>Quality-weighted volume of evidence, deduplicated by account. Three strong first-party findings outweigh fifty vague mentions, so weight, not raw count, drives Confidence.</dd>
149
+ <dt>Accounts affected</dt>
150
+ <dd>Distinct accounts after identity resolution (one customer across Zendesk, CRM, and analytics counts once), so a single loud account is not mistaken for a widespread problem.</dd>
151
+ <dt>The recommendation tiers</dt>
152
+ <dd><b>Decide now</b>: worth building and well supported. <b>Run a research sprint</b>: high value but needs more proof, design the cheapest validating test. <b>Do not commit yet</b>: too unproven to act on. <b>Deprioritize</b>: prize too small to pursue.</dd>
153
+ </dl>
154
+ </details>
155
+
156
+ <footer class="honest" id="footer"></footer>
157
+ </div>
158
+
159
+ <script>
160
+ const CEILING = "Decide-now reachable: first-party analytics, support tickets, and CRM are connected.";
161
+ const DATA = [
162
+ {
163
+ id:"T1", tier:1, verdict:"Decide now", color:"green",
164
+ name:"New admins can't finish workspace setup without CS help",
165
+ value:72, valueBand:"High", confidence:89, confBand:"High",
166
+ weight:31, accounts:9, sources:4,
167
+ quote:"We almost gave up before your CS person walked us through the connectors.",
168
+ quoteSrc:"onboarding call, admin at Acme",
169
+ complaint:"38% of new workspaces never finish setup, dropping at the data-source connection step (up from 24% last quarter).",
170
+ ask:"Guided or auto-detected connector setup so an admin can finish without opening a ticket.",
171
+ why:"Ranks top-right: an activation cliff on the north-star path, corroborated across analytics, tickets, interviews, and churn. The score earns a commit.",
172
+ gaps:["Which connector is worst (Postgres vs Snowflake)?","Is the 38% inflated by trial spam?"]
173
+ },
174
+ {
175
+ id:"T2", tier:2, verdict:"Run a research sprint", color:"amber",
176
+ name:"Teams can't share saved views across the org",
177
+ value:70, valueBand:"High", confidence:63, confBand:"Moderate",
178
+ weight:14, accounts:6, sources:3,
179
+ quote:"Love it solo, but my team can't see my views.",
180
+ quoteSrc:"G2 review, end user",
181
+ complaint:"Views are per-user today, so teams rebuild the same view over and over; raised in two live deals.",
182
+ ask:"Org-shared saved views and dashboards a team can all see.",
183
+ why:"High value and competitors already solved it, but there is no first-party usage data on how often views get re-created. Validate before committing.",
184
+ gaps:["How many seats would actually share a view?","No first-party data on view re-creation."]
185
+ },
186
+ {
187
+ id:"T3", tier:3, verdict:"Do not commit yet", color:"grey",
188
+ name:"Alert noise pushes users to mute critical alerts",
189
+ value:60, valueBand:"Moderate", confidence:24, confBand:"Low",
190
+ weight:5, accounts:2, sources:2,
191
+ quote:"People just mute the whole channel, then miss the one that mattered.",
192
+ quoteSrc:"CS internal observation",
193
+ complaint:"Suspected that alert noise leads users to mute channels and miss critical alerts.",
194
+ ask:"Smarter alert grouping or quieter defaults so the signal survives.",
195
+ why:"Retention-relevant if true, but the core hypothesis is directly contradicted by first-party analytics (alert engagement is flat, not falling). Measure before acting.",
196
+ gaps:["No measured mute-rate.","Hypothesis (E8) directly contradicted by analytics (E12)."]
197
+ },
198
+ {
199
+ id:"T4", tier:4, verdict:"Deprioritize", color:"red",
200
+ name:"Users want a dark-mode dashboard",
201
+ value:35, valueBand:"Low", confidence:12, confBand:"Low",
202
+ weight:2, accounts:4, sources:2,
203
+ quote:"Would love a dark mode for late-night dashboards.",
204
+ quoteSrc:"Capterra review, end user",
205
+ complaint:"Scattered requests for a dark-mode theme, six over the past year.",
206
+ ask:"A dark-mode theme for the dashboard.",
207
+ why:"A secondary-persona, engagement-only preference with no competitive pressure. The prize is too small to pursue now, regardless of confidence.",
208
+ gaps:["No business-impact signal.","Low frequency, spread over 12 months."]
209
+ }
210
+ ];
211
+
212
+ const C = {
213
+ green:{ink:"var(--green)",bg:"var(--green-bg)"},
214
+ amber:{ink:"var(--amber)",bg:"var(--amber-bg)"},
215
+ grey:{ink:"var(--grey)",bg:"var(--grey-bg)"},
216
+ red:{ink:"var(--red)",bg:"var(--red-bg)"}
217
+ };
218
+
219
+ function bandColor(band){return band==="High"?"var(--green)":band==="Moderate"?"var(--amber)":"var(--grey)";}
220
+
221
+ function renderSummary(){
222
+ const n=DATA.length;
223
+ const by=t=>DATA.filter(d=>d.verdict===t).length;
224
+ document.getElementById("summary").innerHTML =
225
+ `<b>${n} opportunities</b>: ${by("Decide now")} Decide now, `+
226
+ `${by("Run a research sprint")} Run a research sprint, `+
227
+ `${by("Do not commit yet")} Do not commit yet, ${by("Deprioritize")} Deprioritize. `+
228
+ `Confidence ceiling: ${CEILING}`;
229
+ }
230
+
231
+ function renderKpis(){
232
+ const by=t=>DATA.filter(d=>d.verdict===t).length;
233
+ const cards=[
234
+ {n:DATA.length,l:"Opportunities",dot:null},
235
+ {n:by("Decide now"),l:"Decide now",dot:"var(--green)"},
236
+ {n:by("Run a research sprint"),l:"Research sprint",dot:"var(--amber)"},
237
+ {n:by("Do not commit yet"),l:"Do not commit yet",dot:"var(--grey)"},
238
+ {n:by("Deprioritize"),l:"Deprioritize",dot:"var(--red)"}
239
+ ];
240
+ document.getElementById("kpis").innerHTML = cards.map(c=>
241
+ `<div class="kpi"><div class="n">${c.n}</div><div class="l">${c.dot?`<span class="dot" style="background:${c.dot}"></span>`:""}${c.l}</div></div>`
242
+ ).join("");
243
+ }
244
+
245
+ function card(d){
246
+ const c=C[d.color];
247
+ return `<div class="card" style="border-left-color:${c.ink}">
248
+ <div class="row1">
249
+ <h3>${d.id}. ${d.name}</h3>
250
+ <span class="verdict" style="color:${c.ink};background:${c.bg}">${d.verdict}</span>
251
+ </div>
252
+ <div class="bands">
253
+ <div class="band">
254
+ <div class="lab"><span>Value</span><span class="bandname" style="color:${bandColor(d.valueBand)}">${d.valueBand}</span></div>
255
+ <div class="barwrap"><div class="fill" style="width:${d.value}%;background:${bandColor(d.valueBand)}"></div></div>
256
+ <div class="tag">audit: ${d.value}/100</div>
257
+ </div>
258
+ <div class="band">
259
+ <div class="lab"><span>Confidence</span><span class="bandname" style="color:${bandColor(d.confBand)}">${d.confBand}</span></div>
260
+ <div class="barwrap"><div class="fill" style="width:${d.confidence}%;background:${bandColor(d.confBand)}"></div></div>
261
+ <div class="tag">audit: ${d.confidence}/100</div>
262
+ </div>
263
+ </div>
264
+ <div class="meta">
265
+ <span><b>${d.weight}</b> evidence weight</span>
266
+ <span><b>${d.accounts}</b> accounts affected</span>
267
+ <span><b>${d.sources}</b> source types</span>
268
+ </div>
269
+ <blockquote>&ldquo;${d.quote}&rdquo; <span class="tag">(${d.quoteSrc})</span></blockquote>
270
+ <div class="pa"><span class="k">Top complaint:</span> ${d.complaint}</div>
271
+ <div class="pa"><span class="k">The ask:</span> ${d.ask}</div>
272
+ <div class="why"><b>Why it ranks here:</b> ${d.why}</div>
273
+ <div class="gaps"><span class="k">Open gaps:</span> ${d.gaps.join(" &middot; ")}</div>
274
+ </div>`;
275
+ }
276
+
277
+ let sortKey="tier";
278
+ function renderCards(){
279
+ const arr=[...DATA].sort((a,b)=>{
280
+ if(sortKey==="tier") return a.tier-b.tier;
281
+ return b[sortKey]-a[sortKey];
282
+ });
283
+ document.getElementById("cards").innerHTML = arr.map(card).join("");
284
+ }
285
+
286
+ function renderFooter(){
287
+ const top=DATA.find(d=>d.tier===1)||DATA[0];
288
+ document.getElementById("footer").innerHTML =
289
+ `<b>Honest read.</b> Confidence ceiling: ${CEILING}<div class="move">`+
290
+ `Cheapest move to raise the top open item: instrument the connection step to break the 38% drop down by connector, which would convert T1's remaining gap into a measured fact and de-risk the build scope.</div>`;
291
+ }
292
+
293
+ document.querySelectorAll(".toolbar button").forEach(b=>{
294
+ b.addEventListener("click",()=>{
295
+ document.querySelectorAll(".toolbar button").forEach(x=>x.classList.remove("active"));
296
+ b.classList.add("active");
297
+ sortKey=b.dataset.sort;
298
+ renderCards();
299
+ });
300
+ });
301
+
302
+ renderSummary(); renderKpis(); renderCards(); renderFooter();
303
+ </script>
304
+ </body>
305
+ </html>
product-eval/examples/activation-q3/scores.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Scores for activation-q3 (exact 0-100 stored for audit; chat leads with bands)
2
+
3
+ - problem: T1
4
+ value: 72
5
+ value_inputs: {persona: 20, funnel: 22, frequency: 20, competitive: 10}
6
+ value_band: high
7
+ confidence: 89
8
+ confidence_band: high
9
+ recommendation: "Decide now"
10
+ rationale: "High-value activation cliff, corroborated across 4 first-party source types; the math supports committing."
11
+ override: null
12
+
13
+ - problem: T2
14
+ value: 70
15
+ value_inputs: {persona: 20, funnel: 15, frequency: 15, competitive: 20}
16
+ value_band: high
17
+ confidence: 63
18
+ confidence_band: moderate
19
+ recommendation: "Run a research sprint"
20
+ rationale: "High value and competitors solved it, but no first-party usage data; firm up proof before committing."
21
+ override: null
22
+
23
+ - problem: T3
24
+ value: 60
25
+ value_inputs: {persona: 15, funnel: 25, frequency: 10, competitive: 10}
26
+ value_band: moderate
27
+ confidence: 24
28
+ confidence_band: low
29
+ recommendation: "Do not commit yet"
30
+ rationale: "Retention-relevant if true, but the core hypothesis is directly contradicted by first-party analytics (E12)."
31
+ override: null
32
+
33
+ - problem: T4
34
+ value: 35
35
+ value_inputs: {persona: 10, funnel: 10, frequency: 10, competitive: 5}
36
+ value_band: low
37
+ confidence: 12
38
+ confidence_band: low
39
+ recommendation: "Deprioritize"
40
+ rationale: "Prize too small: secondary persona, engagement-only, no competitive pressure."
41
+ override: null
product-eval/examples/activation-q3/themes.md ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Problem-framed themes for activation-q3
2
+
3
+ - id: T1
4
+ name: "New admins can't finish workspace setup without CS help"
5
+ members: [E1, E2, E3, E4]
6
+ personas: [admin, buyer]
7
+ distinct_accounts: 9
8
+ total_weight: 31
9
+ source_types: [product_analytics, support_ticket, customer_interview, churn_survey]
10
+ trend: up
11
+ corroboration: cross-source
12
+ contradictions: []
13
+ confidence: 89 # base 89 (W=31) x 1.0 (4 source types) x 1.0 (no contradictions)
14
+ confidence_band: high
15
+ gaps: ["which connector is worst (Postgres vs Snowflake)?", "is the 38% inflated by trial spam?"]
16
+
17
+ - id: T2
18
+ name: "Teams can't share saved views across the org"
19
+ members: [E5, E6, E7]
20
+ personas: [admin, end_user, buyer]
21
+ distinct_accounts: 6
22
+ total_weight: 14
23
+ source_types: [feature_request, sales_call, app_review]
24
+ trend: emerging
25
+ corroboration: cross-source
26
+ contradictions: []
27
+ confidence: 63 # base 63 (W=14) x 1.0 (3 source types) x 1.0
28
+ confidence_band: moderate
29
+ gaps: ["how many seats would actually share?", "no first-party usage data on view re-creation"]
30
+
31
+ - id: T3
32
+ name: "Alert noise pushes users to mute critical alerts"
33
+ members: [E8, E9]
34
+ personas: [end_user]
35
+ distinct_accounts: 2
36
+ total_weight: 5
37
+ source_types: [internal_observation, community_post]
38
+ trend: flat
39
+ corroboration: single-source-ish
40
+ contradictions: [E12] # analytics show alert engagement flat -> 1 unresolved material contradiction
41
+ confidence: 24 # base 30 (W=5) x 0.9 (2 source types) x 0.9 (1 contradiction)
42
+ confidence_band: low
43
+ gaps: ["no measured mute-rate", "E8 hypothesis directly contradicted by E12"]
44
+
45
+ - id: T4
46
+ name: "Users want a dark-mode dashboard"
47
+ members: [E10, E11]
48
+ personas: [end_user]
49
+ distinct_accounts: 4
50
+ total_weight: 2
51
+ source_types: [feature_request, app_review]
52
+ trend: flat
53
+ corroboration: weak
54
+ contradictions: []
55
+ confidence: 12 # base 13 (W=2) x 0.9 (2 source types) x 1.0
56
+ confidence_band: low
57
+ gaps: ["no business-impact signal", "aesthetic preference, low frequency"]