|
| 1 | +# Design: Product-Manager Persona + `personas/` Vertical |
| 2 | + |
| 3 | +Hyperstack enforces *how to work* (Iron Laws, gates) and *how it looks/builds* (designer, engineering-discipline), but nothing owns *what to build and why*. This spec adds a fourth architectural vertical - `personas/` - and its first inhabitant, a `product-manager` persona that grounds build decisions in validated customer problems, prioritizes ruthlessly, and makes opinionated, evidence-backed calls. It bites as a tiered gate before design/build. The persona's rules are not invented: they are 11 framework rules confirmed by adversarial deep research against primary sources (Cagan/SVPG, Torres/Product Talk, Intercom, Christensen, Doshi), persisted at `docs/research/2026-06-21-pm-craft-corpus.json`. |
| 4 | + |
| 5 | +## Problem statement |
| 6 | + |
| 7 | +| Symptom (user-reported) | Root cause | Evidence | |
| 8 | +|---|---|---| |
| 9 | +| "Decisions are random gibberish" | No layer owns product VALUE - what to build is assumed, not validated | Four-Risks taxonomy: PM owns value+viability; Hyperstack has neither | |
| 10 | +| "Not real PM-level calls" | No customer-behaviour grounding before work starts | Discovery is continuous + story-based; Hyperstack jumps to how-work | |
| 11 | +| "Enforcement leaks" (decision quality, not mechanics) | Output hedges / sprawls; no prioritized, defensible call | "Strategy = saying no"; weak PMs ship everything | |
| 12 | + |
| 13 | +The four product risks map onto Hyperstack's roles and expose the exact hole: |
| 14 | + |
| 15 | +``` |
| 16 | +RISK OWNER STATUS |
| 17 | +────────────────────────────────────────────────────── |
| 18 | +VALUE → Product Manager → MISSING ◄ the leak |
| 19 | +VIABILITY → Product Manager → MISSING ◄ the leak |
| 20 | +USABILITY → Designer (designer skill) → exists |
| 21 | +FEASIBILITY → Engineer (eng-discipline) → exists |
| 22 | +``` |
| 23 | + |
| 24 | +The PM persona completes the trio. It is not a bolt-on; it fills a structurally missing accountability. |
| 25 | + |
| 26 | +## Goals / Non-goals |
| 27 | + |
| 28 | +| Goals | Non-goals | |
| 29 | +|---|---| |
| 30 | +| Add `personas/` as a first-class 4th vertical, extensible to future personas | Not a parallel MCP transport - tools register through the existing `loadPlugins` path | |
| 31 | +| Ship `product-manager` persona = bound MCP plugin + skill + agent role + manifest | Not project-specific product opinions - corpus is generic, source-cited PM craft (keeps core generic) | |
| 32 | +| Tiered enforcement: hard-gate net-new, advisory tweaks, user override always | Not a replacement for designer/engineering-discipline - it precedes them | |
| 33 | +| Ground every tool in a verified research claim; stub unproven areas as NEEDS-RESEARCH | Not faking the 4 research-gap areas (MVP-cut, Kano/MoSCoW/ICE, decision-template) | |
| 34 | + |
| 35 | +## Research foundation (corpus → tools) |
| 36 | + |
| 37 | +Every PM tool is backed by a confirmed claim. Full provenance in the persisted corpus. |
| 38 | + |
| 39 | +| Rule | Tool surface | Source (confidence) | |
| 40 | +|---|---|---| |
| 41 | +| Four Product Risks (value/usability/feasibility/viability); PM owns value+viability | `get_four_risks` | Cagan/SVPG (high) | |
| 42 | +| Opportunity-vs-solution test: "more than one way to address this?" | `opportunity_vs_solution` | Torres (high) | |
| 43 | +| JTBD job = progress-in-context (functional/emotional/social); Four Forces | `get_jtbd` | Christensen (high) | |
| 44 | +| Job-statement validator (7-point falsifiable checklist) | `validate_job_statement` | gopractice/Christensen (high) | |
| 45 | +| Story-based discovery; never ask "what do you want"; weekly contact | `get_discovery_rules` | Torres (high) | |
| 46 | +| RICE = (Reach x Impact x Confidence) / Effort | `score_rice` | Intercom, RICE originator (high) | |
| 47 | +| Strategy = saying no; focus 2-3 levers; long list = non-strategy | `get_strategy_rules` | Cagan/SVPG (high) | |
| 48 | +| Anti-patterns: feature-factory, reactivity, viability-avoidance, execution-misdiagnosis (strategy/interpersonal/culture) | `get_anti_patterns` | Cagan + Doshi (high) | |
| 49 | +| Orchestrator: description → grounded product decision + verdict | `resolve_product_decision` | composition of above | |
| 50 | + |
| 51 | +Killed claim (do not encode): "context is THE test for a real job" (1-2 refuted). Correction encoded: execution problems trace to strategy OR interpersonal OR culture (three roots, not one). |
| 52 | + |
| 53 | +## Architecture: the `personas/` vertical |
| 54 | + |
| 55 | +``` |
| 56 | +hyperstack/ |
| 57 | +├── src/plugins/ Layer 1 MCP ground-truth (data) |
| 58 | +├── skills/ Layer 2 process / discipline |
| 59 | +├── agents/ Layer 3 routing roles (who executes) |
| 60 | +└── personas/ Layer 4 ◄ NEW: judgment lenses that OWN decisions |
| 61 | + ├── README.md vertical overview |
| 62 | + ├── persona-registry.ts loads persona.json manifests; exposes to compiler + router |
| 63 | + ├── persona.schema.json manifest schema |
| 64 | + └── product-manager/ |
| 65 | + ├── persona.json manifest (see below) |
| 66 | + ├── PROFILE.md identity, mission, owns value+viability, voice |
| 67 | + ├── LIFECYCLE.md engage criteria, gate steps, handback to hyper |
| 68 | + ├── CHECKS.md the falsifiable gate checklist |
| 69 | + ├── CONTEXT.md context slice it loads |
| 70 | + └── corpus/ framework snippets (distilled from research JSON) |
| 71 | +``` |
| 72 | + |
| 73 | +### Persona manifest (`persona.json`) |
| 74 | + |
| 75 | +```json |
| 76 | +{ |
| 77 | + "id": "product-manager", |
| 78 | + "name": "Product Manager", |
| 79 | + "version": "0.1.0", |
| 80 | + "owns": { |
| 81 | + "risks": ["value", "viability"], |
| 82 | + "plugin": "product-manager", |
| 83 | + "skills": ["pm-gate"], |
| 84 | + "agent": "product-manager" |
| 85 | + }, |
| 86 | + "engages_when": ["net-new feature", "new product", "build request", "scope decision"], |
| 87 | + "gate_policy": { "net_new": "hard", "tweak": "advisory", "override": "user-explicit" } |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +### Bound components |
| 92 | + |
| 93 | +| Part | Location | Contract | |
| 94 | +|---|---|---| |
| 95 | +| MCP plugin `product-manager` | `src/plugins/product-manager/` (existing plugin pattern: `index.ts`/`loader.ts`/`data.ts`/`tools/*`/`snippets/*`) | 9 tools above; stateless; logically persona-owned via manifest | |
| 96 | +| Skill `pm-gate` | `skills/pm-gate/SKILL.md` | the product-decision workflow + tiered gate; Iron Law: "NO NET-NEW BUILD WITHOUT A PASSED PRODUCT DECISION" | |
| 97 | +| Agent role `product-manager` | bound via manifest; contracts under `personas/product-manager/` | owns value+viability; engaged by hyper; hands back to hyper | |
| 98 | +| Binding | `personas/product-manager/persona.json` | ties plugin + skill + agent + corpus + voice | |
| 99 | + |
| 100 | +Pragmatic concession to the 4th-vertical choice: tool *code* lives in `src/plugins/` so it registers through the only verified MCP path (`src/index.ts` → `loadPlugins`). The persona *owns* it via manifest. Physical relocation under `personas/` is possible but requires a new loader wired into `src/index.ts`; deferred unless required. |
| 101 | + |
| 102 | +Unverified mechanism (resolve in the plan, do not assume here): how the `product-manager` agent becomes a discoverable agent-type - declared in the plugin manifest, auto-discovered from an `agents/` dir, or realized purely through the persona contracts + `pm-gate` skill - was not confirmed during codebase analysis. The plan must verify Claude Code's agent-registration path before committing to one. |
| 103 | + |
| 104 | +## Lifecycle - where the gate bites |
| 105 | + |
| 106 | +``` |
| 107 | +user request |
| 108 | + │ |
| 109 | + ▼ |
| 110 | +hyper ── classify ──► build / feature / product call? |
| 111 | + │ │ |
| 112 | + │ YES │ engage product-manager persona [GATE] |
| 113 | + │ ▼ |
| 114 | + │ ┌─────────────────────────────────────┐ |
| 115 | + │ │ 1 GROUND opportunity_vs_solution │ reject solutions-as-problems |
| 116 | + │ │ discovery evidence? │ flag "customer said they want X" |
| 117 | + │ │ 2 ASSESS get_four_risks │ esp VALUE + VIABILITY |
| 118 | + │ │ 3 PRIORITIZE score_rice / saying-no │ what is the ONE call |
| 119 | + │ │ 4 EMIT resolve_product_decision│ PASS | BLOCK | NEEDS-INPUT + rationale |
| 120 | + │ └─────────────────────────────────────┘ |
| 121 | + │ │ PASS |
| 122 | + └── NO (tweak/bug/refactor) ──► advisory brief (no block) |
| 123 | + ▼ |
| 124 | + hyper routes → designer → builder → ship-gate |
| 125 | +``` |
| 126 | + |
| 127 | +### Tiered enforcement policy |
| 128 | + |
| 129 | +| Request class | Gate | Behaviour | |
| 130 | +|---|---|---| |
| 131 | +| Net-new feature / product / scope decision | HARD | must emit PASS before designer/build; BLOCK halts with reason | |
| 132 | +| Tweak / bugfix / refactor / non-product | ADVISORY | brief emitted, no block | |
| 133 | +| User explicit "skip PM" | OVERRIDE | always honoured, logged in the decision trail | |
| 134 | + |
| 135 | +## Build integration |
| 136 | + |
| 137 | +| File | Change | Risk | |
| 138 | +|---|---|---| |
| 139 | +| `skills/hyperstack/SKILL.md` | add `## Personas` section (bootstrap source of truth) | LOW | |
| 140 | +| `src/internal/context-compiler.ts` | extract + emit Personas layer; extend `REQUIRED_BOOTSTRAP_MARKERS` | MED - marker-validated, covered by `context-compiler-behaviour.test.ts` | |
| 141 | +| `generated/runtime-context/hyperstack.bootstrap.md` | regenerated via `bun run compile:context` | auto | |
| 142 | +| `harness/router.md`, `harness/transitions.md` | add persona-gate step `hyper → persona-gate → specialist`; allowed/disallowed transitions | LOW (prose) | |
| 143 | +| `personas/persona-registry.ts`, `persona.schema.json` | new, small | LOW | |
| 144 | +| `personas/product-manager/**` | manifest + contracts + corpus | LOW | |
| 145 | +| `src/plugins/product-manager/**` | new plugin, mirrors `designer` exactly | LOW | |
| 146 | +| `src/index.ts` | import + register `productManagerPlugin` | LOW | |
| 147 | +| `tests/` | persona-registry, pm-gate verdict, bootstrap-marker tests | LOW (pattern exists) | |
| 148 | + |
| 149 | +## Data flow |
| 150 | + |
| 151 | +``` |
| 152 | +research JSON ──(distill, build-time/manual)──► src/plugins/product-manager/snippets/*.txt |
| 153 | + │ createSnippetLoader("product-manager") |
| 154 | +tool call ──► handler ──► data.ts (typed) + snippet(txt) ──► markdown decision/checklist |
| 155 | +pm-gate skill ──► calls product_manager_* tools ──► applies gate_policy ──► PASS|BLOCK|NEEDS-INPUT |
| 156 | +persona-registry ──► reads persona.json ──► feeds compiler (bootstrap) + router (engage_when) |
| 157 | +``` |
| 158 | + |
| 159 | +## Error handling / degraded mode |
| 160 | + |
| 161 | +| Failure | Behaviour | |
| 162 | +|---|---| |
| 163 | +| MCP unavailable | pm-gate states "MCP unavailable", falls back to corpus text, flags decision as ungrounded (consistent with existing degraded-mode rule) | |
| 164 | +| Persona manifest malformed | persona-registry skips it with a surfaced warning (not silent), like plugin-registry resilience | |
| 165 | +| Tool references missing snippet | build-time corpus-integrity check (added) fails loud, not at runtime | |
| 166 | +| Gate verdict ambiguous | default to NEEDS-INPUT (ask user), never silent PASS | |
| 167 | + |
| 168 | +## Testing |
| 169 | + |
| 170 | +Mirror existing `*-behaviour.test.ts`: |
| 171 | +- persona-registry loads `product-manager` manifest; malformed manifest is skipped with warning. |
| 172 | +- bootstrap compiles with Personas markers present (extends marker test). |
| 173 | +- `pm-gate` returns BLOCK when four-risks viability is unaddressed; PASS when all four covered + prioritized call present; NEEDS-INPUT on ambiguity. |
| 174 | +- `opportunity_vs_solution` returns is_solution for a single-path statement. |
| 175 | +- `validate_job_statement` fails a statement missing context. |
| 176 | +- override path bypasses gate and logs. |
| 177 | + |
| 178 | +## Phasing |
| 179 | + |
| 180 | +| Phase | Scope | |
| 181 | +|---|---| |
| 182 | +| **1 (this plan)** | `personas/` scaffold + registry + schema; `product-manager` plugin (9 verified tools); `pm-gate` skill (tiered); bootstrap + router wiring; tests. Gap areas ship as explicit NEEDS-RESEARCH stubs. | |
| 183 | +| **2 (later)** | targeted second research pass for the 4 gaps (MVP-cut mechanics, Kano/MoSCoW/ICE, product-sense practices, decision-writeup template); add decision-log template tool. | |
| 184 | + |
| 185 | +## Open items (research-flagged gaps, not faked) |
| 186 | + |
| 187 | +1. MVP / minimum-scope cut rule - no codifiable line yet. |
| 188 | +2. Kano / MoSCoW / ICE / opportunity-sizing mechanics - only RICE survived verification. |
| 189 | +3. Repeatable product-sense-building practices. |
| 190 | +4. Defensible decision-writeup template (pre-mortem / decision-log format). |
| 191 | + |
| 192 | +## Future personas (why the vertical, not a one-off) |
| 193 | + |
| 194 | +The vertical is justified only if more personas follow. Candidates that fit the same "judgment lens that owns a risk" shape: a security persona (owns risk/threat), a data/analytics persona (owns measurement), an SRE persona (owns reliability). Each = manifest + bound plugin/skill. The registry, schema, and bootstrap layer built here are shared infrastructure. |
| 195 | + |
| 196 | +## Validation pass (post-implementation correction) |
| 197 | + |
| 198 | +A secondary validation (1 empirical accuracy battery + 2 independent adversarial red-team agents) found that 3 of the 9 tools as first built were theater, and corrected them. This supersedes the earlier descriptions of those tools above: |
| 199 | + |
| 200 | +| Tool | As-built flaw | Corrected to | |
| 201 | +|---|---|---| |
| 202 | +| `resolve_product_decision` | hardcoded `NEEDS-INPUT`, never PASS/BLOCK - the "gate engine" could not gate | takes value+viability assessments as input; deterministic verdict PASS (both addressed) / BLOCK (net-new, missing) / ADVISORY (tweak). PASS earned, not automatic. | |
| 203 | +| `opportunity_vs_solution` | ~80% regex classifier emitting an exclusive verdict (the LLM does this better) | returns the Torres rubric + reframe examples for the agent to apply (ground-truth, no verdict) | |
| 204 | +| `validate_job_statement` | 3-regex pseudo-validator | returns the 7-point JTBD criteria for the agent to judge against | |
| 205 | + |
| 206 | +Also corrected: `persona.json` dropped the dangling `agent: product-manager` claim (no such agent exists; the persona is `engaged_by` hyper and realized via the `pm-gate` skill), and LIFECYCLE dropped an unimplemented "decision trail" claim. The 6 framework tools (`get_*` + `score_rice`) were sound and unchanged. Verdict labels are now PASS/BLOCK/ADVISORY (not NEEDS-INPUT). A runtime write-blocking hook was deliberately declined: all Hyperstack skills are prose-enforced via the bootstrap, and the diagnosed problem was decision quality, not enforcement mechanics. |
0 commit comments