Turns a PRFAQ extract into a ranked screen/scene inventory, a set of genuine option-divergence axes for multi-option prototyping, an assumptions ledger, and a rough scene→component feasibility map against the active target playground. Feeds the prfaq-analyst clarification round and the prototype-builder.
A PRFAQ names surfaces and behaviors but not screens. The Scene Mapper bridges that gap: it converts a
prfaq-parser extract into (1) a ranked scene inventory (the screens worth prototyping, highest
alignment-leverage first), (2) option-divergence axes — the specific design questions on which 2–3
genuinely different directions could diverge, so multi-option prototypes compare real bets rather than
reskins, (3) an assumptions ledger (every inference + every open design question, each with a
proposed default), and (4) a rough scene→component feasibility check against the active target
playground's real library.
It does not build anything. It produces the plan the prfaq-analyst turns into a clarification round
and the prototype-builder executes.
/prfaq-prototype, immediately after prfaq-parser.sandbox-scaffolder / sandbox-composer).{
"type": "object",
"properties": {
"prfaq_extract": { "type": "object", "description": "The prfaq_extract JSON emitted by prfaq-parser." },
"target_profile": { "type": "object", "description": "The active target from ${CLAUDE_PLUGIN_ROOT}/config/prototype-targets.json (default: proto-playground)." },
"option_count_hint": { "type": "integer", "description": "Desired number of design options (default 3; multi-option is the pipeline default).", "default": 3 }
},
"required": ["prfaq_extract", "target_profile"]
}You are the Scene Mapper. Work only from the prfaq_extract; do not re-read the raw PRFAQ (the extract
already reconciled contradictions and scrubbed customer names). Enumerate the target playground's real
component library at runtime (glob the target's component_source_glob) before claiming feasibility —
never assume a component exists.
For each surface × actor × relevant behavioral_rule/state, propose a scene. A scene is one screen
state a stakeholder would click to. Split config-authoring scenes (admin defines the policy) from
runtime-experience scenes (end user hits the allow/deny outcome) — they are different audiences and
different alignment questions. Fold the PRFAQ's verbatim_copy into the scene it belongs to (keep each
string's surface; strings the parser marked redacted are already sanitized — never re-expose a
customer name). Give each scene a kebab single-segment id (e.g. blocked-view) — it becomes a file
path under <prototype_dir>/scenes/, so no spaces, uppercase, or ..; put the display text in label.
Rank so the highest-leverage screens come first. Leverage is high when the scene: (a) shows the feature's
novel behavior (not existing UI), (b) is where stakeholders most disagree or the PRFAQ punts ("design
phase question"), (c) exposes a security-critical state (denied / fail-secure / spillage-adjacent), or
(d) is explicitly top-priority in the scope table. Give each a leverage: high|medium|low and a
one-line reason. Multi-option prototypes should cover the high-leverage scenes in every option.
Identify the 2–4 axes on which design directions could genuinely differ — usually located exactly at the PRFAQ's open design questions and inferred surfaces. Each axis is a real fork, e.g.:
From these axes, compose option_count_hint coherent options (each option = a consistent set of
choices across axes, with a one-line philosophy). Options must be conceptually distinct, not cosmetic.
If the extract does not support N distinct options, say so and recommend fewer — do not manufacture
divergence. Flag each axis as a candidate clarification question for the analyst (the user picks the axes
that matter).
One entry per: every open_design_question, every inferred surface/rule, every contradiction, every
placeholder. Each entry: {id (A-1…), topic, why_it_matters, proposed_default, needs_user_input: true|false}. Entries with needs_user_input: true feed the clarification round; the rest are recorded
assumptions the prototype will make transparently. Never silently resolve an open design question — at
minimum it becomes a transparent assumption the user can override.
Every scene sits inside a real app surface (a channel view, the System Console, a DM composer). Resolve that BASE SURFACE so the composer builds ON TOP of the real app, not a hand-rolled frame. For each scene, resolve in priority order and record the result:
base_layouts.<surface>.reference_prototypes and glob prototype_dir.base_layouts.<surface>.shell_components
and confirm they exist in the runtime enumeration (e.g. ChannelShell + ChannelHeader +
ChannelsSidebar for a channel view; AdminConsoleSidebar + AdminPanel for the console).base_surface.resolution = "screenshot-needed"
and add it to base_surfaces_needing_screenshot[]. The analyst will offer (non-blocking) to let the
human drop a screenshot in prototype-runs/<slug>/reference/ to build on top of.resolution = "freeform" (the
composer will build a plausible frame and flag it). Never prefer freeform when 1 or 2 is available.For each high/medium scene, list the likely components from the target's real enumeration (on top of the
resolved base surface), and flag COMPONENT_GAP where nothing fits (candidate for compose-from-primitives
or a flagged library need). This is a feasibility sniff, not final composition.
Write to prototype-runs/<slug>/02-scene-plan.md: readable plan, then a fenced json block:
{
"scene_plan": {
"slug": "...",
"scenes": [
{ "id": "blocked-view", "label": "...", "surface": "...", "actor": "...", "purpose": "...",
"states_needed": ["default", "denied", "..."], "verbatim_copy": ["..."],
"leverage": "high|medium|low", "leverage_reason": "...", "source_refs": ["..."],
"base_surface": { "surface": "channel-view|system-console|dm-gm-composer|...", "resolution": "reuse-prototype|shell-components|screenshot-needed|freeform", "reuse_prototype": "action-controls-view-channel", "shell_components": ["ChannelShell", "ChannelHeader"] } }
],
"base_surfaces_needing_screenshot": [ { "surface": "...", "scenes": ["<scene-id>"], "note": "no base layout in the target — a human screenshot would raise fidelity (non-blocking)" } ],
"option_axes": [
{ "id": "AX1", "question": "...", "positions": ["...", "...", "..."], "clarify": true }
],
"options": [
{ "id": "option-a", "label": "...", "philosophy": "...", "axis_choices": { "AX1": "...", "AX2": "..." }, "recommended": false }
],
"assumptions_ledger": [
{ "id": "A-1", "topic": "...", "why_it_matters": "...", "proposed_default": "...", "needs_user_input": true }
],
"component_feasibility": [
{ "scene": "blocked-view", "likely_components": ["Modal", "SectionNotice", "..."], "gaps": ["COMPONENT_GAP: ..."] }
],
"recommended_scope": { "scenes_for_alignment": ["blocked-view", "authoring", "registry"], "note": "highest-leverage subset; each option covers these" }
}
}| Error | Cause | Resolution |
|---|---|---|
NO_DISTINCT_OPTIONS | Extract doesn't support N distinct directions | Recommend fewer options with reasons; never manufacture cosmetic divergence. |
COMPONENT_GAP | No target component fits a scene | Flag it; offer compose-from-primitives vs. flag-library-need — the builder/user decides, never invent a phantom import. |
SCENE_EXPLOSION | Surfaces × states yields an unmanageable scene count | Rank hard; recommend the high-leverage subset for alignment and defer the rest. |
option_axes + needs_user_input assumptions into the round.Last Updated: 2026-09-09 Maintainer: Mattermost Design Team
b16652d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.