← Market Analyst · c11

plan.md

c11 Plan — Conversation Profiles

Context

Mark asked to (1) pull down the latest Mark Peterson call and the latest AMZ call and treat them as team meetings — speakers who offer a perspective on the customer, not the customer's own voice, though that perspective is still worth capturing; (2) create cycle c11 with a plan to explore and build an extraction engine where the kind of insight, inputs, and expected outputs follow from the nature of the conversation, so the pipeline stops being overconfident (ex blunder: a team member's or advisor's opinion counted as customer validation); (3) extract product direction, todos, outstanding decisions, tabled ideas from working sessions like the Aug 31 Mark Peterson call; (4) review the design repo's founding docs and start formalizing Raw conversations → Insights → Design Brief → wireframe → q-nuxt-layer component build → Refinement → Install on production app.

Two steering notes from Mark: be explicit about the nature of the meeting (better focus, enforceable expectations, mismatch becomes a signal), and keep it simple — small concessions only where they yield the truest analysis.

What I verified (read-only)

  • Mark Peterson's latest call is already in the corpus. 38075ead-… "Mark | Qpoint Dashboard + Reporting" (2026-08-31), extracted, indexed, stamped ciso-advisory via the person override in data/known-entities.json. Avoma has no newer Mark Peterson call.
  • The AMZ call is new. Avoma uuid 6fe4a00b-85a1-40b6-8b1b-c17d3706bc08, "AMZ | Qpoint New Feature Discussion + Fleet Overview Story", 2026-09-02, 91 min, transcript ready. Every attendee is Qpoint. Fetch with npm run fetch -- --uuid 6fe4a00b-… (does not advance the sync window). 13 other new calls exist (Cam syncs, Tyler 1:1s, recruiting screens, Uncork monthly) — out of scope.
  • The blunder is already in the data. Both Mark Peterson calls: interest 5/5, use-case-evidence validates / reaction-based. The Aug 20 AMZ + Sam Curry call: 21 observations, 12 attributed to Qpoint speakers (AMZ 7, Tyler 5) despite prompt guideline 1. Prose rules alone are not reliably followed.
  • No profile reaches extraction. buildExtractionPrompt({transcript, avomaMetadata, notesBlocks}) (pipeline/prompt.ts:64); one uniform prompt, static part cached (cut at ## Transcript). sourceType is resolved after extraction (pipeline/build-index.ts:25 resolveSourceType: person override → company record → 'prospect'). SOURCE_TYPES has internal but nothing assigns it; an all-Qpoint meeting resolves to prospect.
  • Triage is a boolean. data/call-classifications.json external: true|false; false = never extracted, never indexed (extract.ts:245, batch.ts:25, build-index.ts:85).
  • No perspective concept exists; Observation.speaker is a free string. Only hint: speculative described as "hypothetical or secondhand" (schemas/vocabularies.ts:182).
  • Weighting is prose only. goals.md: "validated only with usage-based evidence from more than one company" — never enforced in pipeline/analyze.ts; Map B (trimCallForCompany, L134) even drops evidenceKind/direction.
  • Working-session output is thin. For 38075ead: 7 product-feedback observations, 3 Avoma action items (parseActionItems, pipeline/avoma-notes.ts:84), decisionMaking: null. Avoma notes carry "Key Takeaways" / "Product Development Roadmap" / "Primary product focus decided…" blocks, injected only as free text. Decisions, tabled ideas, todos are not first-class.
  • c8 already set the principle (cycles/c8.agent-pivot/plan.md:73): "Source type says who's talking; evidence kind says what the evidence is." Open c8 items: "Mark M advisory tag decision pending", "callType tuning watch". c11 is free; c10 is complete-but-unarchived.

Design (lean)

A. Declare the nature of the meeting at triage — two fields, both existing vocabularies.data/call-classifications.json entry becomes { subject, date, sourceType, callType, classifiedAt } (sourceType: null = skip; replaces external). Triage shows a derived default for each and Enter accepts:

  • sourceType default: known-entities.json person override → companies.json → all attendees Qpoint ⇒ internal → prospect.
  • callType default: from subject/agenda keywords (review/scoping/roadmap ⇒ roadmap-planning; demo ⇒ demo; else discovery), else ask. The model still reports classification.observedCallType; build-index prints a warning when it disagrees with the declaration. Nothing else — no intent field, no toggles.

B. Expectations are a small table in code, not a UI. CONVERSATION_PROFILES in schemas/vocabularies.ts, keyed by sourceType, with a callType override for session records:

  • counterparty: external | none (internal: company is null, no positioning/decisionMaking/funnel)
  • evidenceCeiling: usage-based (build-partner, prospect) | reaction-based (ciso-advisory, industry-peer, analyst) | speculative (investor, internal)
  • ratesInterest: false for internal; advisory/investor interest is labelled "conviction" in the site
  • sessionRecord: true when callType ∈ {working-session, roadmap-planning, build-feedback, advisor-input}
  • guidance: 2–3 sentences of profile-specific extraction focus Rendered into the static (cached) prompt as one table; the per-call profile line ("This is a ciso-advisory × roadmap-planning conversation; ceiling reaction-based; emit session record") goes at the top of the dynamic part. Static prefix stays byte-identical.

C. One new observation field: perspective = first-hand (speaker's own org/experience/need) | relayed (speaker reporting what others want or would do) | internal (Qpoint speaker). This is the concession that fixes the blunder: the AMZ + Curry call stays prospect, AMZ's lines become internal, Curry's first-hand, "CISOs will never…" becomes relayed.

D. Two deterministic rules in schemas/validation.ts (warn to console, then fix):

  1. speaker matches a Qpoint attendee or roster ⇒ perspective: internal (the code enforcement guideline 1 never had).
  2. use-case-evidence above the profile's evidenceCeiling, or with perspective ≠ first-hand and evidenceKind: usage-based ⇒ downgrade evidenceKind to the ceiling / speculative. Never touches direction.

E. One pure function in pipeline/analyze.ts. computeValidation(evidence) → { status: validated | partial | unvalidated, reasons[] } from the goals.md rule: ≥2 distinct companies with validates + usage-based + first-hand from a non-internal source. Written next to the existing tallies (L190); the prompt is told the status; the model narrates, it does not decide. Map B stops dropping evidence qualifiers.

F. Session record = three lists, emitted when the profile says so, on CallData.session:

decisions[]: { text, status: made | open | tabled, owner?, speaker, verbatim? }
todos[]:     { text, owner?, source: transcript | avoma-notes }   // merged with parseActionItems()
ideas[]:     { text, status: parked | adopted, speaker, perspective, relatedUseCaseId? }

Avoma "Key Takeaways"/"Roadmap"/"Action Item" blocks are candidates the model must confirm against the transcript. Cross-call rollup (open decisions, todos by owner, parked ideas) is computed in site/composables/useCallData.ts from all-calls.json — no new pipeline file, no new page: a "Direction" section on the dashboard and a session tab on the call detail page.

G. resolveSourceType precedence: declared classification → person override → company record → all-Qpoint ⇒ internal → prospect. README amended: the label still corrects via npm run index; the extraction behaviour needs a re-extract.

H. No stopgap. Extracting the AMZ call under today's prompt yields either nothing (guideline 1 followed) or leaked team opinion as prospect signal (Aug 20). Fetch now; extract as the T1 pilot.

Threads (ordered)

  • T1 — Profile + perspective + session record; pilot on the two calls.
    • schemas/vocabularies.ts: PERSPECTIVES, CONVERSATION_PROFILES, renderConversationProfiles().
    • schemas/call.ts: Observation.perspective, Classification.observedCallType?, Classification.interestLevel?, CallData.profile { sourceType, callType }, CallData.session?.
    • pipeline/prompts/extract-call.md: perspective in schema + guideline; rewrite guideline 1 (capture Qpoint speakers, tag internal; tag secondhand relayed); {{CONVERSATION_PROFILES}}; session-record section.
    • pipeline/prompt.ts: buildExtractionPrompt({…, profile}); profile line in dynamic part.
    • pipeline/extract.ts: triage (L67-107) asks sourceType + callType with derived defaults; single-file path reads the classification (or --source/--type); stamp profile; print cache_read_input_tokens.
    • pipeline/batch.ts: filter sourceType !== null; pass profile.
    • schemas/validation.ts: perspective vocab + rules D1/D2 (uses attendees + roster from pipeline/known-entities.ts).
    • pipeline/build-index.ts: precedence G; observedCallType warning; index gains perspectiveCounts, sessionCounts.
    • Baseline first (scratch run, recorded in cycles/c11.<name>/CYCLE.md): Qpoint-speaker leakage per call; validates items by sourceType; gate result on today's data (expected 0 validated).
    • Pilot: npm run fetch -- --uuid 6fe4a00b-…; triage internal × working-session; extract; re-extract 38075ead as ciso-advisory × roadmap-planning; npm run index; review both session records with Mark.
  • T2 — Gate + site. analyze.ts (EvidenceItem.perspective, computeValidation, perspectiveBalance, Map B fix); prompts/analyze-use-case.md told the computed status. Site: perspective pill (site/utils/format.ts, pages/calls/[id].vue next to the Qpoint badge at L81); perspective + sourceType in FilterBar.vue; internal/advisory excluded from prospect KPIs (useCallData.ts:354/413); use-case cards show gate status + reasons; Direction section + session tab.
  • T3 — Re-extract corpus (5 files) + docs. --force, index, analyze. Update README.md "How to Interpret", CLAUDE.md, .claude/skills/extract/SKILL.md, goals.md. Close c8's "Mark M tag" item (stays ciso-advisory; his calls are declared by callType).
  • T4 — Design-machine hand-off (lean). Survey result per stage — 1 Raw conversations: documented (market-analyst CLAUDE.md § Data Flow). 2 Insights: a queryable corpus, not a durable artifact; consumers hand-copy quotes into their own cycle docs (design c77/c78/c81; c81 had to inline Cam's directive because the repo wasn't reachable). 3 Design brief: bob-wire brief-writer template is de-facto canon but its "Before Starting" never reads market-analyst; design hand-writes briefs in three shapes; no brief has a required source field. 4 Wireframe: fully documented (bob-wire). 5 Layer component build: mockups + extraction documented, the "graduation" trigger is undefined (used in c67/c77/c79, defined only in abandoned c44). 6 Refinement: practice is rich (/iterate, c84 iteration prompt/plan pairs, c67 checkpoint gate) but undefined as a stage; design's page-registry.ts has no status field. 7 Production install: layer release is airtight (q-nuxt-layer/CLAUDE.md § Releasing, design/.claude/skills/release-layer), but surfaces ship to qcontrol/qdash, which is in no Sister Projects list and not in buildCycles.ts; nothing links shipped UI back to the originating insight, so feedback.json's shipped/validated-in-use statuses never advance. The one real end-to-end run is bob-wire c21.inventory, sourced by hand from the Aug 31 Mark Peterson call. c11 does the three smallest things that make the front half explicit, without reversing c12's peer-to-peer ruling:
    1. Brief = the Insights artifact. market-analyst/briefs/ gets a template with a required ## Source block (call ids, observation/session item refs, sourceType + perspective + evidenceKind visible) and a briefs/INDEX.md; cam-dashboard-directives.md is retrofitted as the first entry. A /brief skill drafts one from /analyze output (the analyze skill stays read-only).
    2. Pipeline map, one page. design/brand/pipeline.md (or a § in design_philosophy.md "How We Work"): the 7 stages as a table — artifact, owner repo, location, status field — with the c21 run as the worked example. A map, not an orchestrator.
    3. Bilateral pointers. bob-wire/.claude/agents/brief-writer.md "Before Starting" reads ../market-analyst/briefs/INDEX.md; market-analyst CLAUDE.md Sister Projects names the brief as the hand-off artifact. Deferred to a design-repo cycle (noted in c11's Outcome as follow-ups): graduation rule in component_philosophy.md, refinement/ruling record + status field on page-registry.ts, ## Ship section convention, qcontrol in Sister Projects / buildCycles.ts, back-link from shipped work to brief ids.

Decisions confirmed by Mark (2026-09-03)

  • AMZ call: fetch now; extract only as the T1 pilot (no stopgap under the current prompt).
  • Mark Peterson stays ciso-advisory — he acts as a product advisor under his own firm's identity. The declared callType + perspective tagging is what keeps his conviction from reading as customer validation.
  • Pipeline map lives in design/brand, with clear references from this repo's docs. Where appropriate, this repo's CLAUDE.md/skills direct agents to read the relevant instructions in ../design. Operating model to state in the map: design is the driver; appendage projects (market-analyst, bob-wire, q-nuxt-layer) run as sub-agents with clear goals and narrowed scope. market-analyst's scope in that model: turn conversations into perspective-tagged insights and briefs; nothing downstream.
  • Archive c10 in the same commit that creates c11.

Execution order

  1. npm run fetch -- --uuid 6fe4a00b-85a1-40b6-8b1b-c17d3706bc08 → raw transcript + sidecars only. No extraction yet.
  2. cycle-manager: create cycles/c11.conversation-profiles/ (initial-prompt.md, CYCLE.md, plan.md), add to CYCLES.md, archive c10 to HISTORY.md. Commit c11: create cycle (conversation profiles) + close c10.
  3. T1 implementation + pilot on the two calls; review session records with Mark; commit.
  4. T2 → T3 → T4 as separate commits, Key Changes dated in CYCLE.md as they land.

Cycle records (via cycle-manager agent)

  • cycles/c11.conversation-profiles/initial-prompt.md — Mark's ask verbatim (all messages), canonical closing line.
  • cycles/c11.conversation-profiles/CYCLE.md — Why / Objective / Scope (T1–T4; out of scope: the 13 other Avoma calls, OAuth, design-repo stages 5–7) / Key Changes (dated) / Outcome.
  • cycles/c11.conversation-profiles/plan.md — this design.
  • cycles/CYCLES.md — add c11 (flat bullet format); remove c10. cycles/HISTORY.md — c10 entry in the c9 format.
  • Docs touched in T3/T4: README.md "How to Interpret" (declared profile, perspective, validation gate), CLAUDE.md (Sister Projects: design as driver, link to ../design/brand/pipeline.md; brief as hand-off artifact), .claude/skills/extract/SKILL.md (two-field triage), goals.md (points at the enforced rule).

Risks

  • relayed vs first-hand is judgment; only internal is deterministic. The gate treats anything not first-hand as non-validating, so errors bias toward under-confidence.
  • Declared vs registry sourceType can disagree — fixed precedence + index warning.
  • interestLevel optional: build-index.ts:178 defaults to 0 and would drag averages; site skips undefined.
  • Every use case will read unvalidated — that is the honest state; the card explains what would validate it (nextTest).
  • validation.ts must stay in sync with the new fields (structured-output grammar ceiling in output-schema.ts:1-14 does not apply to prompt-driven extraction).

Verification

  • npx tsc --noEmit clean (root + site).
  • AMZ 2026-09-02: sourceType: internal, every observation perspective: internal, no interest/funnel, session present, internal pill in dashboard.
  • Mark Peterson 08-31: ciso-advisory, secondhand CISO claims relayed + downgraded, session record lists the visibility-before-governance decision, spec/prototype todos (Mark P / Tyler / Rob), parked ideas (gamified rollout checklist, auto-docs from recordings).
  • AMZ + Curry 08-20 re-extracted: AMZ/Tyler internal, Curry first-hand.
  • data/analysis/use-cases/*.json carry validation.status + reasons; overview validated = 0 with reasons naming the missing usage-based / second-company evidence.
  • Extract log shows cache_read_input_tokens > 0 on the second call of a run under two different profiles.
  • npm run dev: Direction section and session tab render.

Qpoint Brand Style Guide