problem-pages-plan.md
c12 thread: problem-pages — plan
Written 2026-09-21 from a read of every page, the layout, the composable, and the components in site/. Prompt: problem-pages-prompt.md. This expands plan.md §2J into a placement plan and becomes the spec for thread T2. Status: plan; nothing built.
1. What the site is today
Eight pages, one composable, ten components. Every page reads data/all-calls.json (+ use-cases.json, feedback.json, analysis.json) through useCallData(); a global FilterBar (size, call type, funnel, source type, perspective, theme, interest) and a nav-level TimeframeFilter narrow filteredCalls. All pages share one card idiom: border border-grey-700 rounded-xl p-6 with an uppercase tracking-wider section label.
| Page | What it answers today | Lens | Verdict for the problem framing |
|---|---|---|---|
Dashboard / | Three rows of StatCard (conversations, avg interest, avg urgency; use cases with evidence, product feedback, market signals; next steps, action items, risks) → Use-Case Evidence rollup (gate status, direction/perspective pills, top 3 items) → Direction (c11 session rollup) → six DistributionBar charts → themes → AI recommendations → recent calls | Sales review first, thesis second | Re-order, don't rebuild. The Use-Case Evidence and Direction blocks are already problem-adjacent; the KPI rows are the sales-shaped part. |
Calls /calls | Filterable table of calls | Neutral | Add a Problems column (count chips). |
Call detail /calls/[id] | Summary + Details, attendees, themes, Use-Case Evidence, Product Feedback, market-signal sections, Session Record, urgency, action items, decision making, positioning, competitive, key quotes | Per-call, type-sliced | Add one section near the top: problems tagged here + candidate problems from extraction. This is where promotion happens. |
Companies /companies, /companies/[name] | Per-company cards; detail rolls up the company's calls | Segment | Add problems-per-company chips. Small. |
Insights /insights | InsightCategoryCard per market-signal type grouped by category, with mention/company counts; competitive landscape | Thesis categories | Stays as the market lens. Gains a surface facet in the filter, nothing more. |
Feedback /feedback | Product-feedback observations grouped by product area, status pill from feedback.json (always new) | Triage | Retire the lifecycle (decision 4): drop the status pill and the rollup read; banner "not a tracker"; bugs and blockers stay listed. Keep the page, demote it in the nav. |
Segments /segments | SegmentCard by company size / persona / funnel; AI segment analysis | Market | Untouched this cycle; persona segments later read personas.json. |
| About | Prose mirror of README "How to Interpret" | — | Add the micro-problem section. |
What already exists that the problem pages compose from (assemblage, not invention): StatCard, Insight + InsightQuote (text + verbatim + speaker + role + company links), InsightCategoryCard (collapsible grouped list with mention and company counts — nearly a problem list already), CountDots, DistributionBar, the pill helpers in site/utils/format.ts (perspectivePill, sourceTypePill, directionPill, validationPill), the Use-Case Evidence card markup on the dashboard (gate pill with reasons tooltip, direction pills, perspective pills, top-N items with call links), and the Direction block (three-column rollup of session items). The problem detail page is the use-case card at full size; the problem list is InsightCategoryCard with standing instead of category.
2. Placement — where the problem-centric framing lives
2.1 Navigation
Dashboard · Problems · Calls · Companies · Insights · Feedback · Segments · About → Problems moves to second position, immediately after Dashboard. Feedback drops to after Insights (it is observation-level now). The nav is the first thing that says what the tool is for.
2.2 Dashboard / — re-ordered, first row becomes the problem space
- Row 1 (new): Problem KPIs.
StatCard× 3: Problems recurring (valueN, subtitle "first-hand from 2+ orgs", →/problems?standing=recurring), Problems observed (subtitle "first-hand from one org"), Candidates awaiting review (subtitle "proposed by extraction, not promoted", →/problems#candidates). A fourth optional: Briefed / in design onceconsumers.jsonexists. - Row 2 (new): Problem space — a compact card: top 6 problems by standing then first-hand count, each row = statement (bold) · question (muted, one line) · standing pill · company chips · persona chips · status pill · "→". Link "All problems →". This replaces nothing; it sits above Use-Case Evidence.
- Use-Case Evidence stays, and each use-case card gains a line of child problem chips (the decomposition made visible) — problems whose
useCaseIdsinclude it. A use case with no children reads "no problem under this hypothesis" in the same muted yellow used for "Not validated". - Direction stays; each decision/idea that carries a
problemIdshows a small problem chip. - Rows 3–4 (existing sales KPIs) move below Direction: conversations / avg interest / avg urgency; next steps / action items / risks. Nothing removed.
- Charts, themes, recommendations, recent calls unchanged. One new chart in the charts grid: Problems by surface (
DistributionBaroversurfaces[]of promoted problems) beside Observation Mix; build it with the/datavizrules when the time comes.
2.3 /problems — the registry, the new home
Header: "Problems" · "The problem space, formed from N conversations · standing is computed from first-hand, non-internal evidence; advisor and team input is labelled".
- Controls row: the global
FilterBar(so timeframe/source/perspective still narrow evidence counts) plus three problem-specific selects: standing (candidate / observed / recurring), persona, surface, use case, status. A "sort by" toggle: standing · first-hand count · recency of last evidence · status. - List: one card per promoted problem, in the
InsightCategoryCardcollapsible idiom:- collapsed row: statement · standing pill (
recurringgreen /observedsky /candidategrey, tooltip = reasons like the validation pill) ·CountDotsof first-hand count · company chips (with source-type colour, so "Tiled · Moto‑advisory" reads honestly) · status pill (in-design,partial, …) · persona chips · surface chips · use-case chips - expanded: the question in the quote style · situation · evidence grouped by perspective (first-hand first, then relayed, then internal) as
Insightcards with verbatim, speaker, role, company link · challenge evidence in its own sub-row (red-tinted pill) · session items tagged to it (decision/idea with status) · consumers (briefs, design/bob-wire cycles) · "what would make this recurring" (the computed reasons)
- collapsed row: statement · standing pill (
- Candidates section (anchor
#candidates, below the list): everycandidateProblems[]item acrossfilteredCallsnot yet promoted, grouped bynearestProblemId("near: personal-vs-enterprise-accounts") or "new", each with statement, question (if proposed), persona hint, speaker + perspective pill, verbatim, call link, and a copy-JSON action that puts a ready-to-paste registry entry on the clipboard. This is the promotion workflow: you curatedata/problems.json; the site never writes. - Missing-hypothesis callout at the top of the list when any promoted problem has empty
useCaseIds: "N problems have no use case — a hypothesis may be missing" with chips.
2.4 /problems/[id] — one problem, full size
The use-case card at full page size, same order as the expanded row above, plus: a status timeline (dated transitions from the registry: candidate → observed → … with the consumer that moved it), a by-company table (company · source type · first-hand / relayed / internal counts · last heard), and related problems (share a use case or a surface). Deep-linkable so a brief's Source block can cite /problems/<id>.
2.5 /calls/[id] — where promotion starts
New section Problems directly after Summary + Details (before Attendees):
- left: tagged problems — chips for every
problemIdon this call's observations and session items, each with the count of items and a link to/problems/<id>; hover shows the items. - right: candidate problems (from
candidateProblems[]): statement, question, persona hint, speaker + perspective pill, verbatim,near: <id>chip, copy-JSON action. Empty state: "extraction proposed no new problems for this call". - Details grid gains Shown (artifact chips from the triage
shown[]declaration) once Q10 lands; observations withartifactRefshow a small "re:" chip.
Existing sections unchanged. Observation cards (evidence, feedback, market signals) gain a problem chip where problemId is set.
2.6 Smaller placements
/callstable: a Problems column with chips (count-capped, "+N")./companies/[name]: a Problems card: which promoted problems this company evidences, with perspective counts./companiescards: a problem count./insights:FilterBargains surface and persona selects (shared, so they also apply on/problems);Insightcards show a problem chip when tagged. No structural change — this stays the market lens./feedback: remove thefeedback.jsonread and status pill; banner "Product feedback is listed here, not tracked here — defects go to a tracker when one exists (c12 decision 4)"; items gain a surface chip and problem chip. Nav label stays "Feedback"./about: a "Micro problems" subsection under How to Interpret (statement vs question, standing rule, candidates vs promoted), mirroring the README change in T4.
3. Data contract the site needs (what T1/T2 must produce)
The site reads JSON at runtime from /data/. New or extended files:
| File | Producer | Shape (summary) |
|---|---|---|
data/problems.json | Mark (curated) | { problems: [{ id, statement, question, personaIds[], situation, useCaseIds[], surfaces[], status, statusHistory[{status,date,by?}], origin, notes, evidence?: [{ callId, observations?: number[], session?: { decisions?: number[], ideas?: number[] } }] }] } |
data/problems-index.json | build-index.ts (T1) | per problem: standing, counts { companies, firstHand, relayed, internal, challenges }, reasons[], companies[{ name, sourceType, counts }], lastHeard, resolved evidence items (callId, index, kind, perspective) |
data/all-calls.json / data/calls/*.json | extraction (T1) | observations[].problemId?, .problemDirection?, .surface?, .artifactRef?; session.decisions[].problemId?, session.ideas[].problemId?; candidateProblems[] |
data/personas.json | Mark, seeded from c55 (T1) | { personas: [{ id, name, orgSize, role, jobs[] }] } |
data/consumers.json | npm run consumers (T4) | { briefs: { [file]: cycles[] }, problems: { [id]: [{ repo, cycle, artifact }] } } |
Bridge so the site can ship before extraction tags flow: problems.json carries hand-curated evidence[] refs (call id + observation indices), transcribed from problems-draft.md. build-index.ts merges curated refs with extraction's problemId tags into one evidence list per problem (deduplicated by call + index). Curation stays legitimate after T1 — Mark can always pin an observation to a problem the extractor missed.
useCallData.ts keeps its own interface copies (known gotcha): add problemId, problemDirection, surface, artifactRef on Observation; problemId on session items; candidateProblems on CallData; Problem, ProblemStanding, Persona interfaces. Load problems.json, problems-index.json, personas.json, consumers.json in loadData() (each .catch to empty, as today). Drop the feedback.json load.
4. Composable and components
useCallData()gains:problems,personas,problemRollup(per problem: registry record + index standing + evidence items resolved againstfilteredCalls, so the global filters and timeframe narrow the counts; recomputed client-side from the same rule asproblem-standing.tsso filtered views stay honest),candidateProblems(flattened acrossfilteredCalls, withpromoted: boolean),problemsBySurface,problemsByPersona.FiltersgainsproblemId,surface,persona;filterOptionsgains the three lists.- New components:
ProblemRow.vue(collapsed/expanded card),StandingPill.vue(with reasons tooltip; mirrorsvalidationPill),ProblemChip.vue(id → statement tooltip, link),SurfaceChip.vue,PersonaChip.vue,CandidateProblem.vue(with copy-JSON). Reused as-is:StatCard,Insight,InsightQuote,CountDots,DistributionBar,FilterBar(extended). - Colours stay site-side in
site/utils/format.ts(standingPill,problemStatusPill,surfaceLabel,personaLabel); labels for surfaces and personas come fromschemas/vocabularies.ts/personas.jsonthrough the existing cross-project import bridge.
5. Build order (this is T2, sequenced)
- Registry bridge (no extraction change): transcribe
problems-draft.mdintodata/problems.jsonwith curatedevidence[]refs;personas.jsonfrom c55 + P12;problem-standing.ts+build-index.tswritingproblems-index.json.npm run index. Gate: the 15 problems and their standing appear in the index exactly as the draft computed by hand. /problemslist +/problems/[id]against the bridge data; nav reordered.- Dashboard re-order (rows 1–2, child chips on use cases, sales KPIs demoted).
- Call page Problems section (tagged + candidates; candidates will be empty until T1 lands, the section says so).
- Smaller placements (calls table column, companies, insights filter facets, feedback retire, about).
- After T1: candidates populate,
problemIdtags merge with curated refs, artifact chips appear. No page changes needed for that — only data.
Each step: npx tsc --noEmit clean in site/; dev server check; commit.
6. Decisions — confirmed by Mark 2026-09-21
- Nav: "Problems", second position. ✅
- Dashboard: re-order as §2.2 — chosen because it keeps the most gateways to other places. Rule for the build: every new element is a link (KPI cards →
/problemsfiltered; problem rows →/problems/<id>; child chips → problem; company chips → company; nothing on the existing dashboard loses its link). ✅ - Feedback page: remove it and fold in. Product-feedback observations surface on
/problems(tagged ones under their problem; untagged bugs/blockers in a "Feedback not tied to a problem" section at the bottom, with the "not a tracker" note), on the call page (already), and via atypefacet on Insights. Nav loses "Feedback";feedback.jsonis no longer read. Redirect/feedback→/problems#feedback. ✅ (supersedes §2.6 "feedback" bullet) - Build against the bridge now. ✅
- Source type beside each company chip in the standing label. ✅
7. Verification
/problemslists 15 promoted problems; the four recurring ones (unknown-agent-fleet, personal-vs-enterprise-accounts, unexplained-ai-spend, prove-posture-over-time) carryrecurringwith two company chips each; counts equalproblems-index.json.- Narrowing the
FilterBartosourceType = prospectdrops Moto evidence and re-computes standing live (personal-vs-enterprise-accounts falls toobserved). /problems/personal-vs-enterprise-accountsshows the question as the quote, evidence grouped first-hand → relayed → internal, and the c21 brief under consumers onceconsumers.jsonexists.- Dashboard row 1 reads recurring 4 / observed 11 / candidates 0 (before T1); use-case cards show child chips;
gateway-bypass-detectionreads "no problem under this hypothesis". /calls/09ecfede…Problems section shows chips for sensor-coverage-gap, personal-vs-enterprise-accounts, unexplained-ai-spend, web-ai-usage-blindspot, session-transcript-governance from curated refs./feedbackrenders with no status pills and the not-a-tracker banner;feedback.jsonis no longer fetched.npx tsc --noEmitclean at root and insite/;npm run generatesucceeds (with the dev server stopped).