← Market Analyst · c12

plan.md

c12 Plan — Micro Problems (draft, pre-kick-off)

Drafted 2026-09-21. Status: planning only — nothing built, no CYCLE.md, not on CYCLES.md. Companion: lens-analysis.md (the "why"). Kick-off waits on the decision queue in §5.

1. Context

Mark's ask: the tool was conceived as a market analyst; it is now used to build design briefs that feed product work. Shape it to gather design-grade insight without losing the market-shaped work. Treat market-analyst as the source of truth for the problem space, used to draft micro problems (the design methodology's term: a wicked macro problem given tangible form as addressable pieces) that in turn tell stories, set priority, write briefs, and inform the product. Analyse the existing lens, recommend changes, plan the cycle. Plan first; ask; suggest what he may not be considering.

Mode: archaeology → assemblage. The problems are already in the corpus; the pieces (perspective, gate, session records, brief template, personas) already exist. c12 surfaces the former and composes the latter into a new grain.

What I verified (read-only)

  • Corpus: 6 phase-2 calls processed, 7 entries in data/call-classifications.json (the Aug 7 "Mark | Qpoint Catch-Up" was classified null today, 2026-09-21 — uncommitted, left untouched). 0 of 7 use cases pass the gate. data/feedback.json has never held an item.
  • The design need is visible in artifacts written by hand: briefs/cam-dashboard-directives.md is organized around "what the dashboard should answer" (user questions); bob-wire c21.inventory/design-brief.md is user stories in question form + decisions-as-constraints + open questions, assembled from a session record and Mark's notes. Neither had data to draw on at that grain.
  • Design has a page vocabulary this repo lacks (c90/c92: Fleet Overview, Teams/Users/Devices, Inventory, Sessions, Session Details, actor detail pages; c92/detail-pages-guide.md states purpose per page). PRODUCT_AREAS here is capability-level and predates it.
  • Design c55 personas are hypothesis-first and unlinked to this corpus (11 personas, page × persona matrix, "no relevant calls yet" at the time).
  • The strongest voice is archived: Cam's 9 Cisco build-partner calls are phase-1 schema only (archive/phase-1/); the gate cannot see them.
  • Only transcripts enter the pipeline. inbox/ (Cam's chat, QControl-as-Signal docs) has no path in.
  • Hand-off framing is stale: design CLAUDE.md still describes this repo as "Customer intelligence & ICP data."
  • Patterns to reuse by analogy: use-cases.json + useCaseId + direction + computeValidation() (registry curated by a human, extraction tags evidence, standing computed in code); c11's triage declaration (a human states the nature of the input; the prompt gets it as a dynamic block; the static prefix stays cached).

2. Design (lean)

A. Registry. data/problems.json — curated by Mark, extraction never writes it. Record: id, statement (the pain, one sentence, in the user's frame — canonical), question (the mirror image: the question the user asks of the product; required to promote a candidate — if it cannot be written, the product cannot address it), personaIds[], situation, useCaseIds[] (may be empty — a signal), surfaces[], status (candidate → observed → recurring → briefed → in-design → shipped → validated-in-use | retired, each transition dated), origin { callId | docId, date, speaker, perspective }, notes.

B. Extraction — four optional fields, no new observation type.

  • Observation.problemId? — tag any observation (pain-point, feature-request, use-case-evidence, concern…) to a registered problem, plus problemDirection?: has | challenges | variant (has = they have it; challenges = evidence it is not real/ready; variant = a different shape of it).
  • CallData.candidateProblems[] — { statement, question?, personaHint, situation, speaker, perspective, verbatim, nearestProblemId? }. The archaeology output Mark curates into A; extraction proposes both forms when it can.
  • Observation.surface? — from a new PRODUCT_SURFACES vocabulary in vocabularies.ts (hand-synced from design's page registry; single source stays here).
  • Observation.artifactRef? — { repo: design | bob-wire | qdash, slug } when the reaction is to something shown; only allowed when triage declared it (C).
  • CallData.language[]? — { term, meaningAsUsed, speaker, perspective, verbatim }. Small; skip if Mark says no (§5 Q9).
  • Prompt: registry rendered into the static (cached) part like {{USE_CASES}}; nothing per-call except C's shown-artifacts line in the dynamic block.

C. Triage — one more optional field. shown?: string[] (artifact slugs) on the classification entry. /extract asks "anything on screen?" with Enter = none. The prompt's "This Conversation" block lists them; artifactRef outside that list is stripped by finalize.ts.

D. Standing is computed. schemas/problem-standing.ts computeStanding(problemId, calls) → { standing: candidate | observed | recurring, counts: { companies, firstHand, relayed, internal, challenges }, reasons[] }. observed = ≥1 first-hand has from a non-internal source; recurring = ≥2 distinct companies. Used by build-index.ts (writes data/problems-index.json), the site, and analyze. Same discipline as validation-gate.ts: the model is told the standing and narrates around it.

E. Intake expansion. npm run ingest -- <file.md> --source <sourceType> --type document --date … --speaker … writes data/raw/<docId>.txt + hand-authored .meta.json (source: 'document'), classification entry, then the normal extract path. CALL_TYPES gains document; MeetingSource gains document. Same prompt; the profile block says "this is a written document, not a transcript; speaker is the author." Date backfill skipped as for sagetap.

F. Legacy reconnect (decision Q6). Batch re-extract Cam's 9 phase-1 calls as build-partner × working-session with legacy: true on the index entry; market KPIs and the use-case gate exclude legacy by default (flag to include), the problem layer includes it. Raw transcripts copy from archive/phase-1/data/raw/; the archive itself stays frozen.

G. Registries. data/personas.json seeded from design c55's persona one-pagers (id, name, orgSize, role, jobs[], hypothesisSource, status). PERSONA_TYPES stays the coarse attendee tag; Attendee.personaId? links when clear.

H. Briefs. INDEX.md gains kind (problem | story | position) and problems columns; TEMPLATE.md Source block gains a Problems line (ids + standing at time of writing); /brief <problem-id | topic> reads the registry and standing first. A story uses the same template with ## Narrative replacing ## Goal/## Success Criteria: persona, situation, the sequence of problems, verbatims, the moment the product answers. Stories can name the fixture world they should seed.

I. Loop closure. npm run consumers scans ../design/cycles/*/{CYCLE.md,*-prompt.md,*-plan.md} and ../bob-wire/cycles/*/briefs/*.md for briefs/<name>.md and problem:<id> mentions → data/consumers.json; build-index.ts advances briefed / in-design (never backwards; shipped stays manual). pipeline.md hand-off rule 4: a design or bob-wire cycle that works a problem cites problem:<id> in its initial prompt or CYCLE.md.

J. Site. /problems page (table: statement, persona, surface, use case, standing, companies, first-hand count, status; row → observations grouped by call with perspective pills); dashboard first row becomes problems observed / recurring / briefed / shipped, sales KPIs second row; call page shows candidateProblems with a "register" hint; Insights gains a surface facet. useCallData.ts interface copies updated (known gotcha).

K. Docs — the identity rewrite. This is where the redefinition lands in the docs every agent reads, not just in the data: CLAUDE.md Project Overview restated (a design tool first — the source of truth for Qpoint's problem space, drafting micro problems that tell stories, set priority, and write briefs; market analysis is a critical part of it), with "What this project does" gaining the problem layer as step 3; goals.md two lenses (Market: unchanged; Design: the key questions from lens-analysis §7.7); README.md "How to Interpret" gains problems/standing/document intake; CLAUDE.md architecture + skills; /extract, /analyze, /brief skills; design CLAUDE.md Sister Projects re-framed ("source of truth for the problem space; start at data/problems.json and briefs/INDEX.md"); pipeline.md stage 2 artifact adds the problem registry + rule 4.

Explicitly not doing: removing any market instrument; a defect-tracker hand-off (bugs and deployment blockers stay observation types and are listed on the site as not tracked here; a /defects ship-off to Linear/GitHub/Slack is shelved until a formal tracker exists — follow-up); an MCP server (noted as follow-up); a per-problem LLM map stage (computed tallies first; add a map only if the registry passes ~25 entries); OAuth (c3 stays parked).

3. Threads (ordered)

  • T0 — Archaeology (no code). Read the 6 processed calls, the Cam brief, the c21 brief, c55 page-persona-analysis.md, c92 detail-pages-guide.md. Hand-write problems-draft.md: 10–15 micro problems in the §2A shape, each with the observations that evidence it (call id, speaker, perspective), plus the persona and surface lists it implies. Review with Mark. Gate: if the shape needs changing, change §2 before T1. Also produces the first PRODUCT_SURFACES and personas.json seed lists.
  • T1 — Registry + extraction + triage. §2A–D. vocabularies.ts (PRODUCT_SURFACES, PROBLEM_STATUSES, PROBLEM_DIRECTIONS, render helpers), call.ts, validation.ts (new fields, artifactRef ∈ declared shown), prompt.ts ({{PROBLEMS}} static; shown-artifacts dynamic), extract-call.md (guideline 18: problems; 19: candidates; 20: language), extract.ts triage shown, classifications.ts, finalize.ts, build-index.ts (problems-index.json), problem-standing.ts. Pilot: re-extract Tiled (prospect × demo) and Mark P Aug 31 (advisory × roadmap) against the T0 registry; confirm cache still reads; confirm candidates are new material, not restatements of registered problems.
  • T2 — Site + analyze. §2J; analyze Map A/overview told each problem's standing (analyze-overview.md gains a "problem space" section: recurring problems with no brief, briefed problems with challenge evidence, surfaces with no first-hand problems).
  • T3 — Intake expansion. §2E document ingest (pilot: inbox/chat-from-cam.md as build-partner × document), and §2F legacy reconnect if Q6 is yes (batch; compare Cam-brief claims against the re-extracted candidates as the acceptance test).
  • T4 — Briefs, loop, docs. §2G–I, K. Retrofit the two existing briefs with kind + problem ids; write one story from the T0 registry as the worked example (candidate: the "sensor coverage → personal-account exposure" arc from the Aug 31 / Sep 3 Mark P calls + Tiled); run npm run consumers and confirm c21 / c78 / c81 / c92 are found.

Each thread = its own commit(s), dated Key Changes entry, and where a mid-thread prompt arrives, <thread>-prompt.md / <thread>-plan.md per the convention.

4. Execution order

  1. Mark answers §5 (or picks the recommendations). cycle-manager: kick off c12 (CYCLE.md, CYCLES.md entry). Commit c12: kick off (micro problems). Also commit or discard the pending call-classifications.json edit first — it is Mark's, not c12's.
  2. T0 → review → freeze §2 shape.
  3. T1 (pilot on 2 calls) → T2 → T3 → T4. Re-extract the corpus under the new prompt at the end of T1 (--force, 6 calls), not before.
  4. Outcome + close; note follow-ups (MCP server, digest, per-problem map stage, silence-as-signal).

5. Decision queue (clarifying questions)

Unlocking decisions (gate T0/T1):

  1. Who forms the problems? ✅ Confirmed 2026-09-21: extraction does its best to identify candidate problems from each call (candidateProblems[]); Mark and the agent then refine together and promote candidates to full problems in problems.json — problems that are addressable and solvable by the product. Extraction tags problemId only against promoted problems.
  2. Problem ↔ use case. ✅ Confirmed 2026-09-21: decomposition — a problem lists the use cases it belongs to (many-to-many, optional), use cases roll problems up, and a problem with no use case is a signal that the registry is missing a hypothesis. Distinctions to hold: problems are demand-side (the user's wicked problem decomposed), use cases are supply-side (our capability hypotheses); the link is where demand meets hypothesis. The registry holds the problem, never the solution — surfaces[] is the one deliberately solution-shaped field (where it would be answered). Granularity rule of thumb: a micro problem is answerable by one surface's data + affordances in one sitting; if it needs several, it is a use case or a story. Standing (evidence, computed) and status (lifecycle, human) are separate axes.
  3. Statement form. ✅ Confirmed 2026-09-21: both, as two fields. statement is the pain form and is canonical (title, id, story voice); question is the product-facing mirror ("Who has the sensor installed, and who doesn't?") and is required at promotion — the addressability test. /problems shows the pain as the row title with the question beneath; briefs use the question in Goal / Success Criteria and the pain in Evidence; stories use the pain.
  4. feedback.json. ✅ Confirmed 2026-09-21: retire it; product-feedback observations keep their types and severity, lifecycle lives on the problem. This is a design tool first (market analysis is a critical part); no formal bug integration at this stage. Bugs and deployment blockers surfaced in calls are listed on the site as not tracked here; a rudimentary ship-off (Linear / Slack / GitHub) is shelved as a follow-up until a formal tracker exists.

Canon/detail decisions (can be made during work):

  1. Document intake now or later? Recommend: now, lean (T3) — it is what makes "source of truth" true, and inbox/ already has material.
  2. Phase-1 Cam calls. ⏸ Deferred by Mark 2026-09-21 — "not at this time"; take up later. The case, for when it comes back:
    • For reconnecting: Cam's nine Cisco calls are the only usage-based, first-hand, build-partner, agent-era evidence anyone has; unreviewed-mcp-surface (P15) goes from the thinnest promoted problem to the best-evidenced, and unknown-agent-fleet (P1) gains a second build-partner voice; the Cam brief's Source block becomes machine-checkable instead of hand-assigned; cost is nine calls on the Batches API, well under a dollar, and a legacy: true flag keeps market KPIs and the use-case gate clean by default.
    • Against, for now: it blurs the deliberate phase-2 restart (Aug 2026) and the archive's "frozen at phase-1-final" promise; the calls predate the pivot's vocabulary, so extraction would be reading old conversations through new categories (category squeeze risk); Cam's prototype reactions are about his dashboard, not a shipped Qpoint surface, so some "usage-based" would need hand-review; it adds a flag every consumer must remember to respect; and six phase-2 calls already produce four recurring problems — the registry is not starved yet.
    • Middle path if wanted later: re-extract only the three or four Cam calls the brief cites most (Feb 24, Mar 23, Mar 30) rather than all nine.
  3. Stories. Recommend: a brief kind (kind: story), same directory and index, ## Narrative section. Alternative: a separate stories/ artifact with its own index.
  4. Personas. ✅ Confirmed 2026-09-21 ("let's try it out, we may shift"): data/personas.json here, seeded from c55 + P12 end user/developer; a person may map to two personas. Surfaces (23 page families) likewise — try, then shift as they get used. 9a. T1 pilot result (2026-09-21): see CYCLE.md — both calls PASSED; tags, challenges, session tags, artifactRef (9 on the Sep 3 call), surfaces, and candidates all exercised; cache held (14,128 read on call 2). Re-extracting a call invalidates its curated ref indices — the two pilot calls now rely on tags; curatedAgainst warns for the rest.
  5. Language capture. Recommend: yes, as a small optional language[] block. Cheap; skip if it feels like scope creep.
  6. Shown-artifacts at triage. Recommend: yes — the same "declare, don't infer" concession c11 made; Enter = none keeps it free when nothing was shown.
  7. Dashboard re-shaping. Recommend: problems first row, sales KPIs second — design reader first. Alternative: a separate /problems entry point and the dashboard unchanged.

T0 review decisions (Mark, 2026-09-21)

  • The 15 promoted problems stand as drafted. Merges considered (sensor coverage ↔ fleet compliance state; personal accounts ↔ unsanctioned services) rejected: different questions.
  • Advisor first-hand counts toward standing, labelled — advisors are first-class sources. Implemented in schemas/problem-standing.ts (source type travels with the company; the site prints "Moto · CISO Advisory"). A person-level org on the known-entities.json source override keys an advisor's company-less calls to one organization.
  • Registry changes: governance-evidence-reporting and session-record-governance added to use-cases.json; account-identity-control added as the rewrite of gateway-bypass-detection, which is parked with a note.
  • Variant evidence does not move standing. Found while wiring the index: three "adjacent" refs counted as recurring under the first rule. variant items are shown and counted separately; only has items from first-hand, non-internal sources qualify. Hand count and index now agree (4 recurring, 11 observed).

6. Risks

  • Registry drift. Problems can proliferate into a backlog. Mitigation: standing is computed; candidate with no first-hand evidence after N calls is visibly stale; retired needs a reason.
  • Double-tagging load on extraction. problemId on top of useCaseId and category. Mitigation: problemId is optional and only for registered problems; candidates are a separate block; pilot on two calls before the corpus re-extract.
  • Surface vocabulary goes stale as design's pages move (c92 renamed /teams → /teams-users-devices in a week). Mitigation: keep surfaces coarse (page families, not routes); hand-sync note in vocabularies.ts.
  • Legacy reconnect blurs the phase-2 restart. Mitigation: legacy flag, excluded by default from market KPIs and the gate.
  • Consumers scan is heuristic (grep for ids and filenames). Mitigation: it only advances to briefed / in-design; shipped stays manual with a date.
  • Static prompt grows (registry + personas + surfaces). Mitigation: all static, cached; measure cache_read_input_tokens in the T1 pilot.

7. Verification

  • npx tsc --noEmit clean (root + site).
  • T0: problems-draft.md reviewed; every problem cites ≥1 observation by call id and perspective.
  • T1 pilot: Tiled re-extract tags ≥3 registered problems first-hand; Mark P Aug 31 tags problems with perspective first-hand for his own experience and relayed for CISO-board claims; artifactRef only appears where shown was declared; cache read >0 on call 2.
  • T2: /problems renders standing + counts identical to data/problems-index.json; dashboard first row is problem KPIs.
  • T3: inbox/chat-from-cam.md ingests as build-partner × document, all observations first-hand/relayed (never usage-based unless the doc says they ran it); legacy Cam calls (if Q6) appear with legacy: true, excluded from Avg Interest, included in problem standing.
  • T4: npm run consumers finds bob-wire c21 and design c78/c81/c92 for cam-dashboard-directives.md / the c21 brief; one story brief exists with a Source block; goals.md has two lenses; design CLAUDE.md Sister Projects re-framed.

Qpoint Brand Style Guide